Skip to content

Commit

Permalink
Fix digest algorithm selection when hash func != cert hash.
Browse files Browse the repository at this point in the history
Previously, this was deriving the hash to be used in the TSA response
from the hash of the cert. But these are not guaranteed to be the same!
Signers can choose to use different hash algorithms than their certs -
e.g. if my cert is signed with sha384, I can still choose to sign the
TSA request w/ sha256.

This is typically passed through to the signer via crypto.SignerOpts.
This somewhat obfuscated in the pkcs7 encoding, but this ultimately gets
pulled out when signed here: https://github.com/digitorus/pkcs7/blob/3a137a8743524b3683ca4e11608d0dde37caee99/sign.go#L214-L217

To fix this, a new func CreateResponseWithOpts that passes in the
SignerOpts so that we can encode them to be used in signing later.
To keep backwards compatibility with previous behavior before
6c67f27, CreateResponse defaults to
SHA256.
  • Loading branch information
wlynch authored and vanbroup committed Sep 2, 2023
1 parent d1ad5ca commit d7194bf
Showing 1 changed file with 35 additions and 7 deletions.
42 changes: 35 additions & 7 deletions timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,12 @@ func CreateRequest(r io.Reader, opts *RequestOptions) ([]byte, error) {
return req.Marshal()
}

// CreateResponse returns a DER-encoded timestamp response with the specified contents.
// CreateResponseWithOpts returns a DER-encoded timestamp response with the specified contents.
// The fields in the response are populated as follows:
//
// The responder cert is used to populate the responder's name field, and the
// certificate itself is provided alongside the timestamp response signature.
func (t *Timestamp) CreateResponse(signingCert *x509.Certificate, priv crypto.Signer) ([]byte, error) {
func (t *Timestamp) CreateResponseWithOpts(signingCert *x509.Certificate, priv crypto.Signer, opts crypto.SignerOpts) ([]byte, error) {
messageImprint := getMessageImprint(t.HashAlgorithm, t.HashedMessage)

tsaSerialNumber, err := generateTSASerialNumber()
Expand All @@ -421,7 +421,7 @@ func (t *Timestamp) CreateResponse(signingCert *x509.Certificate, priv crypto.Si
if err != nil {
return nil, err
}
signature, err := t.generateSignedData(tstInfo, priv, signingCert)
signature, err := t.generateSignedData(tstInfo, priv, signingCert, opts)
if err != nil {
return nil, err
}
Expand All @@ -438,6 +438,19 @@ func (t *Timestamp) CreateResponse(signingCert *x509.Certificate, priv crypto.Si
return tspResponseBytes, nil
}

// CreateResponse returns a DER-encoded timestamp response with the specified contents.
// The fields in the response are populated as follows:
//
// The responder cert is used to populate the responder's name field, and the
// certificate itself is provided alongside the timestamp response signature.
//
// This function is equivalent to CreateResponseWithOpts, using a SHA256 hash.
//
// Deprecated: Use CreateResponseWithOpts instead.
func (t *Timestamp) CreateResponse(signingCert *x509.Certificate, priv crypto.Signer) ([]byte, error) {
return t.CreateResponseWithOpts(signingCert, priv, crypto.SHA256)
}

// CreateErrorResponse is used to create response other than granted and granted with mod status
func CreateErrorResponse(pkiStatus Status, pkiFailureInfo FailureInfo) ([]byte, error) {
var bs asn1.BitString
Expand Down Expand Up @@ -591,18 +604,33 @@ func (t *Timestamp) populateSigningCertificateV2Ext(certificate *x509.Certificat
return signingCertV2Bytes, nil
}

func (t *Timestamp) generateSignedData(tstInfo []byte, signer crypto.Signer, certificate *x509.Certificate) ([]byte, error) {
// digestAlgorithmToOID converts the hash func to the corresponding OID.
// This should have parity with [pkcs7.getHashForOID].
func digestAlgorithmToOID(hash crypto.Hash) (asn1.ObjectIdentifier, error) {
switch hash {
case crypto.SHA1:
return pkcs7.OIDDigestAlgorithmSHA1, nil
case crypto.SHA256:
return pkcs7.OIDDigestAlgorithmSHA256, nil
case crypto.SHA384:
return pkcs7.OIDDigestAlgorithmSHA384, nil
case crypto.SHA512:
return pkcs7.OIDDigestAlgorithmSHA512, nil
}
return nil, pkcs7.ErrUnsupportedAlgorithm
}

func (t *Timestamp) generateSignedData(tstInfo []byte, signer crypto.Signer, certificate *x509.Certificate, opts crypto.SignerOpts) ([]byte, error) {
signedData, err := pkcs7.NewSignedData(tstInfo)
if err != nil {
return nil, err
}

digestAlgOID, err := pkcs7.GetDigestOIDForSignatureAlgorithm(certificate.SignatureAlgorithm)
alg, err := digestAlgorithmToOID(opts.HashFunc())
if err != nil {
return nil, err
}

signedData.SetDigestAlgorithm(digestAlgOID)
signedData.SetDigestAlgorithm(alg)
signedData.SetContentType(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4})

signingCertV2Bytes, err := t.populateSigningCertificateV2Ext(certificate)
Expand Down

0 comments on commit d7194bf

Please sign in to comment.