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

95 lines
4.2 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. Callers always verify the
// release, with Verify (spec §63 step 10).
//
// A source that fetches releases over a network (a relay, the Release API or
// a cache) must also verify each response with Verify and discard the one
// that fails, and report datekeys.ErrReleaseUnavailable when no response
// passes (spec §63 step 9), as provider/drand.Client does: an invalid release
// from the network is then reported at step 9, not with the codes of step 10.
// A source that hands over a release the caller supplies directly need not
// verify it; its release gets the codes of step 10.
//
// Errors should wrap datekeys.ErrReleaseUnavailable, and no other normative
// error, when the release cannot be obtained, for example because the round
// is not published yet. capsule.Open reports any error of a source at step 9
// with ErrReleaseUnavailable as its only code, the one spec §63 gives that
// step, and keeps only the text of an error that carries another code.
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),
// in the order of spec §63 step 10: the expected round (ErrRoundMismatch),
// then the signature (ErrReleaseInvalid), which must be the canonical
// encoding of a point of the signature group of the scheme other than the
// point at infinity (spec §12.2), with the length of that group, and a valid
// BLS signature of the round under the pinned public key. The chain hash is
// covered because the pinned public key is bound to it by profile.Validate.
//
// The BLS verification of drand decodes the signature with the canonical
// decoder of kilic/bls12-381, which rejects every other encoding, and the
// point at infinity never verifies because the pinned public key is not the
// point at infinity. The error of drand is not copied.
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 is not the canonical encoding of a point of the key group: %w", p.ID, datekeys.ErrUnknownProfile)
}
beacon := &common.Beacon{Round: r.Round, Signature: r.Signature}
if err := scheme.VerifyBeacon(beacon, key); err != nil {
return fmt.Errorf("provider: the signature is not a canonical point encoding, or does not verify as the BLS signature of round %d under %s: %w", r.Round, p.ID, datekeys.ErrReleaseInvalid)
}
return nil
}

Powered by TurnKey Linux.