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>
returnnil,fmt.Errorf("capsule: self-check: the reader finds the verdicts %s and %s in this security area, not %s and %s%s",v.Signature,v.Seal,wantSig,wantSeal,detailText(v.Detail))
!strings.HasSuffix(late.Lines()[1],", sin acreditar que fuera antes de la fecha de apertura: se selló después de esa fecha o demasiado cerca de ella.")||
// Spec v0.16, §29.7 and §29.11: a valid seal proves that it came before
// the round time only with accuracy; without it, S5 and its reason, the
// first that holds.
forname,tc:=rangemap[string]struct{
ocmstest.TokenOptions
whentime.Time
sealcapsule.Verdict
reasoncapsule.SealReason
}{
"no accuracy, years before":{cmstest.TokenOptions{},signedAt,capsule.VerdictSealedLate,capsule.ReasonNoAccuracy},
"no accuracy under BTSP":{cmstest.TokenOptions{Policy:cmstest.BTSPPolicy},signedAt,capsule.VerdictSealedLate,capsule.ReasonNoAccuracyBTSP},
"no accuracy, after the round time":{cmstest.TokenOptions{Policy:cmstest.BTSPPolicy},roundTime,capsule.VerdictSealedLate,capsule.ReasonLate},
"an accuracy of 0 seconds":{cmstest.TokenOptions{AccuracyRaw:cmstest.Seq(cmstest.Int(0))},roundTime.Add(-time.Microsecond),capsule.VerdictSealed,capsule.ReasonNone},
"BTSP with accuracy":{cmstest.TokenOptions{Policy:cmstest.BTSPPolicy,Accuracy:time.Second},signedAt,capsule.VerdictSealed,capsule.ReasonNone},
"an accuracy of 0 at the round time":{cmstest.TokenOptions{AccuracyRaw:cmstest.Seq(cmstest.Int(0))},roundTime,capsule.VerdictSealedLate,capsule.ReasonLate},
lines=append(lines,"Según un sello a nombre de "+q(c.sealTSA.holder)+", existía el "+at(c.sealTime)+", antes de que la cápsula pudiera abrirse. DateKeys no comprueba quién emitió el sello.")
g.add(vcase{name:"alg 2: t plus the accuracy of the seal equals the round time: F6, not before the opening date",area:g.area(g.signed(only(ana),edge,ana),nil),
g.add(vcase{name:"alg 2: a seal of the BTSP policy without accuracy: F6, not proven before the opening date, by its policy",area:g.area(g.signed(only(ana),btsp,ana),nil),
g.add(vcase{name:"alg 2: a key 3 of seal_type 4294967295 beside it: F5 and S1",area:g.area(key2,g.must(capsule.EncodeSeal(capsule.SealTypeTest,[]byte{1}))),
g.add(vcase{name:"alg 2: a valid seal of seal_type 2 in key 3 beside it: F5 and S4",area:g.area(key2,g.must(capsule.EncodeSeal(capsule.SealTypeRFC3161,cmstest.Token(subject[:],vecSigned,cmstest.TokenOptions{},tsa.Signer)))),
g.add(vcase{name:"alg 2: a valid seal of seal_type 2 in key 3 beside it: F5 and S4",area:g.area(key2,g.must(capsule.EncodeSeal(capsule.SealTypeRFC3161,cmstest.Token(subject[:],vecSigned,cmstest.TokenOptions{Accuracy:time.Second},tsa.Signer)))),
sealedAt("seal: before the round time: S4",tok(vecSigned,cmstest.TokenOptions{},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: after the round time: S5",tok(vecRound.Add(time.Minute),cmstest.TokenOptions{},tsa),tsa,vecRound.Add(time.Minute),capsule.VerdictSealedLate)
sealedAt("seal: before the round time: S4",tok(vecSigned,second,tsa),tsa,vecSigned,capsule.VerdictSealed)
late("seal: after the round time, without accuracy: S5, sealed after",tok(vecRound.Add(time.Minute),cmstest.TokenOptions{},tsa),tsa,vecRound.Add(time.Minute),capsule.ReasonLate)
early:=vecRound.Add(-time.Second)
sealedAt("seal: t plus the accuracy past the round time: S5",tok(early,cmstest.TokenOptions{Accuracy:2*time.Second},tsa),tsa,early,capsule.VerdictSealedLate)
sealedAt("seal: t plus the accuracy equal to the round time: S5",tok(early,cmstest.TokenOptions{Accuracy:time.Second},tsa),tsa,early,capsule.VerdictSealedLate)
late("seal: t plus the accuracy past the round time: S5",tok(early,cmstest.TokenOptions{Accuracy:2*time.Second},tsa),tsa,early,capsule.ReasonLate)
late("seal: t plus the accuracy equal to the round time: S5",tok(early,cmstest.TokenOptions{Accuracy:time.Second},tsa),tsa,early,capsule.ReasonLate)
late("seal: without accuracy, years before the round time: S5, it does not say its precision",tok(vecSigned,cmstest.TokenOptions{},tsa),tsa,vecSigned,capsule.ReasonNoAccuracy)
late("seal: of the BTSP policy of ETSI, without accuracy: S5, it does not say the precision its policy requires",tok(vecSigned,cmstest.TokenOptions{Policy:cmstest.BTSPPolicy},tsa),tsa,vecSigned,capsule.ReasonNoAccuracyBTSP)
late("seal: of the BTSP policy, without accuracy, after the round time: S5, sealed after",tok(vecRound,cmstest.TokenOptions{Policy:cmstest.BTSPPolicy},tsa),tsa,vecRound,capsule.ReasonLate)
sealedAt("seal: of the BTSP policy, with accuracy: S4",tok(vecSigned,cmstest.TokenOptions{Policy:cmstest.BTSPPolicy,Accuracy:time.Second},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: an accuracy of 0 seconds, a microsecond before the round time: S4",tok(vecRound.Add(-time.Microsecond),cmstest.TokenOptions{AccuracyRaw:cmstest.Seq(cmstest.Int(0))},tsa),tsa,vecRound.Add(-time.Microsecond),capsule.VerdictSealed)
sealedAt("seal: an empty accuracy, a precision of 0: S4",tok(vecSigned,cmstest.TokenOptions{AccuracyRaw:cmstest.Seq()},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: t plus an accuracy of 999 ms and 999 µs, a microsecond before the round time: S4",
sealedAt("seal: an accuracy of seconds, millis and micros: S4",tok(vecSigned,cmstest.TokenOptions{Accuracy:time.Second+5*time.Millisecond+7*time.Microsecond},tsa),tsa,vecSigned,capsule.VerdictSealed)
fraction:=vecSigned.Add(250*time.Millisecond)
sealedAt("seal: a genTime with a fraction of a second: S4, t with its fraction",tok(fraction,cmstest.TokenOptions{},tsa),tsa,fraction,capsule.VerdictSealed)
sealedAt("seal: the certificate of the authority twice: S4",tok(vecSigned,cmstest.TokenOptions{TSATwice:true},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: a CRL in the token decides nothing: S4",tok(vecSigned,cmstest.TokenOptions{CRL:cmstest.Seq(cmstest.Seq(cmstest.Int(1)),cmstest.Seq(cmstest.OID(cmstest.OIDECDSA256)),cmstest.BitString([]byte{0}))},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: signing-certificate-v2 in the token: S4",tok(vecSigned,cmstest.TokenOptions{SigCertV2:true},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: a genTime with a fraction of a second: S4, t with its fraction",tok(fraction,second,tsa),tsa,fraction,capsule.VerdictSealed)
sealedAt("seal: the certificate of the authority twice: S4",tok(vecSigned,cmstest.TokenOptions{TSATwice:true, Accuracy:time.Second},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: a CRL in the token decides nothing: S4",tok(vecSigned,cmstest.TokenOptions{CRL:cmstest.Seq(cmstest.Seq(cmstest.Int(1)),cmstest.Seq(cmstest.OID(cmstest.OIDECDSA256)),cmstest.BitString([]byte{0})), Accuracy:time.Second},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: signing-certificate-v2 in the token: S4",tok(vecSigned,cmstest.TokenOptions{SigCertV2:true, Accuracy:time.Second},tsa),tsa,vecSigned,capsule.VerdictSealed)
tsaName:=cmstest.TLV(0xa0,cmstest.TLV(0xa4,cmstest.Name(cmstest.ATV(cmstest.OIDCommonName,cmstest.UTF8("Autoridad de Sellado de prueba")))))
sealedAt("seal: ordering TRUE, a nonce, a tsa and extensions: S4",tok(vecSigned,cmstest.TokenOptions{After:[][]byte{cmstest.Bool(true),cmstest.Int(99),tsaName,exts}},tsa),tsa,vecSigned,capsule.VerdictSealed)
sealedAt("seal: ordering TRUE, a nonce, a tsa and extensions: S4",tok(vecSigned,cmstest.TokenOptions{After:[][]byte{cmstest.Bool(true),cmstest.Int(99),tsaName,exts}, Accuracy:time.Second},tsa),tsa,vecSigned,capsule.VerdictSealed)
rsaTSA:=named(cmstest.NewRSA("Autoridad RSA de prueba",2048,certFrom,certTo),"Autoridad RSA de prueba")
sealedAt("seal: an authority of RSA with RSASSA-PSS and SHA-512: S4",tok(vecSigned,cmstest.TokenOptions{CMS:cmstest.Options{PSS:true,Hash:crypto.SHA512}},rsaTSA),rsaTSA,vecSigned,capsule.VerdictSealed)
sealedAt("seal: an authority of RSA with RSASSA-PSS and SHA-512: S4",tok(vecSigned,cmstest.TokenOptions{CMS:cmstest.Options{PSS:true,Hash:crypto.SHA512}, Accuracy:time.Second},rsaTSA),rsaTSA,vecSigned,capsule.VerdictSealed)
// The case of §76, change 1: a name that lines up a text of its own.
spaced:=cmstest.NewECDSA("TSA"+strings.Repeat(" ",50)+"Firmado con la clave que guardaste como Banco",elliptic.P256(),certFrom,certTo)
vspaced:=vsigner{spaced,hashOfCert(spaced),""}
sealedAt("seal: an authority named with 50 spaces and the text of F3: S4, by its SHA-256",tok(vecSigned,cmstest.TokenOptions{},vspaced),vspaced,vecSigned,capsule.VerdictSealed)
sealedAt("seal: an authority named with 50 spaces and the text of F3: S4, by its SHA-256",tok(vecSigned,second,vspaced),vspaced,vecSigned,capsule.VerdictSealed)
// S3: it reads, and does not verify (§29.11, step 3).
seal("seal: over another subject: S3",cmstest.Token([]byte("other"),vecSigned,cmstest.TokenOptions{},tsa.Signer),capsule.VerdictSealInvalid)
g.add(vcase{name:"seal: over a capsule without a signature: F0 and S4",area:g.area(nil,g.must(capsule.EncodeSeal(capsule.SealTypeRFC3161,cmstest.Token(noSig[:],vecSigned,cmstest.TokenOptions{},tsa.Signer)))),
g.add(vcase{name:"seal: over a capsule without a signature: F0 and S4",area:g.area(nil,g.must(capsule.EncodeSeal(capsule.SealTypeRFC3161,cmstest.Token(noSig[:],vecSigned,second,tsa.Signer)))),
g.add(vcase{name:"seal: beside a signature of alg 4294967295, which it seals all the same: F1 and S4",area:g.area(unknown,g.must(capsule.EncodeSeal(capsule.SealTypeRFC3161,cmstest.Token(beside[:],vecSigned,cmstest.TokenOptions{},tsa.Signer)))),
g.add(vcase{name:"seal: beside a signature of alg 4294967295, which it seals all the same: F1 and S4",area:g.area(unknown,g.must(capsule.EncodeSeal(capsule.SealTypeRFC3161,cmstest.Token(beside[:],vecSigned,second,tsa.Signer)))),