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

84 lines
3.5 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)
}
// 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.