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/cmd/datekeys/release_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 main
import (
"bytes"
"encoding/hex"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"time"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/internal/testkit"
)
const releases = "../../testdata/releases"
// unlock1000 is the round time of round 1000, that of time_only.dkc.
var unlock1000 = time.Date(2023, 8, 23, 15, 59, 24, 0, time.UTC)
// releaseFile is the release object of round in testdata/releases.
func releaseFile(round uint64) string { return filepath.Join(releases, testkit.ReleaseFileName(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
// Spec v0.15, §63 step 9.c: a release in hand opens the capsule without any
// network request, even with a clock behind the round time, which decrypt
// only warns about.
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 TestDecryptWithReleaseInHand(t *testing.T) {
dir := t.TempDir()
want, err := os.ReadFile(filepath.Join(fixtures, "time_only.plaintext"))
if err != nil {
t.Fatal(err)
}
jsonFile := filepath.Join(dir, "1000.json")
if err := os.WriteFile(jsonFile, []byte(`{"round":1000,"signature":"`+hex.EncodeToString(testkit.Release(1000).Signature)+`"}`), 0o644); err != nil {
t.Fatal(err)
}
for _, c := range []struct {
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
name, release string
now time.Time
}{
{"a release object", releaseFile(1000), later},
{"a release object and a clock behind", releaseFile(1000), unlock1000.Add(-time.Hour)},
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
{"drand's JSON", jsonFile, later},
{"a local archive", filepath.Join(releases, testkit.ArchiveFile), unlock1000.Add(-time.Nanosecond)},
} {
t.Run(c.name, func(t *testing.T) {
out := filepath.Join(dir, c.name)
_, stderr, err := cli(t, c.now, "decrypt", "-in", filepath.Join(fixtures, "time_only.dkc"), "-out", out, "-release", c.release)
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
if err != nil {
t.Fatal(err)
}
if got, _ := os.ReadFile(out); !bytes.Equal(got, want) {
t.Fatal("wrong content")
}
if behind := strings.Contains(stderr, "may be behind"); behind != c.now.Before(unlock1000) {
t.Fatalf("warning of a clock behind: %v, in %q", behind, stderr)
}
})
}
}
// A release in hand that step 10 rejects gives the code of step 10, and one
// the archive lacks is ErrReleaseUnavailable; nothing is written.
func TestDecryptWithBadReleaseInHand(t *testing.T) {
dir := t.TempDir()
write := func(name string, b []byte) string {
p := filepath.Join(dir, name)
if err := os.WriteFile(p, b, 0o644); err != nil {
t.Fatal(err)
}
return p
}
r1001, _ := os.ReadFile(releaseFile(1001))
r1000, _ := os.ReadFile(releaseFile(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
v2 := bytes.Clone(r1000)
v2[bytes.Index(v2, []byte{0x01, 0x01})+1] = 2
for _, c := range []struct {
name string
release string
capsule string
want *datekeys.Error
}{
{"another round", write("1001.cbor", r1001), "time_only", datekeys.ErrRoundMismatch},
{"version 2", write("v2.cbor", v2), "time_only", datekeys.ErrUnsupportedVersion},
{"a byte after it", write("long.cbor", append(bytes.Clone(r1000), 0)), "time_only", datekeys.ErrNonCanonicalCBOR},
{"a large file", write("large.cbor", append(bytes.Clone(r1000), make([]byte, 20000)...)), "time_only", datekeys.ErrNonCanonicalCBOR},
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
{"bad JSON", write("bad.json", []byte(`{"round":1000}`)), "time_only", datekeys.ErrReleaseInvalid},
{"a round the archive lacks", filepath.Join(releases, testkit.ArchiveFile), "format2_time_and_key_sixteen", datekeys.ErrReleaseUnavailable},
} {
t.Run(c.name, func(t *testing.T) {
out := filepath.Join(dir, c.name)
args := []string{"decrypt", "-in", filepath.Join(fixtures, c.capsule+".dkc"), "-out", out, "-release", c.release}
if c.capsule == "format2_time_and_key_sixteen" {
args = append(args, "-identity", filepath.Join(dir, "none"))
// The capsule needs a credential before the release: give it
// one that is never reached.
write("none", []byte(testkit.Stranger().String()+"\n"))
}
_, _, err := cli(t, later, args...)
if !errors.Is(err, c.want) {
t.Fatalf("got %v, want %v", err, c.want)
}
if _, err := os.Stat(out); !os.IsNotExist(err) {
t.Fatal("output written")
}
})
}
if _, _, err := cli(t, later, "decrypt", "-in", filepath.Join(fixtures, "time_only.dkc"), "-out", filepath.Join(dir, "x"), "-release", "a.cbor", "-relay", "http://127.0.0.1:1"); err == nil {
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
t.Fatal("-release and -relay together")
}
}
// A network source is still never asked before the round time.
func TestDecryptFromRelayBeforeTheRoundTime(t *testing.T) {
_, _, err := cli(t, unlock1000.Add(-time.Second), "decrypt", "-in", filepath.Join(fixtures, "time_only.dkc"), "-out", filepath.Join(t.TempDir(), "x"), "-relay", "http://127.0.0.1:1")
if !errors.Is(err, datekeys.ErrReleaseUnavailable) {
t.Fatalf("got %v", err)
}
}

Powered by TurnKey Linux.