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/capsule/inhand_test.go

121 lines
4.6 KiB

Release object, release in hand and step 9.c option B (spec v0.15 draft) The release of a round becomes a file, .dkr: a release object in deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}, which provider.EncodeRelease writes and DecodeRelease reads with its layers (size, type and version, schema). provider.ParseRelease also reads drand's JSON as the input of the caller. Verify checks the chain hash a release names before its round and its signature, with ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the informative format of the draft. capsule.OpenOptions.Release takes a release in hand, a provider.Supplier, exclusive with Source: Open does not compare it with the clock (step 9.c, option B) and reports a clock behind it in Opened.ClockBehind; a network source is still never asked before the round time. The CLI gains decrypt -release FILE (.dkr, drand's JSON or a local archive), decrypt -save-release FILE.dkr and the command release, which fetches, verifies and saves the .dkr without opening the capsule. Test data: vectors/release.json, releases/<round>.dkr for rounds 1000, 1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In mutations.json every case says its source, "supplied" or "network"; the case "round not reached yet", a release in hand, now opens, and four cases are added: the same with a network source, a release of another round from a network source, and two release objects of another chain. SpecVersion stays 0.14 until the author approves the draft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 hours ago
package capsule_test
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"time"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/profile"
"g.activething.com/go/DateKeys/provider"
)
// supplierFunc adapts a function to provider.Supplier.
type supplierFunc func(*profile.Profile, provider.Condition) ([]byte, error)
func (f supplierFunc) Supply(p *profile.Profile, c provider.Condition) ([]byte, error) {
return f(p, c)
}
func readRelease(t *testing.T, round uint64) []byte {
t.Helper()
b, err := os.ReadFile(filepath.Join("../testdata/releases", testkit.ReleaseFileName(round)))
if err != nil {
t.Fatal(err)
}
return b
}
// Spec v0.15, §63 step 9.c: a release in hand is not compared with the
// clock. The capsule opens with a clock before the round time, Opened says
// the clock is behind, and Opened.Release encodes to the official release
// object of the round.
Release object, release in hand and step 9.c option B (spec v0.15 draft) The release of a round becomes a file, .dkr: a release object in deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}, which provider.EncodeRelease writes and DecodeRelease reads with its layers (size, type and version, schema). provider.ParseRelease also reads drand's JSON as the input of the caller. Verify checks the chain hash a release names before its round and its signature, with ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the informative format of the draft. capsule.OpenOptions.Release takes a release in hand, a provider.Supplier, exclusive with Source: Open does not compare it with the clock (step 9.c, option B) and reports a clock behind it in Opened.ClockBehind; a network source is still never asked before the round time. The CLI gains decrypt -release FILE (.dkr, drand's JSON or a local archive), decrypt -save-release FILE.dkr and the command release, which fetches, verifies and saves the .dkr without opening the capsule. Test data: vectors/release.json, releases/<round>.dkr for rounds 1000, 1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In mutations.json every case says its source, "supplied" or "network"; the case "round not reached yet", a release in hand, now opens, and four cases are added: the same with a network source, a release of another round from a network source, and two release objects of another chain. SpecVersion stays 0.14 until the author approves the draft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 hours ago
func TestReleaseInHand(t *testing.T) {
f := loadFixture(t, "format3_time_and_key_portable")
obj := readRelease(t, 1000)
Release object, release in hand and step 9.c option B (spec v0.15 draft) The release of a round becomes a file, .dkr: a release object in deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}, which provider.EncodeRelease writes and DecodeRelease reads with its layers (size, type and version, schema). provider.ParseRelease also reads drand's JSON as the input of the caller. Verify checks the chain hash a release names before its round and its signature, with ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the informative format of the draft. capsule.OpenOptions.Release takes a release in hand, a provider.Supplier, exclusive with Source: Open does not compare it with the clock (step 9.c, option B) and reports a clock behind it in Opened.ClockBehind; a network source is still never asked before the round time. The CLI gains decrypt -release FILE (.dkr, drand's JSON or a local archive), decrypt -save-release FILE.dkr and the command release, which fetches, verifies and saves the .dkr without opening the capsule. Test data: vectors/release.json, releases/<round>.dkr for rounds 1000, 1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In mutations.json every case says its source, "supplied" or "network"; the case "round not reached yet", a release in hand, now opens, and four cases are added: the same with a network source, a release of another round from a network source, and two release objects of another chain. SpecVersion stays 0.14 until the author approves the draft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 hours ago
for _, tc := range []struct {
name string
now time.Time
behind bool
}{
{"after the round time", f.unlock(t), false},
{"a clock one nanosecond behind", f.unlock(t).Add(-1), true},
{"a clock years behind", testkit.Genesis(), true},
} {
o := f.openOptions(t)
o.Source, o.Release, o.Now = nil, provider.Encoded(obj), testkit.Fixed(tc.now)
Release object, release in hand and step 9.c option B (spec v0.15 draft) The release of a round becomes a file, .dkr: a release object in deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}, which provider.EncodeRelease writes and DecodeRelease reads with its layers (size, type and version, schema). provider.ParseRelease also reads drand's JSON as the input of the caller. Verify checks the chain hash a release names before its round and its signature, with ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the informative format of the draft. capsule.OpenOptions.Release takes a release in hand, a provider.Supplier, exclusive with Source: Open does not compare it with the clock (step 9.c, option B) and reports a clock behind it in Opened.ClockBehind; a network source is still never asked before the round time. The CLI gains decrypt -release FILE (.dkr, drand's JSON or a local archive), decrypt -save-release FILE.dkr and the command release, which fetches, verifies and saves the .dkr without opening the capsule. Test data: vectors/release.json, releases/<round>.dkr for rounds 1000, 1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In mutations.json every case says its source, "supplied" or "network"; the case "round not reached yet", a release in hand, now opens, and four cases are added: the same with a network source, a release of another round from a network source, and two release objects of another chain. SpecVersion stays 0.14 until the author approves the draft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 hours ago
out, err := capsule.Open(context.Background(), nil, bytes.NewReader(f.dkc), o)
if err != nil {
t.Fatalf("%s: %v", tc.name, err)
}
if out.ClockBehind != tc.behind {
t.Fatalf("%s: ClockBehind %v", tc.name, out.ClockBehind)
}
saved, err := provider.EncodeRelease(out.Release)
if err != nil || !bytes.Equal(saved, obj) {
t.Fatalf("%s: the release encodes to %x, %v", tc.name, saved, err)
Release object, release in hand and step 9.c option B (spec v0.15 draft) The release of a round becomes a file, .dkr: a release object in deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}, which provider.EncodeRelease writes and DecodeRelease reads with its layers (size, type and version, schema). provider.ParseRelease also reads drand's JSON as the input of the caller. Verify checks the chain hash a release names before its round and its signature, with ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the informative format of the draft. capsule.OpenOptions.Release takes a release in hand, a provider.Supplier, exclusive with Source: Open does not compare it with the clock (step 9.c, option B) and reports a clock behind it in Opened.ClockBehind; a network source is still never asked before the round time. The CLI gains decrypt -release FILE (.dkr, drand's JSON or a local archive), decrypt -save-release FILE.dkr and the command release, which fetches, verifies and saves the .dkr without opening the capsule. Test data: vectors/release.json, releases/<round>.dkr for rounds 1000, 1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In mutations.json every case says its source, "supplied" or "network"; the case "round not reached yet", a release in hand, now opens, and four cases are added: the same with a network source, a release of another round from a network source, and two release objects of another chain. SpecVersion stays 0.14 until the author approves the draft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 hours ago
}
}
}
// A release in hand keeps the order of step 9: the credentials first (9.a,
// 9.b), with no request; then the release, whose failure to be supplied is
// ErrReleaseUnavailable alone at step 9; then step 10 with its codes, the
// layers of the object first.
func TestReleaseInHandErrors(t *testing.T) {
f := loadFixture(t, "format3_time_and_key_portable")
other := profile.Quicknet().ChainHash
other[5] ^= 1
otherChain, err := provider.EncodeRelease(provider.Release{Round: 1001, Signature: testkit.Release(1001).Signature, ChainHash: other[:]})
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
name string
supply func() ([]byte, error)
noAccess bool
want *datekeys.Error
step int
calls int
}{
{"no credential, before any supply", func() ([]byte, error) { return readRelease(t, 1000), nil }, true, datekeys.ErrAccessRequired, 9, 0},
{"nothing supplied", func() ([]byte, error) { return nil, fmt.Errorf("none: %w", datekeys.ErrReleaseUnavailable) }, false, datekeys.ErrReleaseUnavailable, 9, 1},
{"a supplier failing with another code", func() ([]byte, error) { return nil, fmt.Errorf("odd: %w", datekeys.ErrIntegrity) }, false, datekeys.ErrReleaseUnavailable, 9, 1},
{"an empty object", func() ([]byte, error) { return nil, nil }, false, datekeys.ErrNonCanonicalCBOR, 10, 1},
{"another chain and another round", func() ([]byte, error) { return otherChain, nil }, false, datekeys.ErrProfileMismatch, 10, 1},
{"another round", func() ([]byte, error) { return readRelease(t, 1001), nil }, false, datekeys.ErrRoundMismatch, 10, 1},
{"drand's JSON of another round", func() ([]byte, error) {
return []byte(`{"round":1001,"signature":"00"}`), nil
}, false, datekeys.ErrRoundMismatch, 10, 1},
} {
calls := 0
o := f.openOptions(t)
o.Source, o.Now = nil, testkit.Fixed(testkit.Genesis())
o.Release = supplierFunc(func(*profile.Profile, provider.Condition) ([]byte, error) { calls++; return tc.supply() })
if tc.noAccess {
o.AccessKey = nil
}
step, err := openAt(t, f.dkc, o)
if !onlyCode(err, tc.want) || step != tc.step || calls != tc.calls {
t.Errorf("%s: got %v at step %d after %d supplies, want %v at step %d after %d", tc.name, err, step, calls, tc.want, tc.step, tc.calls)
}
}
o := f.openOptions(t)
o.Release = provider.Encoded(readRelease(t, 1000))
if _, err := capsule.Open(context.Background(), nil, bytes.NewReader(f.dkc), o); err == nil || datekeys.Code(err) != "" {
t.Fatalf("Source and Release together: %v", err)
}
o.Source, o.Release = nil, nil
if _, err := capsule.Open(context.Background(), nil, bytes.NewReader(f.dkc), o); err == nil || errors.Is(err, datekeys.ErrReleaseUnavailable) {
t.Fatalf("no source: %v", err)
}
}

Powered by TurnKey Linux.