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

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)) }
// 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.
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 {
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)},
{"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)
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))
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},
{"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 {
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.