|
|
|
|
package locator_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"crypto/rand"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"g.activething.com/go/DateKeys/datekey"
|
|
|
|
|
"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"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const cid = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"
|
|
|
|
|
|
|
|
|
|
func sample(t *testing.T) (*locator.Locator, []byte, []byte) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
dkc := make([]byte, 5000)
|
|
|
|
|
rand.Read(dkc)
|
|
|
|
|
loc, rest, err := locator.NewEnvelope(dkc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
loc.Addresses = []locator.Address{{URI: "https://ejemplo.org/foto.jpg", Offset: 123456}, {URI: "ipfs://" + cid}}
|
|
|
|
|
return loc, rest, dkc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spec v0.11 §44.1: the envelope is the .dkc in age, split in a header that
|
|
|
|
|
// the locator carries and a rest without a mark.
|
|
|
|
|
func TestEnvelope(t *testing.T) {
|
|
|
|
|
loc, rest, dkc := sample(t)
|
|
|
|
|
if bytes.Contains(rest, []byte("age-encryption")) || len(rest) != int(loc.RestSize) || len(loc.EnvelopeHeader) > locator.MaxHeaderLen || !bytes.HasSuffix(loc.EnvelopeHeader, []byte("\n")) {
|
|
|
|
|
t.Fatalf("rest of %d bytes, header %q", len(rest), loc.EnvelopeHeader)
|
|
|
|
|
}
|
|
|
|
|
got, err := loc.OpenEnvelope(rest)
|
|
|
|
|
if err != nil || !bytes.Equal(got, dkc) {
|
|
|
|
|
t.Fatalf("the envelope: %v", err)
|
|
|
|
|
}
|
|
|
|
|
// Inside another file: the rest is read from its offset, and only that.
|
|
|
|
|
host := bytes.Repeat([]byte("JPEG"), 1000)
|
|
|
|
|
file, offset := locator.Hide(host, rest)
|
|
|
|
|
trailing := append(bytes.Clone(file), []byte("more after")...)
|
|
|
|
|
for _, f := range [][]byte{file, trailing} {
|
|
|
|
|
r, err := loc.RestIn(f, offset)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if got, err := loc.OpenEnvelope(r); err != nil || !bytes.Equal(got, dkc) {
|
|
|
|
|
t.Errorf("rest in a host: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if _, err := loc.RestIn(host, uint64(len(host))); err == nil {
|
|
|
|
|
t.Error("a rest beyond the end of the resource")
|
|
|
|
|
}
|
|
|
|
|
// Whoever recompresses the host breaks the rest, and the locator says so.
|
|
|
|
|
bad := bytes.Clone(rest)
|
|
|
|
|
bad[len(bad)/2] ^= 1
|
|
|
|
|
if _, err := loc.OpenEnvelope(bad); err == nil || !strings.Contains(err.Error(), "SHA-256 of the rest") {
|
|
|
|
|
t.Errorf("a rest changed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := loc.OpenEnvelope(rest[:len(rest)-1]); err == nil {
|
|
|
|
|
t.Error("a short rest")
|
|
|
|
|
}
|
|
|
|
|
wrong := *loc
|
|
|
|
|
wrong.CapsuleDigest[0] ^= 1
|
|
|
|
|
if _, err := wrong.OpenEnvelope(rest); err == nil || !strings.Contains(err.Error(), "capsule_digest") {
|
|
|
|
|
t.Errorf("another capsule_digest: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spec §44.1: the plaintext of the locator measures 4096 bytes, or the least
|
|
|
|
|
// multiple that holds it, so its length does not tell the addresses.
|
|
|
|
|
func TestLocatorPlaintext(t *testing.T) {
|
|
|
|
|
loc, _, _ := sample(t)
|
|
|
|
|
one := *loc
|
|
|
|
|
one.Addresses = loc.Addresses[:1]
|
|
|
|
|
for _, l := range []*locator.Locator{&one, loc} {
|
|
|
|
|
b, err := l.Marshal()
|
|
|
|
|
if err != nil || len(b) != locator.Block {
|
|
|
|
|
t.Fatalf("%d bytes, %v", len(b), err)
|
|
|
|
|
}
|
|
|
|
|
back, err := locator.Unmarshal(b)
|
|
|
|
|
if err != nil || len(back.Addresses) != len(l.Addresses) || back.Addresses[0] != l.Addresses[0] || back.EnvelopeKey != l.EnvelopeKey ||
|
|
|
|
|
!bytes.Equal(back.EnvelopeHeader, l.EnvelopeHeader) || back.RestDigest != l.RestDigest || back.RestSize != l.RestSize || back.CapsuleDigest != l.CapsuleDigest {
|
|
|
|
|
t.Fatalf("the round trip: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Eight long addresses make it larger: the least multiple of 4096.
|
|
|
|
|
big := *loc
|
|
|
|
|
big.Addresses = nil
|
|
|
|
|
for range locator.MaxAddresses {
|
|
|
|
|
big.Addresses = append(big.Addresses, locator.Address{URI: "https://ejemplo.org/" + strings.Repeat("a", 900), Offset: 1 << 40})
|
|
|
|
|
}
|
|
|
|
|
b, err := big.Marshal()
|
|
|
|
|
if err != nil || len(b)%locator.Block != 0 || len(b) < 2*locator.Block {
|
|
|
|
|
t.Fatalf("%d bytes, %v", len(b), err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := locator.Unmarshal(b); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
// Anything other than that length is not canonical.
|
|
|
|
|
b1, _ := one.Marshal()
|
|
|
|
|
if _, err := locator.Unmarshal(append(bytes.Clone(b1), make([]byte, locator.Block)...)); err == nil {
|
|
|
|
|
t.Error("a plaintext of two blocks for one")
|
|
|
|
|
}
|
|
|
|
|
if _, err := locator.Unmarshal(b1[:len(b1)-1]); err == nil {
|
|
|
|
|
t.Error("a truncated plaintext")
|
|
|
|
|
}
|
|
|
|
|
nonzero := bytes.Clone(b1)
|
|
|
|
|
nonzero[len(nonzero)-1] = 1
|
|
|
|
|
if _, err := locator.Unmarshal(nonzero); err == nil {
|
|
|
|
|
t.Error("padding that is not zeros")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAddresses(t *testing.T) {
|
Review fixes: author keys, the writer, the CLI, extensions and the locator
Fixes of the review of the session of 1 and 2 October that the text of
spec v0.11 already asks for:
- authorkey: String and GoString hide the secret key, which only Secret
returns; ParsePublic refuses a key that is not a point of the curve
(ed25519strict.OnCurve, checked against the square root of testkit).
- capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never
a capsule without the signature or the seal that was asked for. A panic
while evaluating the signature or the seal fails only that part, F1 or
S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can
refuse to publish the files.
- extension.CheckWrite, the rule of encoders of spec 72: the writers of
capsules and .dkk files refuse datekeys.note and datekeys.capsule outside
the arrays where they are registered, or with invalid data.
- CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE
before it signs (rule 20); decrypt -expect-author compares the key of an
F4 and writes nothing unless it matches; decrypt notifies a public note
that it does not show; the lines of the verdicts break at the last space
that fits, each row after the first behind a mark, so that the terminal
never breaks them; L is the payload, not the content.
- locator: a reader rejects an address that breaks 44.1 and keeps the
others; addresses refuse the special-purpose blocks of IANA, IPv6 outside
2000::/3, localhost and local names, characters outside RFC 3986, dot
segments, and a CID that does not decode to version 1 and a multihash;
ParseInfo checks that the locator is an age file with one tlock stanza
for the round of its DateKey; Info.Extension reads what it writes; its
errors carry no normative code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
for _, ok := range []string{"https://ejemplo.org/a.bin", "https://ejemplo.org:8443/x?y=1", "ipfs://" + cid, "ipfs://" + cid + "/ruta",
|
Spec v0.12 draft: the rules of an address that the reference applied
The text of 44.1 now says what the reference already refused and a
second implementation that followed the text would have accepted:
segments of 1 to 63 characters that neither start nor end with a hyphen,
the scheme in lower case, 0x and the local names in either case, an IPv4
without leading zeros, and a CID of at most 128 characters in canonical
base32, with minimal varints and a digest of at least one byte. A port is
written without leading zeros: the reference accepted 0443 and 00443 and
refused 000443, the same number, and now refuses the three. Change 6 of
section 76 records it, and section 64 lists the cases.
locator.json is made again with 30 more addresses, 247 in all, and
TestAddresses checks the same rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days ago
|
|
|
"https://1.1.1.1/a", "https://[2606:4700::1111]/a", "https://ejemplo.org/a%20b", "https://ejemplo.org/~ana/(1),x;y=z",
|
|
|
|
|
"https://" + strings.Repeat("a", 63) + ".org/", "https://a--b.ejemplo.org/", "https://Ejemplo.ORG:443/"} {
|
|
|
|
|
if err := locator.CheckURI(ok); err != nil {
|
|
|
|
|
t.Errorf("%s: %v", ok, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, bad := range []string{"", "http://ejemplo.org/a", "file:///etc/passwd", "ftp://x/y", "https://user:pass@ejemplo.org/", "https://", "ipfs://notacid",
|
Review fixes: author keys, the writer, the CLI, extensions and the locator
Fixes of the review of the session of 1 and 2 October that the text of
spec v0.11 already asks for:
- authorkey: String and GoString hide the secret key, which only Secret
returns; ParsePublic refuses a key that is not a point of the curve
(ed25519strict.OnCurve, checked against the square root of testkit).
- capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never
a capsule without the signature or the seal that was asked for. A panic
while evaluating the signature or the seal fails only that part, F1 or
S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can
refuse to publish the files.
- extension.CheckWrite, the rule of encoders of spec 72: the writers of
capsules and .dkk files refuse datekeys.note and datekeys.capsule outside
the arrays where they are registered, or with invalid data.
- CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE
before it signs (rule 20); decrypt -expect-author compares the key of an
F4 and writes nothing unless it matches; decrypt notifies a public note
that it does not show; the lines of the verdicts break at the last space
that fits, each row after the first behind a mark, so that the terminal
never breaks them; L is the payload, not the content.
- locator: a reader rejects an address that breaks 44.1 and keeps the
others; addresses refuse the special-purpose blocks of IANA, IPv6 outside
2000::/3, localhost and local names, characters outside RFC 3986, dot
segments, and a CID that does not decode to version 1 and a multihash;
ParseInfo checks that the locator is an age file with one tlock stanza
for the round of its DateKey; Info.Extension reads what it writes; its
errors carry no normative code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
"https://ejemplo.org/ñ", "https://ejemplo.org/a b", "ipfs://Qm" + strings.Repeat("a", 44), "https://" + strings.Repeat("a", 1100),
|
|
|
|
|
// Review: addresses that are not public, and names that only a machine
|
|
|
|
|
// or a local network resolves.
|
|
|
|
|
"https://100.64.0.1/", "https://0.1.2.3/", "https://192.0.0.8/", "https://198.18.0.1/", "https://240.0.0.1/", "https://255.255.255.255/",
|
|
|
|
|
"https://[64:ff9b::7f00:1]/", "https://[64:ff9b::a9fe:a9fe]/", "https://[::127.0.0.1]/", "https://[::ffff:127.0.0.1]/",
|
|
|
|
|
"https://[2002:7f00:1::1]/", "https://[2001::1]/", "https://[2001:db8::1]/", "https://[fec0::1]/", "https://[fc00::1]/", "https://[ff02::1]/",
|
|
|
|
|
"https://localhost/", "https://foo.localhost/", "https://intranet/", "https://a.local/", "https://router.home.arpa/", "https://x.internal/",
|
|
|
|
|
// Characters that RFC 3986 does not allow, a broken percent sign and a
|
|
|
|
|
// "." or ".." segment.
|
|
|
|
|
"https://a.org/%zz", "https://a.org/%", "https://a.org/<script>", "https://a.org/{x}", "https://a.org/a|b", "https://a.org/^", "https://a.org/`",
|
|
|
|
|
"ipfs://" + cid + "/../../ipns/x", "ipfs://" + cid + "/%2e%2e/x", "https://a.org/./x",
|
|
|
|
|
// Base32 that is not a CID v1: no version 1, or a multihash cut.
|
Spec v0.12 draft: the rules of an address that the reference applied
The text of 44.1 now says what the reference already refused and a
second implementation that followed the text would have accepted:
segments of 1 to 63 characters that neither start nor end with a hyphen,
the scheme in lower case, 0x and the local names in either case, an IPv4
without leading zeros, and a CID of at most 128 characters in canonical
base32, with minimal varints and a digest of at least one byte. A port is
written without leading zeros: the reference accepted 0443 and 00443 and
refused 000443, the same number, and now refuses the three. Change 6 of
section 76 records it, and section 64 lists the cases.
locator.json is made again with 30 more addresses, 247 in all, and
TestAddresses checks the same rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days ago
|
|
|
"ipfs://b" + strings.Repeat("a", 39), "ipfs://" + cid[:len(cid)-2],
|
|
|
|
|
// Spec v0.12 writes what the reference already refused: segments of 1
|
|
|
|
|
// to 63 characters that neither start nor end with a hyphen, the
|
|
|
|
|
// scheme in lower case, 0x and local names in either case, an IPv4
|
|
|
|
|
// without leading zeros and a canonical CID; and a port without
|
|
|
|
|
// leading zeros, which it accepted up to five digits.
|
|
|
|
|
"https://" + strings.Repeat("a", 64) + ".org/", "https://-a.ejemplo.org/", "https://a-.ejemplo.org/", "https://ejemplo.org./", "https://a..b.org/",
|
|
|
|
|
"HTTPS://ejemplo.org/", "IPFS://" + cid, "https://ejemplo.0X10/", "https://nas.LOCAL/", "https://01.2.3.4/", "https://8.8.8.08/",
|
|
|
|
|
"https://ejemplo.org:0443/", "https://ejemplo.org:00443/", "https://[2606:4700::1111]:0443/",
|
|
|
|
|
"ipfs://" + cid[:len(cid)-1] + "j", "ipfs://" + cid + "==", "ipfs://b" + strings.ToUpper(cid[1:]), "ipfs://bafkqaaa"} {
|
|
|
|
|
if err := locator.CheckURI(bad); err == nil {
|
|
|
|
|
t.Errorf("%q accepted", bad)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if h := (locator.Address{URI: "https://ejemplo.org:8443/x"}).Host(); h != "ejemplo.org" {
|
|
|
|
|
t.Errorf("host %q", h)
|
|
|
|
|
}
|
|
|
|
|
if h := (locator.Address{URI: "ipfs://" + cid}).Host(); h != cid {
|
|
|
|
|
t.Errorf("cid %q", h)
|
|
|
|
|
}
|
|
|
|
|
loc, _, _ := sample(t)
|
|
|
|
|
loc.Addresses = nil
|
|
|
|
|
if _, err := loc.Marshal(); err == nil {
|
|
|
|
|
t.Error("no addresses")
|
|
|
|
|
}
|
|
|
|
|
loc.Addresses = make([]locator.Address, 9)
|
|
|
|
|
if _, err := loc.Marshal(); err == nil {
|
|
|
|
|
t.Error("nine addresses")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spec §44.1: the locator is an age file with one tlock stanza for the round
|
|
|
|
|
// of the DateKey: it opens with that release and with no other.
|
|
|
|
|
func TestSealedLocator(t *testing.T) {
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
loc, _, _ := sample(t)
|
|
|
|
|
sealed, err := locator.Seal(p, 1000, loc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if bytes.Contains(sealed, []byte(cid)) || bytes.Contains(sealed, loc.EnvelopeKey[:]) {
|
|
|
|
|
t.Error("the locator is not hidden")
|
|
|
|
|
}
|
|
|
|
|
back, err := locator.Open(p, 1000, testkit.Release(1000), sealed)
|
|
|
|
|
if err != nil || back.EnvelopeKey != loc.EnvelopeKey || back.Addresses[1].URI != loc.Addresses[1].URI {
|
|
|
|
|
t.Fatalf("Open: %v", err)
|
|
|
|
|
}
|
|
|
|
|
// Another round: unusable.
|
|
|
|
|
if _, err := locator.Open(p, 1001, testkit.Release(1001), sealed); err == nil {
|
|
|
|
|
t.Error("a locator for round 1000 opened with round 1001")
|
|
|
|
|
}
|
|
|
|
|
if _, err := locator.Open(p, 1000, testkit.Release(1001), sealed); err == nil {
|
|
|
|
|
t.Error("a locator opened with another release")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestInfo(t *testing.T) {
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
dk, err := datekey.Resolve(p, testkit.Genesis().AddDate(0, 0, 400))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
Review fixes: author keys, the writer, the CLI, extensions and the locator
Fixes of the review of the session of 1 and 2 October that the text of
spec v0.11 already asks for:
- authorkey: String and GoString hide the secret key, which only Secret
returns; ParsePublic refuses a key that is not a point of the curve
(ed25519strict.OnCurve, checked against the square root of testkit).
- capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never
a capsule without the signature or the seal that was asked for. A panic
while evaluating the signature or the seal fails only that part, F1 or
S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can
refuse to publish the files.
- extension.CheckWrite, the rule of encoders of spec 72: the writers of
capsules and .dkk files refuse datekeys.note and datekeys.capsule outside
the arrays where they are registered, or with invalid data.
- CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE
before it signs (rule 20); decrypt -expect-author compares the key of an
F4 and writes nothing unless it matches; decrypt notifies a public note
that it does not show; the lines of the verdicts break at the last space
that fits, each row after the first behind a mark, so that the terminal
never breaks them; L is the payload, not the content.
- locator: a reader rejects an address that breaks 44.1 and keeps the
others; addresses refuse the special-purpose blocks of IANA, IPv6 outside
2000::/3, localhost and local names, characters outside RFC 3986, dot
segments, and a CID that does not decode to version 1 and a multihash;
ParseInfo checks that the locator is an age file with one tlock stanza
for the round of its DateKey; Info.Extension reads what it writes; its
errors carry no normative code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
loc, _, _ := sample(t)
|
|
|
|
|
sealed, err := locator.Seal(p, dk.Round, loc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
other, err := locator.Seal(p, dk.Round+1, loc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
// Review: the locator is an age file with one tlock stanza for the round
|
|
|
|
|
// of the DateKey; anything else is refused by the writer, and unusable.
|
|
|
|
|
for name, s := range map[string][]byte{"not an age file": []byte("an age file"), "another round": other} {
|
|
|
|
|
if _, err := (&locator.Info{DateKey: dk, Sealed: s}).Extension(); err == nil {
|
|
|
|
|
t.Errorf("%s: written", name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, in := range []*locator.Info{
|
|
|
|
|
{DateKey: dk},
|
|
|
|
|
{Note: "Cartas del viaje a Lisboa", DateKey: dk},
|
Review fixes: author keys, the writer, the CLI, extensions and the locator
Fixes of the review of the session of 1 and 2 October that the text of
spec v0.11 already asks for:
- authorkey: String and GoString hide the secret key, which only Secret
returns; ParsePublic refuses a key that is not a point of the curve
(ed25519strict.OnCurve, checked against the square root of testkit).
- capsule: a typed nil in AuthorKey, CMSSigner or Sealer is an error, never
a capsule without the signature or the seal that was asked for. A panic
while evaluating the signature or the seal fails only that part, F1 or
S2, not both. OpenOptions.Accept sees the verdicts before step 18 and can
refuse to publish the files.
- extension.CheckWrite, the rule of encoders of spec 72: the writers of
capsules and .dkk files refuse datekeys.note and datekeys.capsule outside
the arrays where they are registered, or with invalid data.
- CLI: encrypt -sign shows the author key and the code of AUTHOR_MESSAGE
before it signs (rule 20); decrypt -expect-author compares the key of an
F4 and writes nothing unless it matches; decrypt notifies a public note
that it does not show; the lines of the verdicts break at the last space
that fits, each row after the first behind a mark, so that the terminal
never breaks them; L is the payload, not the content.
- locator: a reader rejects an address that breaks 44.1 and keeps the
others; addresses refuse the special-purpose blocks of IANA, IPv6 outside
2000::/3, localhost and local names, characters outside RFC 3986, dot
segments, and a CID that does not decode to version 1 and a multihash;
ParseInfo checks that the locator is an age file with one tlock stanza
for the round of its DateKey; Info.Extension reads what it writes; its
errors carry no normative code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
|
|
|
{Note: "Con localizador", DateKey: dk, Sealed: sealed},
|
|
|
|
|
} {
|
|
|
|
|
x, err := in.Extension()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
out, err := locator.ParseInfo(x)
|
|
|
|
|
if err != nil || out.Note != in.Note || out.DateKey != in.DateKey || !bytes.Equal(out.Sealed, in.Sealed) {
|
|
|
|
|
t.Errorf("%+v: %v", in, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// What makes the extension unusable.
|
|
|
|
|
good, _ := (&locator.Info{Note: "n", DateKey: dk}).Extension()
|
|
|
|
|
for name, x := range map[string]extension.Extension{
|
|
|
|
|
"another id": {ID: extension.NoteID, Version: 1, Data: good.Data},
|
|
|
|
|
"version 2": {ID: extension.CapsuleID, Version: 2, Data: good.Data},
|
|
|
|
|
"no data": {ID: extension.CapsuleID, Version: 1},
|
|
|
|
|
"not CBOR": {ID: extension.CapsuleID, Version: 1, Data: []byte("nope")},
|
|
|
|
|
"a bad DateKey": {ID: extension.CapsuleID, Version: 1, Data: []byte{0xa1, 0x01, 0x61, 0x6e}},
|
|
|
|
|
"a note with tab": {ID: extension.CapsuleID, Version: 1, Data: []byte{0xa2, 0x00, 0x63, 'a', '\t', 'b', 0x01, 0x60}},
|
|
|
|
|
} {
|
|
|
|
|
if _, err := locator.ParseInfo(x); err == nil {
|
|
|
|
|
t.Errorf("%s: accepted", name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if _, err := (&locator.Info{Note: "a\nb", DateKey: dk}).Extension(); err == nil {
|
|
|
|
|
t.Error("a note with a line feed")
|
|
|
|
|
}
|
|
|
|
|
}
|