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/locator/vectors_test.go

137 lines
5.1 KiB

package locator_test
import (
"bytes"
"encoding/hex"
"errors"
"path/filepath"
"slices"
"testing"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/extension"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/locator"
"g.activething.com/go/DateKeys/profile"
)
func unhex(t *testing.T, s string) []byte {
t.Helper()
b, err := hex.DecodeString(s)
if err != nil {
t.Fatal(err)
}
return b
}
// testdata/vectors/locator.json is frozen: the extension of the .dkk reads,
// the sealed locator opens with the release of its round, the rest is found
// in the host at its offset, the envelope gives the .dkc back, and the rules
// of the addresses and of the padding give what the file says (spec v0.12,
// §44.1). So do its cases of §64: a locator whose addresses a reader rejects
// and uses in turn, the resources of the rest, the data of the extension and
// the plaintexts of the locator.
func TestLocatorVectors(t *testing.T) {
var v testkit.LocatorVectorFile
if err := testkit.ReadJSON(filepath.Join("..", "testdata", "vectors", "locator.json"), &v); err != nil {
t.Fatal(err)
}
p := profile.Quicknet()
info, err := locator.ParseInfo(extension.Extension{ID: extension.CapsuleID, Version: 1, Data: unhex(t, v.Extension)})
if err != nil || info.Note != v.Note || info.DateKey.Compact() != v.DateKey || info.DateKey.Round != v.Round || !bytes.Equal(info.Sealed, unhex(t, v.Sealed)) {
t.Fatalf("the extension: %+v, %v", info, err)
}
loc, err := locator.Open(p, v.Round, testkit.Release(v.Round), unhex(t, v.Sealed))
if err != nil {
t.Fatal(err)
}
plain, err := loc.Marshal()
if err != nil || !bytes.Equal(plain, unhex(t, v.Plaintext)) || len(plain) != locator.Block {
t.Fatalf("the plaintext of the locator: %d bytes, %v", len(plain), err)
}
if hex.EncodeToString(loc.EnvelopeKey[:]) != v.EnvelopeKey || hex.EncodeToString(loc.RestDigest[:]) != v.RestDigest || loc.RestSize != v.RestSize ||
hex.EncodeToString(loc.CapsuleDigest[:]) != v.CapsuleDigest || hex.EncodeToString(loc.EnvelopeHeader) != v.Header || len(loc.Addresses) != len(v.Addresses) {
t.Fatalf("the fields of the locator: %+v", loc)
}
for i, a := range v.Addresses {
if loc.Addresses[i].URI != a.URI || loc.Addresses[i].Offset != a.Offset || loc.Addresses[i].Host() != a.Host {
t.Errorf("address %d: %+v", i, loc.Addresses[i])
}
}
// The rest, from the host at the offset of the first address, opens the envelope.
rest, err := loc.RestIn(unhex(t, v.Host), v.HostOffset)
if err != nil || !bytes.Equal(rest, unhex(t, v.Rest)) {
t.Fatalf("the rest in the host: %v", err)
}
dkc, err := loc.OpenEnvelope(rest)
if err != nil || !bytes.Equal(dkc, unhex(t, v.DKC)) {
t.Fatalf("the envelope: %v", err)
}
for _, c := range v.PaddingCases {
if got := locator.PlaintextLength(c.Base); got != c.Total || got%locator.Block != 0 {
t.Errorf("padding of %d: %d, want %d", c.Base, got, c.Total)
}
}
for _, c := range v.URICases {
if got := locator.CheckURI(c.URI) == nil; got != c.OK {
t.Errorf("%q: accepted %v, want %v", c.URI, got, c.OK)
}
}
// The mixed locator reads: a reader rejects the addresses that break the
// rules and uses the other, which finds the rest in the host.
want := unhex(t, v.DKC)
mixed, err := locator.Open(p, v.Round, testkit.Release(v.Round), unhex(t, v.Mixed.Sealed))
if err != nil {
t.Fatal(err)
}
if back, err := locator.Unmarshal(unhex(t, v.Mixed.Plaintext)); err != nil || len(back.Addresses) != len(v.Mixed.Addresses) || len(mixed.Addresses) != len(v.Mixed.Addresses) {
t.Fatalf("the mixed locator: %v", err)
}
var usable []locator.Address
for i, a := range v.Mixed.Addresses {
if mixed.Addresses[i] != (locator.Address{URI: a.URI, Offset: a.Offset}) {
t.Errorf("mixed address %d: %+v", i, mixed.Addresses[i])
}
if a.Usable {
usable = append(usable, mixed.Addresses[i])
}
}
if got := mixed.Usable(); len(usable) == 0 || !slices.Equal(got, usable) {
t.Fatalf("usable: %v, want %v", got, usable)
}
if rest, err = mixed.RestIn(unhex(t, v.Host), usable[0].Offset); err != nil {
t.Fatal(err)
}
if dkc, err := mixed.OpenEnvelope(rest); err != nil || !bytes.Equal(dkc, want) {
t.Fatalf("the envelope of the mixed locator: %v", err)
}
// A reader reads RestSize bytes from the offset, whatever follows, and
// uses them only when their SHA-256 is resto_digest.
for _, c := range v.RestCases {
opens := false
if r, err := loc.RestIn(unhex(t, c.Resource), c.Offset); err == nil {
dkc, err := loc.OpenEnvelope(r)
opens = err == nil && bytes.Equal(dkc, want)
}
if opens != c.Opens {
t.Errorf("%s: opens %v, want %v", c.Name, opens, c.Opens)
}
}
// An extension that a reader cannot use carries only
// ERR_EXTENSION_DATA_INVALID (spec §54).
for _, c := range v.ExtensionCases {
_, err := locator.ParseInfo(extension.Extension{ID: extension.CapsuleID, Version: 1, Data: unhex(t, c.Data)})
if (err == nil) != c.OK || err != nil && !errors.Is(err, datekeys.ErrExtensionDataInvalid) {
t.Errorf("%s: %v", c.Name, err)
}
}
for _, c := range v.PlaintextCases {
if _, err := locator.Unmarshal(unhex(t, c.Plaintext)); (err == nil) != c.OK {
t.Errorf("%s: %v", c.Name, err)
}
}
}

Powered by TurnKey Linux.