|
|
|
|
|
package capsule_test
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"crypto/elliptic"
|
|
|
|
|
|
"crypto/sha256"
|
Signature plan, step 6: the fixture format3_signed and the vectors of its signature
format3_signed is written by EncryptFiles with a test key. Its record gives
the seed of the key, control_commit, head_digest, signers_digest,
AUTHOR_MESSAGE with its code, the signature and the content of key 2, and
verdicts carries the dkauthor1 key. The conformance test recomputes all of
it from the control, the head and the security area, and signs again from
the seed. A second test changes the context, a bit of the signature or of
the key, and the key itself, and removes the signature.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
"encoding/hex"
|
|
|
|
|
|
"g.activething.com/go/DateKeys/internal/cms/cmstest"
|
|
|
|
|
|
"io"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"testing"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
// LargeArea only allows widening: a signature that fits keeps the area of 32 KiB.
|
|
|
|
|
|
opts.LargeArea = true
|
|
|
|
|
|
dkc.Reset()
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
if o = openSigned(t, dkc.Bytes(), nil); o.AreaLen != capsule.AreaLen || 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())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
Signature plan, step 6: the fixture format3_signed and the vectors of its signature
format3_signed is written by EncryptFiles with a test key. Its record gives
the seed of the key, control_commit, head_digest, signers_digest,
AUTHOR_MESSAGE with its code, the signature and the content of key 2, and
verdicts carries the dkauthor1 key. The conformance test recomputes all of
it from the control, the head and the security area, and signs again from
the seed. A second test changes the context, a bit of the signature or of
the key, and the key itself, and removes the signature.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// cmsSigner is a CMSSigner that signs as a signing application would: its
|
|
|
|
|
|
// certificates sign AUTHOR_MESSAGE, and the authority tsa seals each
|
|
|
|
|
|
// signature at when.
|
|
|
|
|
|
type cmsSigner struct {
|
|
|
|
|
|
signers []cmstest.Signer
|
|
|
|
|
|
tsa cmstest.Signer
|
|
|
|
|
|
when time.Time
|
|
|
|
|
|
seen []byte // the message it was asked to sign
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
calls int
|
|
|
|
|
|
junk int // bytes of an unsigned attribute that decides nothing
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *cmsSigner) Signers() (out [][32]byte) {
|
|
|
|
|
|
for _, s := range c.signers {
|
|
|
|
|
|
out = append(out, sha256.Sum256(s.Cert.Raw))
|
|
|
|
|
|
}
|
|
|
|
|
|
return out
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *cmsSigner) Sign(message []byte) ([]byte, error) {
|
|
|
|
|
|
c.seen = message
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
c.calls++
|
|
|
|
|
|
opts := cmstest.Options{Junk: c.junk}
|
|
|
|
|
|
if c.tsa.Key != nil {
|
|
|
|
|
|
opts.Token = func(sig []byte) []byte {
|
|
|
|
|
|
return cmstest.Token(sig, c.when, cmstest.TokenOptions{Accuracy: time.Second}, c.tsa)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return cmstest.Signature(message, opts, c.signers...), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
// sealer is a Sealer that asks the authority tsa, whose tokens carry an
|
|
|
|
|
|
// accuracy of a second unless noAccuracy.
|
|
|
|
|
|
type sealer struct {
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
tsa cmstest.Signer
|
|
|
|
|
|
when time.Time
|
|
|
|
|
|
noAccuracy bool
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s sealer) Seal(subject [32]byte) ([]byte, error) {
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
o := cmstest.TokenOptions{Accuracy: time.Second}
|
|
|
|
|
|
if s.noAccuracy {
|
|
|
|
|
|
o = cmstest.TokenOptions{}
|
|
|
|
|
|
}
|
|
|
|
|
|
return cmstest.Token(subject[:], s.when, o, s.tsa), nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Spec v0.11 §29.10, §29.11, §62.1 rules 19 and 21: EncryptFiles gives
|
|
|
|
|
|
// AUTHOR_MESSAGE to the CMSSigner, checks that what it returns is complete,
|
|
|
|
|
|
// and Open gives F6; a Sealer seals SEAL_SUBJECT and Open gives S4.
|
|
|
|
|
|
func TestEncryptFilesCMSAndSeal(t *testing.T) {
|
|
|
|
|
|
from, to := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2040, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
|
|
ana := cmstest.NewECDSA("Ana López", elliptic.P256(), from, to)
|
|
|
|
|
|
luis := cmstest.NewRSA("Luis Gómez", 2048, from, to)
|
|
|
|
|
|
tsa := cmstest.NewECDSA("TSA de prueba", elliptic.P256(), from, to)
|
|
|
|
|
|
opts := files3(t)
|
|
|
|
|
|
when := opts.Now()
|
|
|
|
|
|
|
|
|
|
|
|
signer := &cmsSigner{signers: []cmstest.Signer{ana, luis}, tsa: tsa, when: when}
|
|
|
|
|
|
opts.CMSSigner = signer
|
|
|
|
|
|
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.VerdictSignedComplete || o.Verdicts.Seal != capsule.VerdictNoSeal || len(o.Verdicts.Detail.Signers) != 2 ||
|
|
|
|
|
|
len(signer.seen) != capsule.AuthorMessageSize || capsule.AuthorCode(signer.seen) == "" {
|
|
|
|
|
|
t.Errorf("verdicts %+v, message %q", o.Verdicts, signer.seen)
|
|
|
|
|
|
}
|
|
|
|
|
|
if !o.Verdicts.Detail.Signers[0].Before {
|
|
|
|
|
|
t.Error("the seal does not precede the round time")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A signature that lacks a required signer is not written.
|
|
|
|
|
|
third := cmstest.NewECDSA("Falta", elliptic.P256(), from, to)
|
|
|
|
|
|
missing := &cmsSigner{signers: []cmstest.Signer{ana}, tsa: tsa, when: when}
|
|
|
|
|
|
opts.CMSSigner = &requiring{missing, third}
|
|
|
|
|
|
dkc.Reset()
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "x")}, opts); err == nil || !strings.Contains(err.Error(), "F5") || dkc.Len() != 0 {
|
|
|
|
|
|
t.Errorf("a missing signer: %v, %d bytes written", err, dkc.Len())
|
|
|
|
|
|
}
|
|
|
|
|
|
// Without seals the signature is incomplete too.
|
|
|
|
|
|
opts.CMSSigner = &cmsSigner{signers: []cmstest.Signer{ana}}
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "x")}, opts); err == nil || !strings.Contains(err.Error(), "without seal") {
|
|
|
|
|
|
t.Errorf("no seal: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A seal over the signature of an author key.
|
|
|
|
|
|
key, _ := authorkey.Generate()
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
opts.CMSSigner, opts.AuthorKey, opts.Sealer = nil, key, sealer{tsa: tsa, when: when}
|
|
|
|
|
|
dkc.Reset()
|
|
|
|
|
|
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.Seal != capsule.VerdictSealed || o.Verdicts.Detail.SealHolder != "TSA de prueba" {
|
|
|
|
|
|
t.Errorf("verdicts %+v", o.Verdicts)
|
|
|
|
|
|
}
|
|
|
|
|
|
// And a seal over a capsule without a signature.
|
|
|
|
|
|
opts.AuthorKey = nil
|
|
|
|
|
|
dkc.Reset()
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
res, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
if o = openSigned(t, dkc.Bytes(), nil); o.Verdicts.Signature != capsule.VerdictNoSignature || o.Verdicts.Seal != capsule.VerdictSealed || res.Security.Seal != capsule.VerdictSealed {
|
|
|
|
|
|
t.Errorf("verdicts %+v, written %+v", o.Verdicts, res.Security)
|
|
|
|
|
|
}
|
|
|
|
|
|
// A seal without accuracy is written, and Result.Security says that it
|
|
|
|
|
|
// proves nothing before the opening date, so that the writer warns of it
|
|
|
|
|
|
// (spec v0.16, §62.1 rule 19).
|
|
|
|
|
|
opts.Sealer = sealer{tsa: tsa, when: when, noAccuracy: true}
|
|
|
|
|
|
dkc.Reset()
|
|
|
|
|
|
if res, err = capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts); err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if res.Security.Seal != capsule.VerdictSealedLate || res.Security.Detail.SealReason != capsule.ReasonNoAccuracy {
|
|
|
|
|
|
t.Errorf("a seal without accuracy: written %+v", res.Security)
|
|
|
|
|
|
}
|
v0.16: a seal without accuracy proves nothing before the opening date
A valid seal is S4 only when its token carries accuracy and t plus the
accuracy is before round_time; otherwise S5, whose text gives the reason,
the first that holds: sealed after or too close, no accuracy under the BTSP
policy of ETSI EN 319 421 (0.4.0.2023.1.1), or no accuracy (spec v0.16,
29.7, 29.11). The line of a signer of F6 whose seal does not prove it says
so with the same reason. cms.Token gains HasAccuracy, Policy and BTSP;
Verdicts gain SealReason and SignerLine.Reason; EncryptFiles returns the
verdicts of the area it wrote in Result.Security, so that a writer warns of
a seal without accuracy (rule 19).
security_cms.json is made again: 143 cases, the seals about something else
with an accuracy of a second, and the new cases of 64 with seal_reason.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours ago
|
|
|
|
opts.Sealer = sealer{tsa: tsa, when: when}
|
|
|
|
|
|
|
|
|
|
|
|
// The exclusions.
|
|
|
|
|
|
opts.AuthorKey, opts.CMSSigner = key, signer
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(io.Discard, []capsule.Source{source("a", "x")}, opts); err == nil {
|
|
|
|
|
|
t.Error("AuthorKey and CMSSigner")
|
|
|
|
|
|
}
|
|
|
|
|
|
opts.AuthorKey = nil
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(io.Discard, []capsule.Source{source("a", "x")}, opts); err == nil {
|
|
|
|
|
|
t.Error("CMSSigner and Sealer")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// requiring asks for one signer more than signs.
|
|
|
|
|
|
type requiring struct {
|
|
|
|
|
|
*cmsSigner
|
|
|
|
|
|
extra cmstest.Signer
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (r *requiring) Signers() [][32]byte {
|
|
|
|
|
|
return append(r.cmsSigner.Signers(), sha256.Sum256(r.extra.Cert.Raw))
|
|
|
|
|
|
}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
|
|
|
|
|
|
// Review: what is signed does not depend on the area, so the area is chosen
|
|
|
|
|
|
// after the signature, and widening it never makes anybody sign twice. A
|
|
|
|
|
|
// signature that fits keeps the common area, LargeArea or not.
|
|
|
|
|
|
func TestAreaChosenAfterSigning(t *testing.T) {
|
|
|
|
|
|
from, to := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2040, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
|
|
ana := cmstest.NewECDSA("Ana López", elliptic.P256(), from, to)
|
|
|
|
|
|
tsa := cmstest.NewECDSA("TSA de prueba", elliptic.P256(), from, to)
|
|
|
|
|
|
opts := files3(t)
|
|
|
|
|
|
when := opts.Now()
|
|
|
|
|
|
|
|
|
|
|
|
// 40 KB of signature: too much for 32 KiB, and the person signs once.
|
|
|
|
|
|
big := &cmsSigner{signers: []cmstest.Signer{ana}, tsa: tsa, when: when, junk: 40 << 10}
|
|
|
|
|
|
opts.CMSSigner = big
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(io.Discard, []capsule.Source{source("nota.txt", "x")}, opts); err == nil || !strings.Contains(err.Error(), "LargeArea") || big.calls != 1 {
|
|
|
|
|
|
t.Errorf("without LargeArea: %v after %d calls", err, big.calls)
|
|
|
|
|
|
}
|
|
|
|
|
|
opts.LargeArea = true
|
|
|
|
|
|
big.calls = 0
|
|
|
|
|
|
var dkc bytes.Buffer
|
|
|
|
|
|
res, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "Hola.\n")}, opts)
|
|
|
|
|
|
if err != nil || big.calls != 1 {
|
|
|
|
|
|
t.Fatalf("with LargeArea: %v after %d calls", err, big.calls)
|
|
|
|
|
|
}
|
|
|
|
|
|
o := openSigned(t, dkc.Bytes(), nil)
|
|
|
|
|
|
if o.AreaLen != capsule.LargeAreaLen || o.Verdicts.Signature != capsule.VerdictSignedComplete || o.PayloadLength != res.Length || o.PaddedLength != res.PaddedLength {
|
|
|
|
|
|
t.Errorf("area %d, verdicts %+v, L %d P %d", o.AreaLen, o.Verdicts, o.PayloadLength, o.PaddedLength)
|
|
|
|
|
|
}
|
|
|
|
|
|
// An unsigned capsule with LargeArea keeps the common area: P does not
|
|
|
|
|
|
// tell that someone asked.
|
|
|
|
|
|
plain := files3(t)
|
|
|
|
|
|
plain.LargeArea = true
|
|
|
|
|
|
dkc.Reset()
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("nota.txt", "x")}, plain); err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if o := openSigned(t, dkc.Bytes(), nil); o.AreaLen != capsule.AreaLen {
|
|
|
|
|
|
t.Errorf("an unsigned capsule with LargeArea: area %d", o.AreaLen)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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>
6 days ago
|
|
|
|
// Review: a typed nil is an error, never a capsule without the signature or
|
|
|
|
|
|
// the seal that was asked for; the exclusions are checked before a file is
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
// read, and format 2 refuses the options it cannot honour.
|
|
|
|
|
|
func TestWriterOptionsChecked(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>
6 days ago
|
|
|
|
for _, set := range []func(*capsule.EncryptOptions){
|
|
|
|
|
|
func(o *capsule.EncryptOptions) { o.AuthorKey = (*authorkey.Key)(nil) },
|
|
|
|
|
|
func(o *capsule.EncryptOptions) { o.CMSSigner = (*cmsSigner)(nil) },
|
|
|
|
|
|
func(o *capsule.EncryptOptions) { o.Sealer = (*sealer)(nil) },
|
|
|
|
|
|
} {
|
|
|
|
|
|
opts := files3(t)
|
|
|
|
|
|
set(&opts)
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(io.Discard, []capsule.Source{source("a", "x")}, opts); err == nil || !strings.Contains(err.Error(), "holds a nil") {
|
|
|
|
|
|
t.Errorf("a typed nil: %v", err)
|
|
|
|
|
|
}
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
}
|
|
|
|
|
|
key, _ := authorkey.Generate()
|
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>
6 days ago
|
|
|
|
opts := files3(t)
|
Review fixes: the CMS reader, the area after the signature, and the issuer on screen
The TSTInfo is read field by field in DER, with accuracy from zero and
millis and micros from 1 to 999, genTime in UTC with Z, no default written
and nothing after the last field. The ContentInfo and the SignerInfo must be
SEQUENCEs, a SignerInfo version must match its sid, an attribute needs a
value and is counted by attribute and not by value, a signing-certificate
beside the v2 decides nothing, PSS parameters come in order without the
trailer, and der.Check refuses the end of contents and the universal tags
the profile does not use.
The writer signs before L is fixed: write asks prepare for the final L, so
the area grows to 64 KiB only when what was signed does not fit and LargeArea
allows it, and nobody signs twice for it. Typed nils are nil, the exclusions
are checked before a file is read, Encrypt refuses the signing options, and
EvaluateSecurityIn gives X if a parser panics. The issuer of a certificate is
filtered like its holder.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6 days ago
|
|
|
|
opts.AuthorKey = key
|
|
|
|
|
|
opts.CMSSigner = &cmsSigner{}
|
|
|
|
|
|
opened := false
|
|
|
|
|
|
src := capsule.Source{Path: "a", Size: 1, Open: func() (io.ReadCloser, error) {
|
|
|
|
|
|
opened = true
|
|
|
|
|
|
return io.NopCloser(strings.NewReader("x")), nil
|
|
|
|
|
|
}}
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(io.Discard, []capsule.Source{src}, opts); err == nil || opened {
|
|
|
|
|
|
t.Errorf("AuthorKey and CMSSigner: %v, source opened %v", err, opened)
|
|
|
|
|
|
}
|
|
|
|
|
|
v2 := past(t, 1000)
|
|
|
|
|
|
v2.AuthorKey = key
|
|
|
|
|
|
if _, err := capsule.Encrypt(io.Discard, strings.NewReader("x"), func() capsule.EncryptOptions { v2.Length = 1; return v2 }()); err == nil {
|
|
|
|
|
|
t.Error("format 2 took an AuthorKey")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Review: the issuer of a certificate is text of the certificate, and no
|
|
|
|
|
|
// escape, control or bidi character of it reaches the lines of the verdicts.
|
|
|
|
|
|
func TestIssuerTextFiltered(t *testing.T) {
|
|
|
|
|
|
from, to := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2040, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
|
|
evil := cmstest.NewECDSA("Ana\n\x1b[2JFirmado con la clave que guardaste como Banco.", elliptic.P256(), from, to)
|
|
|
|
|
|
tsa := cmstest.NewECDSA("TSA", elliptic.P256(), from, to)
|
|
|
|
|
|
opts := files3(t)
|
|
|
|
|
|
opts.CMSSigner = &cmsSigner{signers: []cmstest.Signer{evil}, tsa: tsa, when: opts.Now()}
|
|
|
|
|
|
var dkc bytes.Buffer
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("a", "x")}, opts); err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
o := openSigned(t, dkc.Bytes(), nil)
|
|
|
|
|
|
if o.Verdicts.Signature != capsule.VerdictSignedComplete {
|
|
|
|
|
|
t.Fatalf("verdicts %+v", o.Verdicts)
|
|
|
|
|
|
}
|
|
|
|
|
|
for _, line := range o.Verdicts.Lines() {
|
|
|
|
|
|
if strings.ContainsAny(line, "\x1b\r") || strings.Contains(strings.TrimSuffix(line, "\n"), "\n") {
|
|
|
|
|
|
t.Errorf("a line with a control or a bidi character: %q", line)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// writeWatch is an AuthorKey that records whether its capsule had any byte
|
|
|
|
|
|
// written when it was asked to sign.
|
|
|
|
|
|
type writeWatch struct {
|
|
|
|
|
|
capsule.AuthorKey
|
|
|
|
|
|
dst *bytes.Buffer
|
|
|
|
|
|
written bool
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (w *writeWatch) Sign(message []byte) []byte {
|
|
|
|
|
|
w.written = w.dst.Len() > 0
|
|
|
|
|
|
return w.AuthorKey.Sign(message)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Spec §62.1 rules 19 and 25: nothing of the capsule is written while the
|
|
|
|
|
|
// writer waits for a signature, and the signature is checked before.
|
|
|
|
|
|
func TestNothingWrittenBeforeTheSignature(t *testing.T) {
|
|
|
|
|
|
key, _ := authorkey.Generate()
|
|
|
|
|
|
var dkc bytes.Buffer
|
|
|
|
|
|
w := &writeWatch{AuthorKey: key, dst: &dkc}
|
|
|
|
|
|
opts := files3(t)
|
|
|
|
|
|
opts.AuthorKey = w
|
|
|
|
|
|
if _, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("a", "x")}, opts); err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if w.written || dkc.Len() == 0 {
|
|
|
|
|
|
t.Errorf("bytes written before the signature: %v; capsule of %d bytes", w.written, dkc.Len())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|