package capsule_test import ( "bytes" "strings" "testing" "g.activething.com/go/DateKeys/authorkey" "g.activething.com/go/DateKeys/capsule" ) // Spec v0.11 §29.8: AUTHOR_MESSAGE is ASCII text of 99 bytes with its code, // and control_commit does not depend on L. func TestAuthorMessage(t *testing.T) { var cc, hd [32]byte cc[0], hd[0] = 1, 2 m := capsule.AuthorMessage(cc, hd, capsule.SignersDigest(capsule.AlgEd25519, nil)) if capsule.AuthorMessageSize != 99 || len(m) != 99 || !bytes.HasPrefix(m, []byte(capsule.AuthorMessagePrefix+"\n")) || m[98] != '\n' { t.Fatalf("AUTHOR_MESSAGE %q", m) } if code := capsule.AuthorCode(m); len(code) != 9 || code[4] != '-' || code[:4] != string(m[34:38]) { t.Errorf("code %q", code) } if capsule.SignersDigest(capsule.AlgEd25519, nil) == capsule.SignersDigest(capsule.AlgCMS, nil) { t.Error("signers_digest does not bind alg") } c := &capsule.Control{PayloadLength: 100, Padding: capsule.Reforzado} c.PayloadIdentity[0] = 7 a, err := capsule.ControlCommit(c, capsule.Format3) if err != nil { t.Fatal(err) } c.PayloadLength = 1 << 40 if b, _ := capsule.ControlCommit(c, capsule.Format3); b != a { t.Error("control_commit depends on L") } c.PayloadIdentity[0] = 8 if b, _ := capsule.ControlCommit(c, capsule.Format3); b == a { t.Error("control_commit does not depend on I_PAYLOAD") } if capsule.SigPart(nil)[0] != 0 || len(capsule.SigPart([]byte{1})) != 33 { t.Error("SIG_PART") } } // Spec v0.11 §29.7, §29.9: a signature of alg 1 gives F4, or F3 with a saved // key; F2 when it does not verify; F1 without the capsule around it, as in // v0.10, and for what this reader does not implement. func TestEvaluateSecurityIn(t *testing.T) { key, _ := authorkey.Generate() ctx := &capsule.SecurityContext{} ctx.ControlCommit[0], ctx.HeadDigest[0] = 1, 2 msg := capsule.AuthorMessage(ctx.ControlCommit, ctx.HeadDigest, capsule.SignersDigest(capsule.AlgEd25519, nil)) security := func(alg uint64, pub, sig []byte) []byte { content, err := capsule.EncodeAuthorSignature(alg, pub, sig) if err != nil { t.Fatal(err) } b, err := capsule.EncodeSecurityWith(content, nil) if err != nil { t.Fatal(err) } return b } signed := security(capsule.AlgEd25519, key.Public(), key.Sign(msg)) v := capsule.EvaluateSecurityIn(signed, ctx) if v.Signature != capsule.VerdictSignedOther || !bytes.Equal(v.AuthorKey[:], key.Public()) { t.Fatalf("verdicts %+v", v) } pub, _ := authorkey.PublicString(key.Public()) if line := v.Lines()[0]; !strings.Contains(line, pub) || !strings.HasSuffix(line, "No prueba quién la tiene.") { t.Errorf("F4 line %q", line) } ctx.AuthorKeys = map[string]string{pub: "Ana"} if v := capsule.EvaluateSecurityIn(signed, ctx); v.Signature != capsule.VerdictSignedSaved || v.Lines()[0] != "Firmado con la clave que guardaste como Ana." { t.Errorf("F3: %+v %q", v, v.Lines()) } other := *ctx other.HeadDigest[0] = 3 if v := capsule.EvaluateSecurityIn(signed, &other); v.Signature != capsule.VerdictSignatureInvalid { t.Errorf("another head: %s", v.Signature) } for name, b := range map[string][]byte{ "no context": signed, "a key of 31": security(capsule.AlgEd25519, key.Public()[:31], key.Sign(msg)), "alg 2, not yet": security(capsule.AlgCMS, []byte{1}, []byte{1}), "an unknown alg 9": security(9, key.Public(), key.Sign(msg)), } { c := ctx if name == "no context" { c = nil } if v := capsule.EvaluateSecurityIn(b, c); v.Signature != capsule.VerdictSignatureUnchecked { t.Errorf("%s: %s", name, v.Signature) } } }