Documentation
¶
Overview ¶
Package tesla implements a TESLA-inspired hash-chain key schedule for delayed disclosure of packet authentication keys.
Key Schedule Overview ¶
Keys form a backward hash chain: the executor generates a random secret seed k_L (the tail) and hashes it L times to obtain the public anchor k_0:
k_0 = H^L(k_L) (public anchor, published at setup)
k_i = H(k_{i+1}) (each key is the hash of the next one)
The executor uses key k_i during epoch i (1 ≤ i < L) and discloses it after a configurable disclosure delay d has elapsed. Because k_0 is public, epoch 0 has no signing key: nothing is tagged before epoch 1 starts. Because k_L is never disclosed, nothing is tagged from epoch L on either. A verifier who has buffered packets from epoch i can verify them once k_i is published by checking:
H^i(k_i) == k_0
Given a disclosed key k_τ, the key for an earlier epoch t (t ≤ τ) is reconstructed by hashing forward τ-t times:
k_t = H^(τ-t)(k_τ)
Per-Measurement Derivation ¶
Because a single executor may service multiple measurements concurrently, an additional per-measurement key ak is derived from the current chain key:
ak = HKDF-SHA256(secret=k_i, info=measurement_id, length=32)
Authentication Tag ¶
The authentication tag written into the IPv4 IPID field is:
tag = HMAC-SHA256(ak, packet_payload)[0:2] (16-bit truncation)
Index ¶
- Constants
- func ChainSeed(seed []byte, generation int64) ([]byte, error)
- func ComputeBPFTag(ak, payload []byte) (uint16, error)
- func ComputeTag(ak, payload []byte) (uint16, error)
- func DeriveAK(k []byte, measurementID []byte) ([]byte, error)
- func DeriveFromDisclosed(disclosedKey []byte, disclosedEpoch, targetEpoch int64) ([]byte, error)
- func VerifyBPFTag(disclosedKey []byte, epoch int64, measurementID, packet []byte, tag uint16) (bool, error)
- func VerifyChain(anchor, key []byte, t int64) bool
- func VerifyTag(disclosedKey []byte, epoch int64, measurementID, packet []byte, tag uint16) (bool, error)
- type Config
- type KeySchedule
- func (ks *KeySchedule) Anchor() []byte
- func (ks *KeySchedule) ChainLength() int64
- func (ks *KeySchedule) ComputeBPFTagForPacket(t time.Time, measurementID, payload []byte) (uint16, error)
- func (ks *KeySchedule) ComputeTagForPacket(t time.Time, measurementID, payload []byte) (uint16, error)
- func (ks *KeySchedule) Config() Config
- func (ks *KeySchedule) CurrentKey(t time.Time) []byte
- func (ks *KeySchedule) DisclosedKey(t time.Time) (index int64, key []byte, ok bool)
- func (ks *KeySchedule) Exhausted(t time.Time) bool
- func (ks *KeySchedule) Expiry() time.Time
- func (ks *KeySchedule) KeyAtEpoch(epoch int64) ([]byte, error)
Constants ¶
const DefaultChainHorizon = 7 * 24 * time.Hour
DefaultChainHorizon is the uptime a default-sized chain covers. The default ChainLength is DefaultChainHorizon/Delay, so a longer epoch gives a shorter chain rather than a shorter usable lifetime.
Variables ¶
This section is empty.
Functions ¶
func ChainSeed ¶
ChainSeed derives the tail k_L of one chain from a configured seed:
k_L = HKDF-SHA256(secret=seed, info="debuglet tesla chain <generation>", length=32)
Each generation of the same seed yields a different chain, so a restart with an unchanged seed does not reuse keys an earlier chain disclosed.
func ComputeBPFTag ¶
ComputeBPFTag computes the BPF SipHash-2-4 tag for a packet payload. It does not perform packet canonicalization (which should be done by the caller or on raw packet payloads).
func ComputeTag ¶
ComputeTag computes the 16-bit authentication tag for a packet payload:
tag = HMAC-SHA256(ak, payload)[0:2]
The returned uint16 is in host byte order; the caller is responsible for writing it into the IPID field in network (big-endian) byte order.
func DeriveAK ¶
DeriveAK computes the per-measurement authentication key:
ak = HKDF-SHA256(secret=k, info=measurementID, length=32)
measurementID should be the raw bytes of the UUID (or any stable byte representation of the measurement identifier).
func DeriveFromDisclosed ¶
DeriveFromDisclosed reconstructs the key at targetEpoch given a disclosed key at disclosedEpoch. Because the chain runs backward (k_i = H(k_{i+1})), a verifier can only derive keys at epochs ≤ disclosedEpoch by hashing the disclosed key forward (disclosedEpoch → targetEpoch, t ≤ disclosedEpoch):
k_t = H^(disclosedEpoch - t)(k_disclosedEpoch)
To reconstruct a later (newer) key you would need an even newer disclosed key, since the chain cannot be inverted.
func VerifyBPFTag ¶
func VerifyBPFTag(disclosedKey []byte, epoch int64, measurementID, packet []byte, tag uint16) (bool, error)
VerifyBPFTag checks whether tag matches the expected SipHash for the packet
func VerifyChain ¶
VerifyChain checks that H^t(k_t) == k_0 (the public anchor). A verifier calls this after reconstructing k_t to confirm its authenticity.
func VerifyTag ¶
func VerifyTag(disclosedKey []byte, epoch int64, measurementID, packet []byte, tag uint16) (bool, error)
VerifyTag checks whether tag matches the expected HMAC for the packet at a given epoch, given the disclosed key for that epoch and the measurement ID.
If packet begins with a valid IPv4 header (version == 4, length ≥ 20), both the IPID field (bytes 4–5) and the IPv4 checksum field (bytes 10–11) are zeroed before hashing, exactly as TagPacket does. The caller should pass the full received packet bytes (with the IPID field containing the observed tag and the checksum field as received on the wire).
Types ¶
type Config ¶
type Config struct {
// Seed is the secret tail k_L of the hash chain. If nil, a random
// 32-byte seed is generated on the first call to NewKeySchedule.
// The seed is NEVER disclosed; it is only used internally to derive
// keys by hashing backwards toward k_0.
Seed []byte
// ChainLength L is the total number of epochs supported by this
// schedule. The keys k_0 … k_L are generated; k_0 is the public
// anchor and k_L is derived from the seed.
//
// If zero, it is derived from Delay so that the chain covers
// DefaultChainHorizon of uptime. Sizing this from a wall-clock horizon
// matters: k_L is never disclosed, so the last signing epoch is L-1 and
// from the start of epoch L (Expiry) the schedule has no signing key and
// nothing is tagged.
ChainLength int64
// Delay is the interval duration I (one epoch). A key disclosed after
// the disclosure delay d (expressed as a number of epochs) has elapsed.
// Defaults to 10 seconds.
Delay time.Duration
// Epoch is the reference wall-clock time that anchors epoch 0.
// Defaults to the time NewKeySchedule is called.
Epoch time.Time
}
Config carries the tuneable parameters of the TESLA key schedule.
type KeySchedule ¶
type KeySchedule struct {
// contains filtered or unexported fields
}
KeySchedule is a thread-safe TESLA hash-chain key schedule.
Keys are indexed by epoch number t where t ∈ [0, L]:
epoch t covers the time interval [Epoch + t*Delay, Epoch + (t+1)*Delay).
The chain direction is backward: k_0 is the public anchor and k_L is the private seed tail. Key k_t is used during epoch t and disclosed after the disclosure delay d has elapsed (i.e., once epoch t+d has started).
k_0 is public from setup, so it never signs: epoch 0, and any instant before Epoch, has no usable signing key. The first usable key is k_1 at Epoch+Delay and the last is k_{L-1}; from Expiry, the start of epoch L, no key signs.
func NewKeySchedule ¶
func NewKeySchedule(cfg Config) (*KeySchedule, error)
NewKeySchedule creates a KeySchedule from cfg.
The chain direction is backward (k_i = H(k_{i+1})). The private seed k_L is stored internally and the public anchor k_0 = H^L(seed) is computed once.
If cfg.Seed is empty a cryptographically random 32-byte seed is generated. A seed that is not exactly 32 bytes long is folded through SHA-256 so that k_L is always one key wide. If cfg.Delay is zero it defaults to 10 seconds. If cfg.ChainLength is zero it is derived from Delay (see DefaultChainHorizon).
func (*KeySchedule) Anchor ¶
func (ks *KeySchedule) Anchor() []byte
Anchor returns k_0, the public anchor that should be published at setup. Verifiers use it to check consistency: H^t(k_t) == k_0.
func (*KeySchedule) ChainLength ¶
func (ks *KeySchedule) ChainLength() int64
ChainLength returns L, the highest epoch this schedule can serve.
func (*KeySchedule) ComputeBPFTagForPacket ¶
func (ks *KeySchedule) ComputeBPFTagForPacket(t time.Time, measurementID, payload []byte) (uint16, error)
ComputeBPFTagForPacket derives ak from the chain key at time t and then computes the BPF SipHash-2-4 tag over payload. It fails while no chain key is usable.
func (*KeySchedule) ComputeTagForPacket ¶
func (ks *KeySchedule) ComputeTagForPacket(t time.Time, measurementID, payload []byte) (uint16, error)
ComputeTagForPacket is a convenience wrapper that derives ak from the chain key at time t and then computes the tag over payload. It fails while no chain key is usable.
func (*KeySchedule) Config ¶
func (ks *KeySchedule) Config() Config
Config returns a copy of the schedule's configuration.
func (*KeySchedule) CurrentKey ¶
func (ks *KeySchedule) CurrentKey(t time.Time) []byte
CurrentKey returns the chain key k_t for the epoch that contains time t, or nil while no key is usable: epochOf maps epoch 0 and any instant before Epoch to 0, whose key is the public anchor (see KeySchedule), and every instant from the start of epoch L to L, whose key k_L is never disclosed. Every signing path reads the key here, so this is the one place the rule is decided.
func (*KeySchedule) DisclosedKey ¶
DisclosedKey returns the epoch index and key that should be disclosed at time t. A key for epoch τ is disclosed once d disclosure-delay epochs have elapsed after τ. With d=1, the key for epoch (current−1) is disclosed when epoch current starts.
If t is still within epoch 0 (no key is disclosable yet), ok is false. From epoch L on the disclosed key stays k_{L-1}, the last signing key; k_L is never disclosed.
func (*KeySchedule) Exhausted ¶
func (ks *KeySchedule) Exhausted(t time.Time) bool
Exhausted reports whether time t falls at or past the end of the chain, the start of epoch L. k_L is never disclosed, so an exhausted schedule has no signing key and CurrentKey returns nil from then on.
func (*KeySchedule) Expiry ¶
func (ks *KeySchedule) Expiry() time.Time
Expiry returns the wall-clock time at which the chain runs out.
func (*KeySchedule) KeyAtEpoch ¶
func (ks *KeySchedule) KeyAtEpoch(epoch int64) ([]byte, error)
KeyAtEpoch returns the chain key for the given epoch index.