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/capsule/trust_test.go

106 lines
3.5 KiB

package capsule_test
import (
"bytes"
"testing"
"filippo.io/age"
datekeys "g.activething.com/go/DateKeys"
"g.activething.com/go/DateKeys/accesskey"
"g.activething.com/go/DateKeys/agewrap"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/extension"
"g.activething.com/go/DateKeys/internal/testkit"
"g.activething.com/go/DateKeys/profile"
)
// Spec §55.1: what each section proves, and what it never proves.
func TestTrustModel(t *testing.T) {
// PUBLIC_HEADER never proves authorship: from the public bytes of
// time_only.dkc anyone builds a capsule with the same PRELUDE, the same
// PUBLIC_HEADER and another plaintext, which passes all 18 steps
// (spec §36.1).
g := loadFixture(t, "time_only")
p, _ := parts(t, "time_only")
rec, err := agewrap.NewTimeRecipient(profile.Quicknet(), 1000)
if err != nil {
t.Fatal(err)
}
payloadID, err := age.GenerateX25519Identity()
if err != nil {
t.Fatal(err)
}
raw, err := agewrap.RawX25519Identity(payloadID)
if err != nil {
t.Fatal(err)
}
payload, _, err := testkit.Encrypt([]byte("forged"), payloadID.Recipient())
if err != nil {
t.Fatal(err)
}
draft, err := capsule.EncodeControl(&capsule.Control{})
if err != nil {
t.Fatal(err)
}
sealedDraft, _, err := testkit.Encrypt(draft, rec)
if err != nil {
t.Fatal(err)
}
prelude := capsule.Prelude{PublicHeaderLen: uint32(len(p.Header)), SealedControlLen: uint32(len(sealedDraft))}.Bytes()
ctrl := &capsule.Control{HeaderBinding: capsule.HeaderBinding(prelude, p.Header)}
copy(ctrl.PayloadIdentity[:], raw)
control, err := capsule.EncodeControl(ctrl)
if err != nil {
t.Fatal(err)
}
sealed, _, err := testkit.Encrypt(control, rec)
if err != nil || len(sealed) != len(sealedDraft) {
t.Fatalf("sealing: %v", err)
}
if !bytes.Equal(prelude[:], p.Prelude) {
t.Fatalf("PRELUDE %x differs from the official %x", prelude, p.Prelude)
}
forged := testkit.Join(prelude[:], p.Header, sealed, payload)
if got, err := open(t, forged, g.openOptions(t)); err != nil || string(got) != "forged" {
t.Fatalf("forged capsule: %q, %v", got, err)
}
// PUBLIC_HEADER is bound from step 15 only: other extension data passes
// steps 1 to 8 and fails header_binding.
e := loadFixture(t, "time_only_extensions")
ep, err := testkit.Split(e.dkc)
if err != nil {
t.Fatal(err)
}
header := bytes.Replace(ep.Header, []byte("public label"), []byte("Public label"), 1)
if bytes.Equal(header, ep.Header) {
t.Fatal("the header data was not found")
}
edited := testkit.Join(ep.Prelude, header, ep.Sealed, ep.Payload)
if step, err := inspectStep(t, edited, nil); err != nil {
t.Fatalf("steps 1 to 8 rejected the edited header data at step %d: %v", step, err)
}
step, _, err := openStep(t, edited, capsule.OpenOptions{Now: testkit.Fixed(e.unlock(t))})
expectStep(t, "PUBLIC_HEADER data edited", step, err, datekeys.ErrHeaderBinding, 15)
// The data of .dkk extensions is bound to nothing: other data opens the
// capsule all the same (spec §72: advisory for its holder only).
f := loadFixture(t, "time_and_key_portable")
k := loadAccessKey(t, "time_and_key_portable_extension")
k.Noncritical = []extension.Extension{mustExt(t, "org.example.delivery", []byte("anything else"))}
var dkk bytes.Buffer
if err := accesskey.Encode(&dkk, k); err != nil {
t.Fatal(err)
}
back, err := accesskey.Decode(&dkk)
if err != nil {
t.Fatal(err)
}
o := f.openOptions(t)
o.AccessKey = back
if got, err := open(t, f.dkc, o); err != nil || !bytes.Equal(got, f.plaintext) {
t.Fatalf(".dkk with other extension data: %v", err)
}
}

Powered by TurnKey Linux.