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
package cms_test
import (
"bytes"
"crypto/elliptic"
"crypto/sha256"
"encoding/asn1"
"errors"
"testing"
"time"
"g.activething.com/go/DateKeys/internal/cms"
"g.activething.com/go/DateKeys/internal/cms/cmstest"
"g.activething.com/go/DateKeys/internal/der"
)
var oidSHA256 = asn1 . ObjectIdentifier { 2 , 16 , 840 , 1 , 101 , 3 , 4 , 2 , 1 }
// tstInfo builds a TSTInfo of RFC 3161 over subject, with the fields after
// genTime that the test gives.
func tstInfo ( subject [ ] byte , genTime [ ] byte , after ... [ ] byte ) [ ] byte {
Tests of the CMS reader: certificates field by field, every check, fuzzing
The builder of the tests, cmstest:
- NewCert writes a certificate from the DER of its tbsCertificate, field by
field: names of any string type with any bytes (UTF8String,
PrintableString with an underscore or an at sign, IA5String,
TeletexString, BMPString of odd length or with a surrogate,
VisibleString, NumericString), an attribute twice or none, no version,
times with a fraction, an extension twice, a compressed EC key, an even
modulus, and any signature. A Signer made so serves Signature and Token.
- Options for the version of a SignerInfo, the hashAlgorithm and the
certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of
the key, a certificate twice, two content-type attributes, an attribute
with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order,
two signature-time-stamp attributes, and edits of the SignedData and of
each SignerInfo.
- Token options for any accuracy, a genTime of free text, ordering FALSE,
a field after the last, an imprint of any length, no message-digest, a
CRL in crls and the certificate of the authority twice.
- Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo
removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature)
and Indefinite.
The tests of internal/cms and internal/der fail for each check of cms.go,
cert.go, verify.go and der.go. A mutation run, which replaces each leaf of
each condition by false and by true, one at a time, kills every mutant
that is not equivalent to the code it mutates.
FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded
with security_cms.json and with what cmstest builds: no panic, what Check
accepts Split reads, and the parsers fail only with ErrForm or
ErrAlgorithm.
capsule: the case of a seal outside the validity of the certificate gave
an invalid seal; it now tests a certificate that expired before a valid
seal (out of validity) apart from an authority that was not valid at its
time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes
or with a hash twice are F1 beside a CMS signature that is valid for the
AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as
spec v0.12 §29.7 says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
return cmstest . Seq ( append ( tstFields ( subject , genTime ) , after ... ) ... )
}
// tstFields are the five required fields of a TSTInfo.
func tstFields ( subject [ ] byte , genTime [ ] byte ) [ ] [ ] byte {
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
h := sha256 . Sum256 ( subject )
Tests of the CMS reader: certificates field by field, every check, fuzzing
The builder of the tests, cmstest:
- NewCert writes a certificate from the DER of its tbsCertificate, field by
field: names of any string type with any bytes (UTF8String,
PrintableString with an underscore or an at sign, IA5String,
TeletexString, BMPString of odd length or with a surrogate,
VisibleString, NumericString), an attribute twice or none, no version,
times with a fraction, an extension twice, a compressed EC key, an even
modulus, and any signature. A Signer made so serves Signature and Token.
- Options for the version of a SignerInfo, the hashAlgorithm and the
certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of
the key, a certificate twice, two content-type attributes, an attribute
with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order,
two signature-time-stamp attributes, and edits of the SignedData and of
each SignerInfo.
- Token options for any accuracy, a genTime of free text, ordering FALSE,
a field after the last, an imprint of any length, no message-digest, a
CRL in crls and the certificate of the authority twice.
- Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo
removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature)
and Indefinite.
The tests of internal/cms and internal/der fail for each check of cms.go,
cert.go, verify.go and der.go. A mutation run, which replaces each leaf of
each condition by false and by true, one at a time, kills every mutant
that is not equivalent to the code it mutates.
FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded
with security_cms.json and with what cmstest builds: no panic, what Check
accepts Split reads, and the parsers fail only with ErrForm or
ErrAlgorithm.
capsule: the case of a seal outside the validity of the certificate gave
an invalid seal; it now tests a certificate that expired before a valid
seal (out of validity) apart from an authority that was not valid at its
time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes
or with a hash twice are F1 beside a CMS signature that is valid for the
AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as
spec v0.12 §29.7 says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
return [ ] [ ] byte {
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
cmstest . Int ( 1 ) , cmstest . OID ( asn1 . ObjectIdentifier { 1 , 2 , 3 , 4 } ) ,
cmstest . Seq ( cmstest . Seq ( cmstest . OID ( oidSHA256 ) ) , cmstest . Octets ( h [ : ] ) ) ,
cmstest . Int ( 42 ) , genTime ,
}
}
func parses ( t * testing . T , tsa cmstest . Signer , info [ ] byte , subject [ ] byte ) ( * cms . Token , error ) {
t . Helper ( )
tok , err := cms . ParseToken ( cmstest . TokenRaw ( info , tsa ) )
if err == nil && ! tok . Check ( subject ) {
t . Fatal ( "a token that parses does not verify" )
}
return tok , err
}
// Review of the CMS reader: the TSTInfo is an OCTET STRING, so it is checked
// field by field, and what DER forbids, or what would make a seal after the
// opening date look before it, is a form error (spec §29.11, S2).
func TestTSTInfoStrict ( t * testing . T ) {
tsa := cmstest . NewECDSA ( "TSA" , elliptic . P256 ( ) , from , to )
subject := [ ] byte ( "seal subject" )
good := cmstest . GeneralizedTime ( "20260930120000Z" )
Tests of the CMS reader: certificates field by field, every check, fuzzing
The builder of the tests, cmstest:
- NewCert writes a certificate from the DER of its tbsCertificate, field by
field: names of any string type with any bytes (UTF8String,
PrintableString with an underscore or an at sign, IA5String,
TeletexString, BMPString of odd length or with a surrogate,
VisibleString, NumericString), an attribute twice or none, no version,
times with a fraction, an extension twice, a compressed EC key, an even
modulus, and any signature. A Signer made so serves Signature and Token.
- Options for the version of a SignerInfo, the hashAlgorithm and the
certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of
the key, a certificate twice, two content-type attributes, an attribute
with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order,
two signature-time-stamp attributes, and edits of the SignedData and of
each SignerInfo.
- Token options for any accuracy, a genTime of free text, ordering FALSE,
a field after the last, an imprint of any length, no message-digest, a
CRL in crls and the certificate of the authority twice.
- Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo
removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature)
and Indefinite.
The tests of internal/cms and internal/der fail for each check of cms.go,
cert.go, verify.go and der.go. A mutation run, which replaces each leaf of
each condition by false and by true, one at a time, kills every mutant
that is not equivalent to the code it mutates.
FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded
with security_cms.json and with what cmstest builds: no panic, what Check
accepts Split reads, and the parsers fail only with ErrForm or
ErrAlgorithm.
capsule: the case of a seal outside the validity of the certificate gave
an invalid seal; it now tests a certificate that expired before a valid
seal (out of validity) apart from an authority that was not valid at its
time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes
or with a hash twice are F1 beside a CMS signature that is valid for the
AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as
spec v0.12 §29.7 says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
if tok , err := parses ( t , tsa , tstInfo ( subject , good ) , subject ) ; err != nil || ! tok . GenTime . Equal ( now ) || tok . Accuracy != 0 {
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
t . Fatalf ( "the baseline: %v" , err )
}
if tok , err := parses ( t , tsa , tstInfo ( subject , cmstest . GeneralizedTime ( "20260930120000.5Z" ) , cmstest . Seq ( cmstest . Int ( 2 ) , cmstest . TLV ( 0x80 , [ ] byte { 5 } ) ) ) , subject ) ; err != nil ||
tok . GenTime . Sub ( now ) != 500 * time . Millisecond || tok . Accuracy != 2 * time . Second + 5 * time . Millisecond {
t . Fatalf ( "a fraction and an accuracy: %v" , err )
}
Tests of the CMS reader: certificates field by field, every check, fuzzing
The builder of the tests, cmstest:
- NewCert writes a certificate from the DER of its tbsCertificate, field by
field: names of any string type with any bytes (UTF8String,
PrintableString with an underscore or an at sign, IA5String,
TeletexString, BMPString of odd length or with a surrogate,
VisibleString, NumericString), an attribute twice or none, no version,
times with a fraction, an extension twice, a compressed EC key, an even
modulus, and any signature. A Signer made so serves Signature and Token.
- Options for the version of a SignerInfo, the hashAlgorithm and the
certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of
the key, a certificate twice, two content-type attributes, an attribute
with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order,
two signature-time-stamp attributes, and edits of the SignedData and of
each SignerInfo.
- Token options for any accuracy, a genTime of free text, ordering FALSE,
a field after the last, an imprint of any length, no message-digest, a
CRL in crls and the certificate of the authority twice.
- Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo
removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature)
and Indefinite.
The tests of internal/cms and internal/der fail for each check of cms.go,
cert.go, verify.go and der.go. A mutation run, which replaces each leaf of
each condition by false and by true, one at a time, kills every mutant
that is not equivalent to the code it mutates.
FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded
with security_cms.json and with what cmstest builds: no panic, what Check
accepts Split reads, and the parsers fail only with ErrForm or
ErrAlgorithm.
capsule: the case of a seal outside the validity of the certificate gave
an invalid seal; it now tests a certificate that expired before a valid
seal (out of validity) apart from an authority that was not valid at its
time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes
or with a hash twice are F1 beside a CMS signature that is valid for the
AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as
spec v0.12 §29.7 says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
// The optional fields, in their order, each once.
tsaName := cmstest . TLV ( 0xa0 , cmstest . TLV ( 0xa4 , cmstest . Seq ( ) ) )
exts := cmstest . TLV ( 0xa1 , cmstest . Seq ( cmstest . OID ( asn1 . ObjectIdentifier { 1 , 2 , 3 } ) , cmstest . Octets ( nil ) ) )
ms := func ( c ... byte ) [ ] byte { return cmstest . TLV ( 0x80 , c ) }
us := func ( c ... byte ) [ ] byte { return cmstest . TLV ( 0x81 , c ) }
for name , tc := range map [ string ] struct {
after [ ] [ ] byte
accuracy time . Duration
} {
"an empty accuracy" : { [ ] [ ] byte { cmstest . Seq ( ) } , 0 } ,
"only millis" : { [ ] [ ] byte { cmstest . Seq ( ms ( 5 ) ) } , 5 * time . Millisecond } ,
"only micros" : { [ ] [ ] byte { cmstest . Seq ( us ( 7 ) ) } , 7 * time . Microsecond } ,
"millis and micros of 999" : { [ ] [ ] byte { cmstest . Seq ( ms ( 0x03 , 0xe7 ) , us ( 0x03 , 0xe7 ) ) } , 999 * time . Millisecond + 999 * time . Microsecond } ,
"millis of 128" : { [ ] [ ] byte { cmstest . Seq ( ms ( 0 , 0x80 ) ) } , 128 * time . Millisecond } ,
"millis of 300" : { [ ] [ ] byte { cmstest . Seq ( ms ( 0x01 , 0x2c ) ) } , 300 * time . Millisecond } ,
Tests of the CMS reader: certificates field by field, every check, fuzzing
The builder of the tests, cmstest:
- NewCert writes a certificate from the DER of its tbsCertificate, field by
field: names of any string type with any bytes (UTF8String,
PrintableString with an underscore or an at sign, IA5String,
TeletexString, BMPString of odd length or with a surrogate,
VisibleString, NumericString), an attribute twice or none, no version,
times with a fraction, an extension twice, a compressed EC key, an even
modulus, and any signature. A Signer made so serves Signature and Token.
- Options for the version of a SignerInfo, the hashAlgorithm and the
certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of
the key, a certificate twice, two content-type attributes, an attribute
with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order,
two signature-time-stamp attributes, and edits of the SignedData and of
each SignerInfo.
- Token options for any accuracy, a genTime of free text, ordering FALSE,
a field after the last, an imprint of any length, no message-digest, a
CRL in crls and the certificate of the authority twice.
- Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo
removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature)
and Indefinite.
The tests of internal/cms and internal/der fail for each check of cms.go,
cert.go, verify.go and der.go. A mutation run, which replaces each leaf of
each condition by false and by true, one at a time, kills every mutant
that is not equivalent to the code it mutates.
FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded
with security_cms.json and with what cmstest builds: no panic, what Check
accepts Split reads, and the parsers fail only with ErrForm or
ErrAlgorithm.
capsule: the case of a seal outside the validity of the certificate gave
an invalid seal; it now tests a certificate that expired before a valid
seal (out of validity) apart from an authority that was not valid at its
time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes
or with a hash twice are F1 beside a CMS signature that is valid for the
AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as
spec v0.12 §29.7 says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
"seconds, millis and micros" : { [ ] [ ] byte { cmstest . Seq ( cmstest . Int ( 1 ) , ms ( 5 ) , us ( 7 ) ) } , time . Second + 5 * time . Millisecond + 7 * time . Microsecond } ,
"2^31 - 1 seconds" : { [ ] [ ] byte { cmstest . Seq ( cmstest . Int ( 1 << 31 - 1 ) ) } , ( 1 << 31 - 1 ) * time . Second } ,
"ordering TRUE" : { [ ] [ ] byte { cmstest . Bool ( true ) } , 0 } ,
"an accuracy and ordering TRUE" : { [ ] [ ] byte { cmstest . Seq ( cmstest . Int ( 1 ) ) , cmstest . Bool ( true ) } , time . Second } ,
"a nonce" : { [ ] [ ] byte { cmstest . Int ( 99 ) } , 0 } ,
"ordering TRUE and a nonce" : { [ ] [ ] byte { cmstest . Bool ( true ) , cmstest . Int ( 99 ) } , 0 } ,
"a tsa" : { [ ] [ ] byte { tsaName } , 0 } ,
"extensions" : { [ ] [ ] byte { exts } , 0 } ,
"every field" : { [ ] [ ] byte { cmstest . Seq ( cmstest . Int ( 3 ) ) , cmstest . Bool ( true ) , cmstest . Int ( 99 ) , tsaName , exts } , 3 * time . Second } ,
} {
tok , err := parses ( t , tsa , tstInfo ( subject , good , tc . after ... ) , subject )
if err != nil || tok . Accuracy != tc . accuracy {
t . Errorf ( "%s: %v" , name , err )
}
}
fields := tstFields ( subject , good )
with := func ( i int , v [ ] byte ) [ ] byte {
f := append ( [ ] [ ] byte ( nil ) , fields ... )
f [ i ] = v
return cmstest . Seq ( f ... )
}
h := sha256 . Sum256 ( subject )
big := [ ] byte { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 5 } // 2^64 + 5, which wraps around to 5 in 64 bits
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
for name , info := range map [ string ] [ ] byte {
Tests of the CMS reader: certificates field by field, every check, fuzzing
The builder of the tests, cmstest:
- NewCert writes a certificate from the DER of its tbsCertificate, field by
field: names of any string type with any bytes (UTF8String,
PrintableString with an underscore or an at sign, IA5String,
TeletexString, BMPString of odd length or with a surrogate,
VisibleString, NumericString), an attribute twice or none, no version,
times with a fraction, an extension twice, a compressed EC key, an even
modulus, and any signature. A Signer made so serves Signature and Token.
- Options for the version of a SignerInfo, the hashAlgorithm and the
certHash of an ESSCertIDv2, a signatureAlgorithm other than the one of
the key, a certificate twice, two content-type attributes, an attribute
with an arc of 2^31, a SignerInfo twice, BER, signerInfos out of order,
two signature-time-stamp attributes, and edits of the SignedData and of
each SignerInfo.
- Token options for any accuracy, a genTime of free text, ordering FALSE,
a field after the last, an imprint of any length, no message-digest, a
CRL in crls and the certificate of the authority twice.
- Edits of the DER after signing: Edit, Retag, Withdraw (a SignerInfo
removed), WithoutTimeStamp (a CAdES-T removed), Merge (a co-signature)
and Indefinite.
The tests of internal/cms and internal/der fail for each check of cms.go,
cert.go, verify.go and der.go. A mutation run, which replaces each leaf of
each condition by false and by true, one at a time, kills every mutant
that is not equivalent to the code it mutates.
FuzzParseSignature, FuzzParseToken, FuzzParseCert and FuzzDERCheck, seeded
with security_cms.json and with what cmstest builds: no panic, what Check
accepts Split reads, and the parsers fail only with ErrForm or
ErrAlgorithm.
capsule: the case of a seal outside the validity of the certificate gave
an invalid seal; it now tests a certificate that expired before a valid
seal (out of validity) apart from an authority that was not valid at its
time (invalid seal). SIGNERS out of order, empty, too long, with 31 bytes
or with a hash twice are F1 beside a CMS signature that is valid for the
AUTHOR_MESSAGE of those SIGNERS, and the names of certificates show as
spec v0.12 §29.7 says.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days ago
"a negative accuracy" : tstInfo ( subject , good , cmstest . Seq ( cmstest . Int ( - 31536000 ) ) ) ,
"an accuracy of -1" : tstInfo ( subject , good , cmstest . Seq ( cmstest . Int ( - 1 ) ) ) ,
"an accuracy that overflows" : tstInfo ( subject , good , cmstest . Seq ( cmstest . Int ( 9223372037 ) ) ) ,
"an accuracy of 2^31 seconds" : tstInfo ( subject , good , cmstest . Seq ( cmstest . Int ( 1 << 31 ) ) ) ,
"an accuracy of 2^64 + 5 seconds" : tstInfo ( subject , good , cmstest . Seq ( cmstest . IntBytes ( big ) ) ) ,
"millis of 0" : tstInfo ( subject , good , cmstest . Seq ( ms ( 0 ) ) ) ,
"millis of 1000" : tstInfo ( subject , good , cmstest . Seq ( ms ( 0x03 , 0xe8 ) ) ) ,
"millis of 5000" : tstInfo ( subject , good , cmstest . Seq ( ms ( 0x13 , 0x88 ) ) ) ,
"millis of -1" : tstInfo ( subject , good , cmstest . Seq ( ms ( 0xff ) ) ) ,
"millis of 5 in two bytes" : tstInfo ( subject , good , cmstest . Seq ( ms ( 0 , 5 ) ) ) ,
"millis of 2^64 + 5" : tstInfo ( subject , good , cmstest . Seq ( ms ( big ... ) ) ) ,
"empty millis" : tstInfo ( subject , good , cmstest . Seq ( ms ( ) ) ) ,
"millis constructed" : tstInfo ( subject , good , cmstest . Seq ( cmstest . TLV ( 0xa0 , cmstest . Int ( 5 ) ) ) ) ,
"micros of 0" : tstInfo ( subject , good , cmstest . Seq ( us ( 0 ) ) ) ,
"micros of 1000" : tstInfo ( subject , good , cmstest . Seq ( us ( 0x03 , 0xe8 ) ) ) ,
"micros before millis" : tstInfo ( subject , good , cmstest . Seq ( us ( 1 ) , ms ( 1 ) ) ) ,
"a field after the micros" : tstInfo ( subject , good , cmstest . Seq ( us ( 1 ) , cmstest . TLV ( 0x82 , [ ] byte { 1 } ) ) ) ,
"millis as an INTEGER" : tstInfo ( subject , good , cmstest . Seq ( cmstest . Int ( 1 ) , cmstest . Int ( 5 ) ) ) ,
"genTime with an offset" : tstInfo ( subject , cmstest . GeneralizedTime ( "20260930130000+0100" ) ) ,
"genTime with a trailing zero" : tstInfo ( subject , cmstest . GeneralizedTime ( "20260930120000.50Z" ) ) ,
"genTime without seconds" : tstInfo ( subject , cmstest . GeneralizedTime ( "202609301200Z" ) ) ,
"genTime as a UTCTime" : tstInfo ( subject , cmstest . UTCTime ( "260930120000Z" ) ) ,
"ordering FALSE written" : tstInfo ( subject , good , cmstest . Bool ( false ) ) ,
"ordering of two bytes" : tstInfo ( subject , good , cmstest . TLV ( 0x01 , [ ] byte { 0xff , 0xff } ) ) ,
"two accuracies" : tstInfo ( subject , good , cmstest . Seq ( cmstest . Int ( 1 ) ) , cmstest . Seq ( cmstest . Int ( 1 ) ) ) ,
"two orderings" : tstInfo ( subject , good , cmstest . Bool ( true ) , cmstest . Bool ( true ) ) ,
"an extra INTEGER at the end" : tstInfo ( subject , good , cmstest . Int ( 7 ) , cmstest . Int ( 8 ) , cmstest . Int ( 9 ) ) ,
"two tsa fields" : tstInfo ( subject , good , tsaName , tsaName ) ,
"two extensions" : tstInfo ( subject , good , exts , exts ) ,
"a field after the extensions" : tstInfo ( subject , good , exts , cmstest . Int ( 1 ) ) ,
"a field out of order" : tstInfo ( subject , good , cmstest . Int ( 7 ) , cmstest . Seq ( cmstest . Int ( 1 ) ) ) ,
"a reserved tag in the extensions" : tstInfo ( subject , good , cmstest . TLV ( 0xa1 , cmstest . TLV ( 0x0e , [ ] byte { 0x41 } ) ) ) ,
"an unused-bits BIT STRING inside" : tstInfo ( subject , good , cmstest . TLV ( 0xa1 , cmstest . TLV ( 0x03 , [ ] byte { 7 , 0xff } ) ) ) ,
"version 2" : cmstest . Seq ( cmstest . Int ( 2 ) ) ,
"version 2, the fields complete" : with ( 0 , cmstest . Int ( 2 ) ) ,
"version 2^64 + 1" : with ( 0 , cmstest . IntBytes ( [ ] byte { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 } ) ) ,
"the version as an ENUMERATED" : with ( 0 , cmstest . TLV ( 0x0a , [ ] byte { 1 } ) ) ,
"the policy as an INTEGER" : with ( 1 , cmstest . Int ( 1 ) ) ,
"the serialNumber as OCTETS" : with ( 3 , cmstest . Octets ( [ ] byte { 42 } ) ) ,
"the messageImprint as a SET" : with ( 2 , cmstest . TLV ( 0x31 , cmstest . Seq ( cmstest . OID ( oidSHA256 ) ) , cmstest . Octets ( h [ : ] ) ) ) ,
"a messageImprint of three fields" : with ( 2 , cmstest . Seq ( cmstest . Seq ( cmstest . OID ( oidSHA256 ) ) , cmstest . Octets ( h [ : ] ) , cmstest . Null ( ) ) ) ,
"a messageImprint of one field" : with ( 2 , cmstest . Seq ( cmstest . Seq ( cmstest . OID ( oidSHA256 ) ) ) ) ,
"a messageImprint algorithm as a SET" : with ( 2 , cmstest . Seq ( cmstest . TLV ( 0x31 , cmstest . OID ( oidSHA256 ) ) , cmstest . Octets ( h [ : ] ) ) ) ,
"a hashedMessage as a BIT STRING" : with ( 2 , cmstest . Seq ( cmstest . Seq ( cmstest . OID ( oidSHA256 ) ) , cmstest . BitString ( h [ : ] ) ) ) ,
"four fields" : cmstest . Seq ( fields [ : 4 ] ... ) ,
"a SET" : cmstest . TLV ( 0x31 , fields ... ) ,
"not a SEQUENCE" : cmstest . Int ( 1 ) ,
"not DER" : append ( tstInfo ( subject , good ) , 0 ) ,
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 _ , err := cms . ParseToken ( cmstest . TokenRaw ( info , tsa ) ) ; ! errors . Is ( err , cms . ErrForm ) {
t . Errorf ( "%s: %v" , name , err )
}
}
}
// retag changes the identifier octet of the first element of the content of
// the last constructed child of der, a SET OF SignerInfo.
func signerInfoTag ( t * testing . T , b [ ] byte , tag byte ) [ ] byte {
t . Helper ( )
_ , ci , _ := der . Split ( b )
_ , content , _ := der . Split ( ci [ 1 ] )
_ , sd , _ := der . Split ( content [ 0 ] )
signerInfos := sd [ len ( sd ) - 1 ]
_ , infos , _ := der . Split ( signerInfos )
at := bytes . Index ( b , infos [ 0 ] )
if at < 0 {
t . Fatal ( "no SignerInfo" )
}
out := bytes . Clone ( b )
out [ at ] = tag
return out
}
func TestSignatureStrictness ( t * testing . T ) {
a := cmstest . NewECDSA ( "Ana" , elliptic . P256 ( ) , from , to )
good := cmstest . Signature ( msg , cmstest . Options { } , a )
for name , b := range map [ string ] [ ] byte {
"ContentInfo as a SET" : append ( [ ] byte { 0x31 } , good [ 1 : ] ... ) ,
"ContentInfo as [3]" : append ( [ ] byte { 0xa3 } , good [ 1 : ] ... ) ,
"SignerInfo as a SET" : signerInfoTag ( t , good , 0x31 ) ,
"SignerInfo as [5]" : signerInfoTag ( t , good , 0xa5 ) ,
"an attribute without a value" : cmstest . Signature ( msg , cmstest . Options { ExtraAttrs : [ ] [ ] byte { cmstest . Seq ( cmstest . OID ( asn1 . ObjectIdentifier { 1 , 2 , 840 , 113549 , 1 , 9 , 3 } ) , cmstest . Set ( 0x31 ) ) } } , a ) ,
"a second content-type" : cmstest . Signature ( msg , cmstest . Options { ExtraAttrs : [ ] [ ] byte { cmstest . Seq ( cmstest . OID ( asn1 . ObjectIdentifier { 1 , 2 , 840 , 113549 , 1 , 9 , 3 } ) , cmstest . Set ( 0x31 , cmstest . OID ( cmstest . OIDData ) ) ) } } , a ) ,
} {
if _ , err := cms . ParseSignature ( b ) ; ! errors . Is ( err , cms . ErrForm ) {
t . Errorf ( "%s: %v" , name , err )
}
}
// Both signing-certificate attributes: the v2 counts, the other decides nothing.
both := cmstest . Signature ( msg , cmstest . Options { SigCertV1 : true } , a )
if sd , err := cms . ParseSignature ( both ) ; err != nil || sd . Signers [ 0 ] . Check ( msg ) != cms . Valid {
t . Errorf ( "signing-certificate beside the v2: %v" , err )
}
// PSS parameters that write their default are not accepted as verifiable.
rsa := cmstest . NewRSA ( "Luis" , 2048 , from , to )
sd , err := cms . ParseSignature ( cmstest . Signature ( msg , cmstest . Options { PSS : true , PSSTrailer : true } , rsa ) )
if err != nil || sd . Signers [ 0 ] . Check ( msg ) != cms . NotVerifiable {
t . Errorf ( "a PSS trailerField written: %v" , err )
}
sd , err = cms . ParseSignature ( cmstest . Signature ( msg , cmstest . Options { PSS : true } , rsa ) )
if err != nil || sd . Signers [ 0 ] . Check ( msg ) != cms . Valid {
t . Errorf ( "PSS without it: %v" , err )
}
}