You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/provider/provider.go

77 lines
2.9 KiB

// Package provider defines conditions, releases and release sources (spec §9,
// §45-§52) and the local verification every release must pass.
//
// A release is never trusted because of where it came from: the DateKeys API,
// a cache or a relay are untrusted transports (spec §3, §48, §52). A remote
// "verified: true" has no security value (spec §51).
package provider
import (
"context"
"fmt"
"github.com/drand/drand/v2/common"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/profile"
)
// Condition is the time condition of a Quicknet-style profile: a round.
type Condition struct {
Round uint64
}
// Release is the material that satisfies a condition. For drand it is the
// BLS signature of the round.
type Release struct {
Round uint64
Signature []byte
}
// ReleaseSource fetches the release of a condition. Implementations need not
// verify it; callers always do, with Verify.
//
// Errors should wrap datekeys.ErrReleaseUnavailable when the release cannot be
// obtained, for example because the round is not published yet.
type ReleaseSource interface {
Fetch(ctx context.Context, p *profile.Profile, c Condition) (Release, error)
}
// ReleaseSourceFunc adapts a function to ReleaseSource.
type ReleaseSourceFunc func(ctx context.Context, p *profile.Profile, c Condition) (Release, error)
// Fetch calls f.
func (f ReleaseSourceFunc) Fetch(ctx context.Context, p *profile.Profile, c Condition) (Release, error) {
return f(ctx, p, c)
}
Spec v0.8.2 refinements: error precedence, trust model, strict order Approved refinements, each recorded with its reproducible case in the §76 v0.8.2 subsection: - §69.1: layered error model with normative precedence (frame, type tag and version, CBOR profile and CDDL, then fields with their own code in ascending key order; across steps the §63 order decides), with a scope paragraph for the optional steps 5, 6 and 8. - §55.1: normative trust table per section (who can write it, from which step it is bound, what it never proves); §72: security-relevant claims go in CONTROL_CBOR or under a signature, .dkk data is advisory. - §31/§54: extension arrays in strictly ascending unsigned byte order of extension_id (one rule for order and uniqueness). - Gaps a second implementation needed: §28.1 malformed age headers, §15/§19 latest unlock time and dk1_ reading rules, §22/§23/§57 length lower bounds, §63 step 8 tlock argument comparison and step 9 order, §12.1 profile validation with the drand chain-hash formula, §74 table of implementation limits. Reference alignment: .dkk errors only at step 9.a (new OpenOptions.AccessKeyFile, used by the CLI), CR/LF in dk1_ is ERR_DATEKEY_INVALID, BODY_LEN 0 is ERR_INTEGRITY, nil identities are not credentials, and AccessIdentity tries every identity on every stanza so its verdict does not depend on their order. dk1.json gains three vectors; every other testdata file is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 weeks ago
// Verify checks a release locally against the pinned profile (spec §17, §51),
// in the order of spec §63 step 10: the expected round (ErrRoundMismatch),
// then the signature length of the scheme and a valid BLS signature under the
// pinned public key (ErrReleaseInvalid). The chain hash is covered because
// the pinned public key is bound to it by profile.Validate.
func Verify(p *profile.Profile, c Condition, r Release) error {
if c.Round == 0 || c.Round > p.MaxRound() {
return fmt.Errorf("provider: round %d outside the range of %s: %w", c.Round, p.ID, datekeys.ErrDateKeyInvalid)
}
if r.Round != c.Round {
return fmt.Errorf("provider: release for round %d, expected %d: %w", r.Round, c.Round, datekeys.ErrRoundMismatch)
}
scheme, err := p.DrandScheme()
if err != nil {
return err
}
if want := scheme.SigGroup.PointLen(); len(r.Signature) != want {
return fmt.Errorf("provider: signature is %d bytes, %s uses %d: %w", len(r.Signature), scheme.Name, want, datekeys.ErrReleaseInvalid)
}
key := scheme.KeyGroup.Point()
if err := key.UnmarshalBinary(p.PublicKey); err != nil {
return fmt.Errorf("provider: pinned public key of %s: %v: %w", p.ID, err, datekeys.ErrUnknownProfile)
}
beacon := &common.Beacon{Round: r.Round, Signature: r.Signature}
if err := scheme.VerifyBeacon(beacon, key); err != nil {
return fmt.Errorf("provider: BLS signature of round %d does not verify under %s: %w", r.Round, p.ID, datekeys.ErrReleaseInvalid)
}
return nil
}

Powered by TurnKey Linux.