// 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) } // Verify checks a release locally against the pinned profile (spec §17, §51): // the expected round, the signature length of the scheme and a valid BLS // signature under the pinned public key. 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 }