Files

73 lines
2.3 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
// Package keystore provides channel key lookup for the ingest pipeline.
// Keys are loaded from config at startup and never written at runtime.
// Future: add a DB-backed fallback that checks the channel_keys table.
package keystore
import (
"bytes"
"crypto/sha256"
"encoding/hex"
)
// Entry holds a resolved channel key along with its metadata.
type Entry struct {
Key []byte // raw key bytes (16 bytes for hashtag channels)
Fingerprint []byte // first 8 bytes of SHA256(key)
Hashtag string // set if derived from a hashtag, empty otherwise
Name string // display name, may be empty
}
// MapKeyStore maps channel hash hex → list of known key entries.
// Multiple entries per hash are supported to handle 1-byte hash collisions.
type MapKeyStore struct {
entries map[string][]Entry // channel hash hex → entries
}
// NewMapKeyStore creates a keystore from a pre-built map of entries.
// Use BuildFromConfig to construct entries from a config.ChannelKeysConfig.
func NewMapKeyStore(entries map[string][]Entry) *MapKeyStore {
store := &MapKeyStore{entries: make(map[string][]Entry)}
for hash, list := range entries {
store.entries[hash] = append(store.entries[hash], list...)
}
return store
}
// GetKey returns all known key entries for the given channel hash byte.
// Returns nil if no keys are known for this hash.
func (s *MapKeyStore) GetKey(channelHash []byte) []Entry {
return s.entries[hex.EncodeToString(channelHash)]
}
// DeriveHashtagKey derives the channel secret and hash for a hashtag name.
//
// secret = SHA256("#" + tag)[:16]
// channel_hash = SHA256(secret)[0]
// fingerprint = SHA256(secret)[:8]
func DeriveHashtagKey(tag string) (secret []byte, channelHash byte, fingerprint []byte) {
input := sha256.Sum256([]byte("#" + tag))
secret = input[:16]
secretHash := sha256.Sum256(secret)
channelHash = secretHash[0]
fingerprint = secretHash[:8]
return
}
// Fingerprint returns the first 8 bytes of SHA256(key).
func Fingerprint(key []byte) []byte {
h := sha256.Sum256(key)
return h[:8]
}
func EntryExists(entries []Entry, e Entry) bool {
for _, existing := range entries {
if bytes.Equal(existing.Key, e.Key) {
return true
}
}
return false
}