mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 02:02:43 +00:00 
			
		
		
		
	 610c8a4d38
			
		
	
	610c8a4d38
	
	
	
		
			
			- The PKI plugin needs to use the customized pkcs7 fork so move it out from the aws credential plugin's package into a shared location
		
			
				
	
	
		
			401 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			401 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package pkcs7
 | |
| 
 | |
| import (
 | |
| 	"crypto"
 | |
| 	"crypto/dsa"
 | |
| 	"crypto/subtle"
 | |
| 	"crypto/x509"
 | |
| 	"crypto/x509/pkix"
 | |
| 	"encoding/asn1"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // Verify is a wrapper around VerifyWithChain() that initializes an empty
 | |
| // trust store, effectively disabling certificate verification when validating
 | |
| // a signature.
 | |
| func (p7 *PKCS7) Verify() (err error) {
 | |
| 	return p7.VerifyWithChain(nil)
 | |
| }
 | |
| 
 | |
| // VerifyWithChain checks the signatures of a PKCS7 object.
 | |
| //
 | |
| // If truststore is not nil, it also verifies the chain of trust of
 | |
| // the end-entity signer cert to one of the roots in the
 | |
| // truststore. When the PKCS7 object includes the signing time
 | |
| // authenticated attr verifies the chain at that time and UTC now
 | |
| // otherwise.
 | |
| func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error) {
 | |
| 	if len(p7.Signers) == 0 {
 | |
| 		return errors.New("pkcs7: Message has no signers")
 | |
| 	}
 | |
| 	for _, signer := range p7.Signers {
 | |
| 		if err := verifySignature(p7, signer, truststore); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // VerifyWithChainAtTime checks the signatures of a PKCS7 object.
 | |
| //
 | |
| // If truststore is not nil, it also verifies the chain of trust of
 | |
| // the end-entity signer cert to a root in the truststore at
 | |
| // currentTime. It does not use the signing time authenticated
 | |
| // attribute.
 | |
| func (p7 *PKCS7) VerifyWithChainAtTime(truststore *x509.CertPool, currentTime time.Time) (err error) {
 | |
| 	if len(p7.Signers) == 0 {
 | |
| 		return errors.New("pkcs7: Message has no signers")
 | |
| 	}
 | |
| 	for _, signer := range p7.Signers {
 | |
| 		if err := verifySignatureAtTime(p7, signer, truststore, currentTime); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func verifySignatureAtTime(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool, currentTime time.Time) (err error) {
 | |
| 	signedData := p7.Content
 | |
| 	ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
 | |
| 	if ee == nil {
 | |
| 		return errors.New("pkcs7: No certificate for signer")
 | |
| 	}
 | |
| 	if len(signer.AuthenticatedAttributes) > 0 {
 | |
| 		// TODO(fullsailor): First check the content type match
 | |
| 		var (
 | |
| 			digest      []byte
 | |
| 			signingTime time.Time
 | |
| 		)
 | |
| 		err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		h := hash.New()
 | |
| 		h.Write(p7.Content)
 | |
| 		computed := h.Sum(nil)
 | |
| 		if subtle.ConstantTimeCompare(digest, computed) != 1 {
 | |
| 			return &MessageDigestMismatchError{
 | |
| 				ExpectedDigest: digest,
 | |
| 				ActualDigest:   computed,
 | |
| 			}
 | |
| 		}
 | |
| 		signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime)
 | |
| 		if err == nil {
 | |
| 			// signing time found, performing validity check
 | |
| 			if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) {
 | |
| 				return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q",
 | |
| 					signingTime.Format(time.RFC3339),
 | |
| 					ee.NotBefore.Format(time.RFC3339),
 | |
| 					ee.NotAfter.Format(time.RFC3339))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if truststore != nil {
 | |
| 		_, err = verifyCertChain(ee, p7.Certificates, truststore, currentTime)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	switch sigalg {
 | |
| 	case x509.DSAWithSHA1, x509.DSAWithSHA256:
 | |
| 		return dsaCheckSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey)
 | |
| 	default:
 | |
| 		return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // dsaCheckSignature verifies the DSA signature on a PKCS7 document. DSA support was
 | |
| // removed from Go's crypto/x509 support prior to Go 1.16. This allows
 | |
| // verifying legacy signatures until affected applications can be migrated off
 | |
| // of DSA.
 | |
| func dsaCheckSignature(algo x509.SignatureAlgorithm, signed, signature []byte, publicKey crypto.PublicKey) error {
 | |
| 	dsaKey, ok := publicKey.(*dsa.PublicKey)
 | |
| 	if !ok {
 | |
| 		return ErrUnsupportedAlgorithm
 | |
| 	}
 | |
| 
 | |
| 	var hashType crypto.Hash
 | |
| 	switch algo {
 | |
| 	case x509.DSAWithSHA1:
 | |
| 		hashType = crypto.SHA1
 | |
| 	case x509.DSAWithSHA256:
 | |
| 		hashType = crypto.SHA256
 | |
| 	default:
 | |
| 		return ErrUnsupportedAlgorithm
 | |
| 	}
 | |
| 	h := hashType.New()
 | |
| 	h.Write(signed)
 | |
| 	signed = h.Sum(nil)
 | |
| 
 | |
| 	dsaSig := new(dsaSignature)
 | |
| 	if rest, err := asn1.Unmarshal(signature, dsaSig); err != nil {
 | |
| 		return err
 | |
| 	} else if len(rest) != 0 {
 | |
| 		return errors.New("x509: trailing data after DSA signature")
 | |
| 	}
 | |
| 	if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
 | |
| 		return errors.New("x509: DSA signature contained zero or negative values")
 | |
| 	}
 | |
| 	// According to FIPS 186-3, section 4.6, the hash must be truncated if it is longer
 | |
| 	// than the key length, but crypto/dsa doesn't do it automatically.
 | |
| 	if maxHashLen := dsaKey.Q.BitLen() / 8; maxHashLen < len(signed) {
 | |
| 		signed = signed[:maxHashLen]
 | |
| 	}
 | |
| 	if !dsa.Verify(dsaKey, signed, dsaSig.R, dsaSig.S) {
 | |
| 		return errors.New("x509: DSA verification failure")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func verifySignature(p7 *PKCS7, signer signerInfo, truststore *x509.CertPool) (err error) {
 | |
| 	signedData := p7.Content
 | |
| 	ee := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
 | |
| 	if ee == nil {
 | |
| 		return errors.New("pkcs7: No certificate for signer")
 | |
| 	}
 | |
| 	signingTime := time.Now().UTC()
 | |
| 	if len(signer.AuthenticatedAttributes) > 0 {
 | |
| 		// TODO(fullsailor): First check the content type match
 | |
| 		var digest []byte
 | |
| 		err := unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeMessageDigest, &digest)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		h := hash.New()
 | |
| 		h.Write(p7.Content)
 | |
| 		computed := h.Sum(nil)
 | |
| 		if subtle.ConstantTimeCompare(digest, computed) != 1 {
 | |
| 			return &MessageDigestMismatchError{
 | |
| 				ExpectedDigest: digest,
 | |
| 				ActualDigest:   computed,
 | |
| 			}
 | |
| 		}
 | |
| 		signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		err = unmarshalAttribute(signer.AuthenticatedAttributes, OIDAttributeSigningTime, &signingTime)
 | |
| 		if err == nil {
 | |
| 			// signing time found, performing validity check
 | |
| 			if signingTime.After(ee.NotAfter) || signingTime.Before(ee.NotBefore) {
 | |
| 				return fmt.Errorf("pkcs7: signing time %q is outside of certificate validity %q to %q",
 | |
| 					signingTime.Format(time.RFC3339),
 | |
| 					ee.NotBefore.Format(time.RFC3339),
 | |
| 					ee.NotAfter.Format(time.RFC3339))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if truststore != nil {
 | |
| 		_, err = verifyCertChain(ee, p7.Certificates, truststore, signingTime)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	sigalg, err := getSignatureAlgorithm(signer.DigestEncryptionAlgorithm, signer.DigestAlgorithm)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	switch sigalg {
 | |
| 	case x509.DSAWithSHA1, x509.DSAWithSHA256:
 | |
| 		return dsaCheckSignature(sigalg, signedData, signer.EncryptedDigest, ee.PublicKey)
 | |
| 	default:
 | |
| 		return ee.CheckSignature(sigalg, signedData, signer.EncryptedDigest)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // GetOnlySigner returns an x509.Certificate for the first signer of the signed
 | |
| // data payload. If there are more or less than one signer, nil is returned
 | |
| func (p7 *PKCS7) GetOnlySigner() *x509.Certificate {
 | |
| 	if len(p7.Signers) != 1 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	signer := p7.Signers[0]
 | |
| 	return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
 | |
| }
 | |
| 
 | |
| // UnmarshalSignedAttribute decodes a single attribute from the signer info
 | |
| func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
 | |
| 	sd, ok := p7.raw.(signedData)
 | |
| 	if !ok {
 | |
| 		return errors.New("pkcs7: payload is not signedData content")
 | |
| 	}
 | |
| 	if len(sd.SignerInfos) < 1 {
 | |
| 		return errors.New("pkcs7: payload has no signers")
 | |
| 	}
 | |
| 	attributes := sd.SignerInfos[0].AuthenticatedAttributes
 | |
| 	return unmarshalAttribute(attributes, attributeType, out)
 | |
| }
 | |
| 
 | |
| func parseSignedData(data []byte) (*PKCS7, error) {
 | |
| 	var sd signedData
 | |
| 	asn1.Unmarshal(data, &sd)
 | |
| 	certs, err := sd.Certificates.Parse()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	// fmt.Printf("--> Signed Data Version %d\n", sd.Version)
 | |
| 
 | |
| 	var compound asn1.RawValue
 | |
| 	var content unsignedData
 | |
| 
 | |
| 	// The Content.Bytes maybe empty on PKI responses.
 | |
| 	if len(sd.ContentInfo.Content.Bytes) > 0 {
 | |
| 		if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 	// Compound octet string
 | |
| 	if compound.IsCompound {
 | |
| 		if compound.Tag == 4 {
 | |
| 			if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		} else {
 | |
| 			content = compound.Bytes
 | |
| 		}
 | |
| 	} else {
 | |
| 		// assuming this is tag 04
 | |
| 		content = compound.Bytes
 | |
| 	}
 | |
| 	return &PKCS7{
 | |
| 		Content:      content,
 | |
| 		Certificates: certs,
 | |
| 		CRLs:         sd.CRLs,
 | |
| 		Signers:      sd.SignerInfos,
 | |
| 		raw:          sd,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // verifyCertChain takes an end-entity certs, a list of potential intermediates and a
 | |
| // truststore, and built all potential chains between the EE and a trusted root.
 | |
| //
 | |
| // When verifying chains that may have expired, currentTime can be set to a past date
 | |
| // to allow the verification to pass. If unset, currentTime is set to the current UTC time.
 | |
| func verifyCertChain(ee *x509.Certificate, certs []*x509.Certificate, truststore *x509.CertPool, currentTime time.Time) (chains [][]*x509.Certificate, err error) {
 | |
| 	intermediates := x509.NewCertPool()
 | |
| 	for _, intermediate := range certs {
 | |
| 		intermediates.AddCert(intermediate)
 | |
| 	}
 | |
| 	verifyOptions := x509.VerifyOptions{
 | |
| 		Roots:         truststore,
 | |
| 		Intermediates: intermediates,
 | |
| 		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
 | |
| 		CurrentTime:   currentTime,
 | |
| 	}
 | |
| 	chains, err = ee.Verify(verifyOptions)
 | |
| 	if err != nil {
 | |
| 		return chains, fmt.Errorf("pkcs7: failed to verify certificate chain: %v", err)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // MessageDigestMismatchError is returned when the signer data digest does not
 | |
| // match the computed digest for the contained content
 | |
| type MessageDigestMismatchError struct {
 | |
| 	ExpectedDigest []byte
 | |
| 	ActualDigest   []byte
 | |
| }
 | |
| 
 | |
| func (err *MessageDigestMismatchError) Error() string {
 | |
| 	return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual  : %X", err.ExpectedDigest, err.ActualDigest)
 | |
| }
 | |
| 
 | |
| func getSignatureAlgorithm(digestEncryption, digest pkix.AlgorithmIdentifier) (x509.SignatureAlgorithm, error) {
 | |
| 	switch {
 | |
| 	case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA1):
 | |
| 		return x509.ECDSAWithSHA1, nil
 | |
| 	case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA256):
 | |
| 		return x509.ECDSAWithSHA256, nil
 | |
| 	case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA384):
 | |
| 		return x509.ECDSAWithSHA384, nil
 | |
| 	case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmECDSASHA512):
 | |
| 		return x509.ECDSAWithSHA512, nil
 | |
| 	case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSA),
 | |
| 		digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA1),
 | |
| 		digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA256),
 | |
| 		digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA384),
 | |
| 		digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmRSASHA512):
 | |
| 		switch {
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
 | |
| 			return x509.SHA1WithRSA, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
 | |
| 			return x509.SHA256WithRSA, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384):
 | |
| 			return x509.SHA384WithRSA, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512):
 | |
| 			return x509.SHA512WithRSA, nil
 | |
| 		default:
 | |
| 			return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
 | |
| 				digest.Algorithm.String(), digestEncryption.Algorithm.String())
 | |
| 		}
 | |
| 	case digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSA),
 | |
| 		digestEncryption.Algorithm.Equal(OIDDigestAlgorithmDSASHA1):
 | |
| 		switch {
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
 | |
| 			return x509.DSAWithSHA1, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
 | |
| 			return x509.DSAWithSHA256, nil
 | |
| 		default:
 | |
| 			return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
 | |
| 				digest.Algorithm.String(), digestEncryption.Algorithm.String())
 | |
| 		}
 | |
| 	case digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP256),
 | |
| 		digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP384),
 | |
| 		digestEncryption.Algorithm.Equal(OIDEncryptionAlgorithmECDSAP521):
 | |
| 		switch {
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA1):
 | |
| 			return x509.ECDSAWithSHA1, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA256):
 | |
| 			return x509.ECDSAWithSHA256, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA384):
 | |
| 			return x509.ECDSAWithSHA384, nil
 | |
| 		case digest.Algorithm.Equal(OIDDigestAlgorithmSHA512):
 | |
| 			return x509.ECDSAWithSHA512, nil
 | |
| 		default:
 | |
| 			return -1, fmt.Errorf("pkcs7: unsupported digest %q for encryption algorithm %q",
 | |
| 				digest.Algorithm.String(), digestEncryption.Algorithm.String())
 | |
| 		}
 | |
| 	default:
 | |
| 		return -1, fmt.Errorf("pkcs7: unsupported algorithm %q",
 | |
| 			digestEncryption.Algorithm.String())
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate {
 | |
| 	for _, cert := range certs {
 | |
| 		if isCertMatchForIssuerAndSerial(cert, ias) {
 | |
| 			return cert
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error {
 | |
| 	for _, attr := range attrs {
 | |
| 		if attr.Type.Equal(attributeType) {
 | |
| 			_, err := asn1.Unmarshal(attr.Value.Bytes, out)
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return errors.New("pkcs7: attribute type not in attributes")
 | |
| }
 |