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.
117 lines
5.1 KiB
117 lines
5.1 KiB
package capsule_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
datekeys "g.activething.com/go/DateKeys"
|
|
"g.activething.com/go/DateKeys/capsule"
|
|
"g.activething.com/go/DateKeys/extension"
|
|
"g.activething.com/go/DateKeys/internal/testkit"
|
|
)
|
|
|
|
// Spec v0.11 §24.1, §62.1 rule 23: the public note is in PUBLIC_HEADER, in
|
|
// clear, and header_binding ties it to the control: changing it makes step 15
|
|
// fail.
|
|
func TestPublicNote(t *testing.T) {
|
|
opts := files3(t)
|
|
opts.PublicNote = "Cartas del viaje a Lisboa"
|
|
var dkc bytes.Buffer
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
in, err := capsule.Inspect(bytes.NewReader(dkc.Bytes()), capsule.InspectOptions{Registry: testkit.Registry()})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if note, ok := in.Header.PublicNote(); !ok || note != "Cartas del viaje a Lisboa" {
|
|
t.Errorf("the note: %q %v", note, ok)
|
|
}
|
|
// The same bytes with another letter in the note: it still inspects, and
|
|
// Open fails at step 15 with ERR_HEADER_BINDING.
|
|
changed := bytes.Replace(dkc.Bytes(), []byte("Lisboa"), []byte("Lisbon"), 1)
|
|
if bytes.Equal(changed, dkc.Bytes()) {
|
|
t.Fatal("the note is not in clear in the capsule")
|
|
}
|
|
o := defaultOpen(1000)
|
|
o.Sink = &testkit.MemorySink{}
|
|
if _, err := capsule.Open(context.Background(), nil, bytes.NewReader(changed), o); !errors.Is(err, datekeys.ErrHeaderBinding) {
|
|
t.Errorf("a note changed: %v", err)
|
|
}
|
|
if _, err := capsule.Open(context.Background(), nil, bytes.NewReader(dkc.Bytes()), o); err != nil {
|
|
t.Errorf("the capsule with its note: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPublicNoteRules(t *testing.T) {
|
|
for name, text := range map[string]string{
|
|
"a tab": "a\tb",
|
|
"a line feed": "a\nb",
|
|
"a space at the end": "a ",
|
|
"a space at the start": " a",
|
|
"too long": strings.Repeat("a", extension.MaxNoteLen+1),
|
|
"not UTF-8": "\xff",
|
|
} {
|
|
if err := extension.CheckNote(text); err == nil {
|
|
t.Errorf("%s: accepted", name)
|
|
}
|
|
opts := files3(t)
|
|
opts.PublicNote = text
|
|
if _, err := capsule.EncryptFiles(&bytes.Buffer{}, []capsule.Source{source("a", "x")}, opts); err == nil {
|
|
t.Errorf("%s: written", name)
|
|
}
|
|
}
|
|
if err := extension.CheckNote(strings.Repeat("a", extension.MaxNoteLen)); err != nil {
|
|
t.Errorf("1024 bytes: %v", err)
|
|
}
|
|
// An unusable note shows nothing.
|
|
if _, ok := extension.Note([]extension.Extension{{ID: extension.NoteID, Version: 1, Data: []byte("a\nb")}}); ok {
|
|
t.Error("an unusable note was shown")
|
|
}
|
|
// The registry knows it only in PUBLIC_HEADER, noncritical.
|
|
var reg extension.Standard
|
|
if !extension.KnownIn(reg, extension.NoteID, 1, extension.PublicHeader, extension.Noncritical) ||
|
|
extension.KnownIn(reg, extension.NoteID, 1, extension.Control, extension.Noncritical) ||
|
|
extension.KnownIn(reg, extension.NoteID, 1, extension.PublicHeader, extension.Critical) ||
|
|
extension.KnownIn(reg, extension.NoteID, 2, extension.PublicHeader, extension.Noncritical) {
|
|
t.Error("registration of datekeys.note")
|
|
}
|
|
if us := extension.CheckNoncriticalIn(extension.PublicHeader, []extension.Extension{{ID: extension.NoteID, Version: 1, Data: []byte("a\tb")}}, reg); len(us) != 1 {
|
|
t.Errorf("the note with a tab is not unusable: %v", us)
|
|
}
|
|
}
|
|
|
|
// Review: spec §72 forbids an encoder to write a registered extension where
|
|
// it is not registered. A note given as an extension is checked like
|
|
// PublicNote, and no array of a capsule but the noncritical one of
|
|
// PUBLIC_HEADER takes it. In CONTROL_CBOR, critical, it would have inspected
|
|
// well and failed after the date at step 14. datekeys.capsule goes in none.
|
|
func TestRegisteredExtensionsWhereRegistered(t *testing.T) {
|
|
note := extension.Extension{ID: extension.NoteID, Version: 1, Data: []byte("Cartas")}
|
|
tab := extension.Extension{ID: extension.NoteID, Version: 1, Data: []byte("a\tb")}
|
|
capsuleExt := extension.Extension{ID: extension.CapsuleID, Version: 1, Data: []byte{0xa0}}
|
|
for name, set := range map[string]func(*capsule.EncryptOptions){
|
|
"a note with a tab": func(o *capsule.EncryptOptions) { o.Noncritical = []extension.Extension{tab} },
|
|
"a note in critical": func(o *capsule.EncryptOptions) { o.Critical = []extension.Extension{note} },
|
|
"a note in the control": func(o *capsule.EncryptOptions) { o.ControlNoncritical = []extension.Extension{note} },
|
|
"a note in the control, critical": func(o *capsule.EncryptOptions) { o.ControlCritical = []extension.Extension{note} },
|
|
"a note in the head": func(o *capsule.EncryptOptions) { o.HeadNoncritical = []extension.Extension{note} },
|
|
"datekeys.capsule": func(o *capsule.EncryptOptions) { o.Noncritical = []extension.Extension{capsuleExt} },
|
|
} {
|
|
opts := files3(t)
|
|
set(&opts)
|
|
if _, err := capsule.EncryptFiles(&bytes.Buffer{}, []capsule.Source{source("a", "x")}, opts); err == nil {
|
|
t.Errorf("%s: written", name)
|
|
}
|
|
}
|
|
opts := files3(t)
|
|
opts.Noncritical = []extension.Extension{note}
|
|
var dkc bytes.Buffer
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("a", "x")}, opts); err != nil {
|
|
t.Fatalf("a valid note as an extension: %v", err)
|
|
}
|
|
}
|