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/signed_test.go

182 lines
6.3 KiB

package capsule_test
import (
"bytes"
"context"
"encoding/hex"
"strings"
"testing"
"g.activething.com/go/DateKeys/authorkey"
"g.activething.com/go/DateKeys/capsule"
"g.activething.com/go/DateKeys/internal/testkit"
)
// openSigned opens dkc with the author keys that the person saved.
func openSigned(t *testing.T, dkc []byte, saved map[string]string) *capsule.Opened {
t.Helper()
o := defaultOpen(1000)
o.Sink, o.AuthorKeys = &testkit.MemorySink{}, saved
opened, err := capsule.Open(context.Background(), nil, bytes.NewReader(dkc), o)
if err != nil {
t.Fatal(err)
}
return opened
}
// Spec v0.11 §29.7, §29.8, §62.1 rule 19: EncryptFiles signs with the key of
// opts.AuthorKey and Open gives F4, or F3 with the key saved.
func TestEncryptFilesSigned(t *testing.T) {
key, err := authorkey.Generate()
if err != nil {
t.Fatal(err)
}
pub, _ := authorkey.PublicString(key.Public())
opts := files3(t)
opts.AuthorKey = key
var dkc bytes.Buffer
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
t.Fatal(err)
}
o := openSigned(t, dkc.Bytes(), nil)
if o.Verdicts.Signature != capsule.VerdictSignedOther || o.Verdicts.AuthorKey != [32]byte(key.Public()) || o.Verdicts.Seal != capsule.VerdictNoSeal || o.AreaLen != capsule.AreaLen {
t.Errorf("verdicts %+v, area %d", o.Verdicts, o.AreaLen)
}
o = openSigned(t, dkc.Bytes(), map[string]string{pub: "Ana"})
if o.Verdicts.Signature != capsule.VerdictSignedSaved || o.Verdicts.AuthorLabel != "Ana" {
t.Errorf("saved key: verdicts %+v", o.Verdicts)
}
other, _ := authorkey.Generate()
otherPub, _ := authorkey.PublicString(other.Public())
if o = openSigned(t, dkc.Bytes(), map[string]string{otherPub: "Luis"}); o.Verdicts.Signature != capsule.VerdictSignedOther {
t.Errorf("another saved key: verdicts %+v", o.Verdicts)
}
// The area grows only when asked, and the signature still verifies.
opts.LargeArea = true
dkc.Reset()
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
t.Fatal(err)
}
if o = openSigned(t, dkc.Bytes(), nil); o.AreaLen != capsule.LargeAreaLen || o.Verdicts.Signature != capsule.VerdictSignedOther {
t.Errorf("large area: verdicts %+v, area %d", o.Verdicts, o.AreaLen)
}
}
// badKey is an AuthorKey that signs wrongly or has a public key of the wrong
// length.
type badKey struct {
pub, sig []byte
}
func (k badKey) Public() []byte { return k.pub }
func (k badKey) Sign([]byte) []byte { return k.sig }
// Spec v0.11 §62.1 rule 19: a signature that does not verify, or a key of
// another length, fails before anything is written.
func TestEncryptFilesSignatureChecked(t *testing.T) {
key, _ := authorkey.Generate()
for _, tc := range []struct {
name string
key capsule.AuthorKey
want string
}{
{"wrong signature", badKey{key.Public(), make([]byte, 64)}, "self-check"},
{"short key", badKey{key.Public()[:31], make([]byte, 64)}, "not 32"},
} {
opts := files3(t)
opts.AuthorKey = tc.key
var dkc bytes.Buffer
_, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "x")}, opts)
if err == nil || !strings.Contains(err.Error(), tc.want) {
t.Errorf("%s: %v", tc.name, err)
}
if dkc.Len() != 0 {
t.Errorf("%s: %d bytes written", tc.name, dkc.Len())
}
}
}
// Spec v0.11 §29.7, §29.8: a signature of the fixture format3_signed holds
// in the context of its capsule and in no other: a bit of the signature, of
// a commitment or of the message changes the verdict to F2; the same message
// signed by another key is another key's F4; and a security area without it
// is F0.
func TestSignedFixtureVerdicts(t *testing.T) {
f := loadFixture(t, "format3_signed")
body := f.plaintext
frame, err := capsule.ParseBodyFrame(body[:capsule.BodyFrameSize], uint64(len(body)))
if err != nil {
t.Fatal(err)
}
security := body[capsule.BodyFrameSize : capsule.BodyFrameSize+frame.SecurityLen]
cb, _ := hex.DecodeString(f.ControlCBOR)
c, err := capsule.DecodeControl(cb, f.format())
if err != nil {
t.Fatal(err)
}
cc, err := capsule.ControlCommit(c, f.format())
if err != nil {
t.Fatal(err)
}
hb := body[capsule.BodyFrameSize+frame.AreaLen : capsule.BodyFrameSize+frame.AreaLen+frame.HeadLen]
ctx := func() *capsule.SecurityContext {
return &capsule.SecurityContext{ControlCommit: cc, HeadDigest: capsule.HeadDigest(hb)}
}
if v := capsule.EvaluateSecurityIn(security, ctx()); v.Signature != capsule.VerdictSignedOther || v.Seal != capsule.VerdictNoSeal {
t.Fatalf("the signature of the fixture: %+v", v)
}
// Another capsule: another control commitment, or another head.
other := ctx()
other.ControlCommit[0] ^= 1
if v := capsule.EvaluateSecurityIn(security, other); v.Signature != capsule.VerdictSignatureInvalid {
t.Errorf("another control: %+v", v)
}
other = ctx()
other.HeadDigest[31] ^= 1
if v := capsule.EvaluateSecurityIn(security, other); v.Signature != capsule.VerdictSignatureInvalid {
t.Errorf("another head: %+v", v)
}
// A bit of the signature, or of the key.
_, value, err := capsule.SecurityKey2(security)
if err != nil {
t.Fatal(err)
}
pub, _ := authorkey.ParsePublic(f.Signature.AuthorKey)
for name, mutate := range map[string]func(sig, key []byte){
"signature": func(sig, key []byte) { sig[63] ^= 1 },
"key": func(sig, key []byte) { key[0] ^= 1 },
} {
sig, key := bytes.Clone(value), bytes.Clone(pub)
mutate(sig, key)
x, err := capsule.EncodeAuthorSignature(capsule.AlgEd25519, key, sig)
if err != nil {
t.Fatal(err)
}
s, err := capsule.EncodeSecurityWith(x, nil)
if err != nil {
t.Fatal(err)
}
if v := capsule.EvaluateSecurityIn(s, ctx()); v.Signature != capsule.VerdictSignatureInvalid {
t.Errorf("a bit of the %s: %+v", name, v)
}
}
// The same message signed by another key: F4 with that key.
msg := capsule.AuthorMessage(cc, capsule.HeadDigest(hb), capsule.SignersDigest(capsule.AlgEd25519, nil))
k, _ := authorkey.Generate()
x, _ := capsule.EncodeAuthorSignature(capsule.AlgEd25519, k.Public(), k.Sign(msg))
s, _ := capsule.EncodeSecurityWith(x, nil)
if v := capsule.EvaluateSecurityIn(s, ctx()); v.Signature != capsule.VerdictSignedOther || v.AuthorKey != [32]byte(k.Public()) {
t.Errorf("another key: %+v", v)
}
// Removed: F0.
if v := capsule.EvaluateSecurityIn(capsule.EncodeSecurity(), ctx()); v.Signature != capsule.VerdictNoSignature {
t.Errorf("removed: %+v", v)
}
}

Powered by TurnKey Linux.