aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/fullsailor/pkcs7
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/fullsailor/pkcs7')
-rw-r--r--vendor/github.com/fullsailor/pkcs7/.gitignore24
-rw-r--r--vendor/github.com/fullsailor/pkcs7/.travis.yml7
-rw-r--r--vendor/github.com/fullsailor/pkcs7/LICENSE22
-rw-r--r--vendor/github.com/fullsailor/pkcs7/README.md8
-rw-r--r--vendor/github.com/fullsailor/pkcs7/ber.go248
-rw-r--r--vendor/github.com/fullsailor/pkcs7/pkcs7.go962
-rw-r--r--vendor/github.com/fullsailor/pkcs7/x509.go133
7 files changed, 1404 insertions, 0 deletions
diff --git a/vendor/github.com/fullsailor/pkcs7/.gitignore b/vendor/github.com/fullsailor/pkcs7/.gitignore
new file mode 100644
index 000000000..daf913b1b
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/fullsailor/pkcs7/.travis.yml b/vendor/github.com/fullsailor/pkcs7/.travis.yml
new file mode 100644
index 000000000..bc1204376
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/.travis.yml
@@ -0,0 +1,7 @@
+language: go
+
+go:
+ - 1.8
+ - 1.9
+ - "1.10"
+ - tip
diff --git a/vendor/github.com/fullsailor/pkcs7/LICENSE b/vendor/github.com/fullsailor/pkcs7/LICENSE
new file mode 100644
index 000000000..75f320908
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Andrew Smith
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/fullsailor/pkcs7/README.md b/vendor/github.com/fullsailor/pkcs7/README.md
new file mode 100644
index 000000000..bfd948f32
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/README.md
@@ -0,0 +1,8 @@
+# pkcs7
+
+[![GoDoc](https://godoc.org/github.com/fullsailor/pkcs7?status.svg)](https://godoc.org/github.com/fullsailor/pkcs7)
+[![Build Status](https://travis-ci.org/fullsailor/pkcs7.svg?branch=master)](https://travis-ci.org/fullsailor/pkcs7)
+
+pkcs7 implements parsing and creating signed and enveloped messages.
+
+- Documentation on [GoDoc](http://godoc.org/github.com/fullsailor/pkcs7)
diff --git a/vendor/github.com/fullsailor/pkcs7/ber.go b/vendor/github.com/fullsailor/pkcs7/ber.go
new file mode 100644
index 000000000..89e96d30c
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/ber.go
@@ -0,0 +1,248 @@
+package pkcs7
+
+import (
+ "bytes"
+ "errors"
+)
+
+// var encodeIndent = 0
+
+type asn1Object interface {
+ EncodeTo(writer *bytes.Buffer) error
+}
+
+type asn1Structured struct {
+ tagBytes []byte
+ content []asn1Object
+}
+
+func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
+ //fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
+ //encodeIndent++
+ inner := new(bytes.Buffer)
+ for _, obj := range s.content {
+ err := obj.EncodeTo(inner)
+ if err != nil {
+ return err
+ }
+ }
+ //encodeIndent--
+ out.Write(s.tagBytes)
+ encodeLength(out, inner.Len())
+ out.Write(inner.Bytes())
+ return nil
+}
+
+type asn1Primitive struct {
+ tagBytes []byte
+ length int
+ content []byte
+}
+
+func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
+ _, err := out.Write(p.tagBytes)
+ if err != nil {
+ return err
+ }
+ if err = encodeLength(out, p.length); err != nil {
+ return err
+ }
+ //fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length)
+ //fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content))
+ out.Write(p.content)
+
+ return nil
+}
+
+func ber2der(ber []byte) ([]byte, error) {
+ if len(ber) == 0 {
+ return nil, errors.New("ber2der: input ber is empty")
+ }
+ //fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber))
+ out := new(bytes.Buffer)
+
+ obj, _, err := readObject(ber, 0)
+ if err != nil {
+ return nil, err
+ }
+ obj.EncodeTo(out)
+
+ // if offset < len(ber) {
+ // return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))
+ //}
+
+ return out.Bytes(), nil
+}
+
+// encodes lengths that are longer than 127 into string of bytes
+func marshalLongLength(out *bytes.Buffer, i int) (err error) {
+ n := lengthLength(i)
+
+ for ; n > 0; n-- {
+ err = out.WriteByte(byte(i >> uint((n-1)*8)))
+ if err != nil {
+ return
+ }
+ }
+
+ return nil
+}
+
+// computes the byte length of an encoded length value
+func lengthLength(i int) (numBytes int) {
+ numBytes = 1
+ for i > 255 {
+ numBytes++
+ i >>= 8
+ }
+ return
+}
+
+// encodes the length in DER format
+// If the length fits in 7 bits, the value is encoded directly.
+//
+// Otherwise, the number of bytes to encode the length is first determined.
+// This number is likely to be 4 or less for a 32bit length. This number is
+// added to 0x80. The length is encoded in big endian encoding follow after
+//
+// Examples:
+// length | byte 1 | bytes n
+// 0 | 0x00 | -
+// 120 | 0x78 | -
+// 200 | 0x81 | 0xC8
+// 500 | 0x82 | 0x01 0xF4
+//
+func encodeLength(out *bytes.Buffer, length int) (err error) {
+ if length >= 128 {
+ l := lengthLength(length)
+ err = out.WriteByte(0x80 | byte(l))
+ if err != nil {
+ return
+ }
+ err = marshalLongLength(out, length)
+ if err != nil {
+ return
+ }
+ } else {
+ err = out.WriteByte(byte(length))
+ if err != nil {
+ return
+ }
+ }
+ return
+}
+
+func readObject(ber []byte, offset int) (asn1Object, int, error) {
+ //fmt.Printf("\n====> Starting readObject at offset: %d\n\n", offset)
+ tagStart := offset
+ b := ber[offset]
+ offset++
+ tag := b & 0x1F // last 5 bits
+ if tag == 0x1F {
+ tag = 0
+ for ber[offset] >= 0x80 {
+ tag = tag*128 + ber[offset] - 0x80
+ offset++
+ }
+ tag = tag*128 + ber[offset] - 0x80
+ offset++
+ }
+ tagEnd := offset
+
+ kind := b & 0x20
+ /*
+ if kind == 0 {
+ fmt.Print("--> Primitive\n")
+ } else {
+ fmt.Print("--> Constructed\n")
+ }
+ */
+ // read length
+ var length int
+ l := ber[offset]
+ offset++
+ indefinite := false
+ if l > 0x80 {
+ numberOfBytes := (int)(l & 0x7F)
+ if numberOfBytes > 4 { // int is only guaranteed to be 32bit
+ return nil, 0, errors.New("ber2der: BER tag length too long")
+ }
+ if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F {
+ return nil, 0, errors.New("ber2der: BER tag length is negative")
+ }
+ if 0x0 == (int)(ber[offset]) {
+ return nil, 0, errors.New("ber2der: BER tag length has leading zero")
+ }
+ //fmt.Printf("--> (compute length) indicator byte: %x\n", l)
+ //fmt.Printf("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes])
+ for i := 0; i < numberOfBytes; i++ {
+ length = length*256 + (int)(ber[offset])
+ offset++
+ }
+ } else if l == 0x80 {
+ indefinite = true
+ } else {
+ length = (int)(l)
+ }
+
+ //fmt.Printf("--> length : %d\n", length)
+ contentEnd := offset + length
+ if contentEnd > len(ber) {
+ return nil, 0, errors.New("ber2der: BER tag length is more than available data")
+ }
+ //fmt.Printf("--> content start : %d\n", offset)
+ //fmt.Printf("--> content end : %d\n", contentEnd)
+ //fmt.Printf("--> content : % X\n", ber[offset:contentEnd])
+ var obj asn1Object
+ if indefinite && kind == 0 {
+ return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding")
+ }
+ if kind == 0 {
+ obj = asn1Primitive{
+ tagBytes: ber[tagStart:tagEnd],
+ length: length,
+ content: ber[offset:contentEnd],
+ }
+ } else {
+ var subObjects []asn1Object
+ for (offset < contentEnd) || indefinite {
+ var subObj asn1Object
+ var err error
+ subObj, offset, err = readObject(ber, offset)
+ if err != nil {
+ return nil, 0, err
+ }
+ subObjects = append(subObjects, subObj)
+
+ if indefinite {
+ terminated, err := isIndefiniteTermination(ber, offset)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ if terminated {
+ break
+ }
+ }
+ }
+ obj = asn1Structured{
+ tagBytes: ber[tagStart:tagEnd],
+ content: subObjects,
+ }
+ }
+
+ // Apply indefinite form length with 0x0000 terminator.
+ if indefinite {
+ contentEnd = offset + 2
+ }
+
+ return obj, contentEnd, nil
+}
+
+func isIndefiniteTermination(ber []byte, offset int) (bool, error) {
+ if len(ber) - offset < 2 {
+ return false, errors.New("ber2der: Invalid BER format")
+ }
+
+ return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil
+}
diff --git a/vendor/github.com/fullsailor/pkcs7/pkcs7.go b/vendor/github.com/fullsailor/pkcs7/pkcs7.go
new file mode 100644
index 000000000..0264466b4
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/pkcs7.go
@@ -0,0 +1,962 @@
+// Package pkcs7 implements parsing and generation of some PKCS#7 structures.
+package pkcs7
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/hmac"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ "errors"
+ "fmt"
+ "math/big"
+ "sort"
+ "time"
+
+ _ "crypto/sha1" // for crypto.SHA1
+)
+
+// PKCS7 Represents a PKCS7 structure
+type PKCS7 struct {
+ Content []byte
+ Certificates []*x509.Certificate
+ CRLs []pkix.CertificateList
+ Signers []signerInfo
+ raw interface{}
+}
+
+type contentInfo struct {
+ ContentType asn1.ObjectIdentifier
+ Content asn1.RawValue `asn1:"explicit,optional,tag:0"`
+}
+
+// ErrUnsupportedContentType is returned when a PKCS7 content is not supported.
+// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2),
+// and Enveloped Data are supported (1.2.840.113549.1.7.3)
+var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type")
+
+type unsignedData []byte
+
+var (
+ oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
+ oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
+ oidEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3}
+ oidSignedAndEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4}
+ oidDigestedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5}
+ oidEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6}
+ oidAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
+ oidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
+ oidAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
+)
+
+type signedData struct {
+ Version int `asn1:"default:1"`
+ DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
+ ContentInfo contentInfo
+ Certificates rawCertificates `asn1:"optional,tag:0"`
+ CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
+ SignerInfos []signerInfo `asn1:"set"`
+}
+
+type rawCertificates struct {
+ Raw asn1.RawContent
+}
+
+type envelopedData struct {
+ Version int
+ RecipientInfos []recipientInfo `asn1:"set"`
+ EncryptedContentInfo encryptedContentInfo
+}
+
+type recipientInfo struct {
+ Version int
+ IssuerAndSerialNumber issuerAndSerial
+ KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
+ EncryptedKey []byte
+}
+
+type encryptedContentInfo struct {
+ ContentType asn1.ObjectIdentifier
+ ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
+ EncryptedContent asn1.RawValue `asn1:"tag:0,optional"`
+}
+
+type attribute struct {
+ Type asn1.ObjectIdentifier
+ Value asn1.RawValue `asn1:"set"`
+}
+
+type issuerAndSerial struct {
+ IssuerName asn1.RawValue
+ SerialNumber *big.Int
+}
+
+// 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)
+}
+
+type signerInfo struct {
+ Version int `asn1:"default:1"`
+ IssuerAndSerialNumber issuerAndSerial
+ DigestAlgorithm pkix.AlgorithmIdentifier
+ AuthenticatedAttributes []attribute `asn1:"optional,tag:0"`
+ DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
+ EncryptedDigest []byte
+ UnauthenticatedAttributes []attribute `asn1:"optional,tag:1"`
+}
+
+// Parse decodes a DER encoded PKCS7 package
+func Parse(data []byte) (p7 *PKCS7, err error) {
+ if len(data) == 0 {
+ return nil, errors.New("pkcs7: input data is empty")
+ }
+ var info contentInfo
+ der, err := ber2der(data)
+ if err != nil {
+ return nil, err
+ }
+ rest, err := asn1.Unmarshal(der, &info)
+ if len(rest) > 0 {
+ err = asn1.SyntaxError{Msg: "trailing data"}
+ return
+ }
+ if err != nil {
+ return
+ }
+
+ // fmt.Printf("--> Content Type: %s", info.ContentType)
+ switch {
+ case info.ContentType.Equal(oidSignedData):
+ return parseSignedData(info.Content.Bytes)
+ case info.ContentType.Equal(oidEnvelopedData):
+ return parseEnvelopedData(info.Content.Bytes)
+ }
+ return nil, ErrUnsupportedContentType
+}
+
+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 _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
+ return nil, err
+ }
+ } else {
+ // assuming this is tag 04
+ content = compound.Bytes
+ }
+ return &PKCS7{
+ Content: content,
+ Certificates: certs,
+ CRLs: sd.CRLs,
+ Signers: sd.SignerInfos,
+ raw: sd}, nil
+}
+
+func (raw rawCertificates) Parse() ([]*x509.Certificate, error) {
+ if len(raw.Raw) == 0 {
+ return nil, nil
+ }
+
+ var val asn1.RawValue
+ if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil {
+ return nil, err
+ }
+
+ return x509.ParseCertificates(val.Bytes)
+}
+
+func parseEnvelopedData(data []byte) (*PKCS7, error) {
+ var ed envelopedData
+ if _, err := asn1.Unmarshal(data, &ed); err != nil {
+ return nil, err
+ }
+ return &PKCS7{
+ raw: ed,
+ }, nil
+}
+
+// Verify checks the signatures of a PKCS7 object
+// WARNING: Verify does not check signing time or verify certificate chains at
+// this time.
+func (p7 *PKCS7) Verify() (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); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func verifySignature(p7 *PKCS7, signer signerInfo) error {
+ signedData := p7.Content
+ hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
+ if err != nil {
+ return err
+ }
+ 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
+ }
+ h := hash.New()
+ h.Write(p7.Content)
+ computed := h.Sum(nil)
+ if !hmac.Equal(digest, computed) {
+ return &MessageDigestMismatchError{
+ ExpectedDigest: digest,
+ ActualDigest: computed,
+ }
+ }
+ // TODO(fullsailor): Optionally verify certificate chain
+ // TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore
+ signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
+ if err != nil {
+ return err
+ }
+ }
+ cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
+ if cert == nil {
+ return errors.New("pkcs7: No certificate for signer")
+ }
+
+ algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm)
+ if algo == x509.UnknownSignatureAlgorithm {
+ // I'm not sure what the spec here is, and the openssl sources were not
+ // helpful. But, this is what App Store receipts appear to do.
+ // The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)"
+ // But we're expecting a digest + encryption algorithm. So... we're going
+ // to determine an algorithm based on the DigestAlgorithm and this
+ // encryption algorithm.
+ if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) {
+ algo = getRSASignatureAlgorithmForDigestAlgorithm(hash)
+ }
+ }
+ return cert.CheckSignature(algo, signedData, signer.EncryptedDigest)
+}
+
+func marshalAttributes(attrs []attribute) ([]byte, error) {
+ encodedAttributes, err := asn1.Marshal(struct {
+ A []attribute `asn1:"set"`
+ }{A: attrs})
+ if err != nil {
+ return nil, err
+ }
+
+ // Remove the leading sequence octets
+ var raw asn1.RawValue
+ asn1.Unmarshal(encodedAttributes, &raw)
+ return raw.Bytes, nil
+}
+
+var (
+ oidDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
+ oidEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
+)
+
+func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate {
+ for _, cert := range certs {
+ if isCertMatchForIssuerAndSerial(cert, ias) {
+ return cert
+ }
+ }
+ return nil
+}
+
+func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) {
+ switch {
+ case oid.Equal(oidDigestAlgorithmSHA1):
+ return crypto.SHA1, nil
+ case oid.Equal(oidSHA256):
+ return crypto.SHA256, nil
+ }
+ return crypto.Hash(0), ErrUnsupportedAlgorithm
+}
+
+func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm {
+ for _, details := range signatureAlgorithmDetails {
+ if details.pubKeyAlgo == x509.RSA && details.hash == hash {
+ return details.algo
+ }
+ }
+ return x509.UnknownSignatureAlgorithm
+}
+
+// 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)
+}
+
+// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed
+var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported")
+
+// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data
+var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type")
+
+// Decrypt decrypts encrypted content info for recipient cert and private key
+func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, error) {
+ data, ok := p7.raw.(envelopedData)
+ if !ok {
+ return nil, ErrNotEncryptedContent
+ }
+ recipient := selectRecipientForCertificate(data.RecipientInfos, cert)
+ if recipient.EncryptedKey == nil {
+ return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
+ }
+ if priv := pk.(*rsa.PrivateKey); priv != nil {
+ var contentKey []byte
+ contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, recipient.EncryptedKey)
+ if err != nil {
+ return nil, err
+ }
+ return data.EncryptedContentInfo.decrypt(contentKey)
+ }
+ fmt.Printf("Unsupported Private Key: %v\n", pk)
+ return nil, ErrUnsupportedAlgorithm
+}
+
+var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}
+var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
+var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
+var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6}
+var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
+
+func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) {
+ alg := eci.ContentEncryptionAlgorithm.Algorithm
+ if !alg.Equal(oidEncryptionAlgorithmDESCBC) &&
+ !alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) &&
+ !alg.Equal(oidEncryptionAlgorithmAES256CBC) &&
+ !alg.Equal(oidEncryptionAlgorithmAES128CBC) &&
+ !alg.Equal(oidEncryptionAlgorithmAES128GCM) {
+ fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg)
+ return nil, ErrUnsupportedAlgorithm
+ }
+
+ // EncryptedContent can either be constructed of multple OCTET STRINGs
+ // or _be_ a tagged OCTET STRING
+ var cyphertext []byte
+ if eci.EncryptedContent.IsCompound {
+ // Complex case to concat all of the children OCTET STRINGs
+ var buf bytes.Buffer
+ cypherbytes := eci.EncryptedContent.Bytes
+ for {
+ var part []byte
+ cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part)
+ buf.Write(part)
+ if cypherbytes == nil {
+ break
+ }
+ }
+ cyphertext = buf.Bytes()
+ } else {
+ // Simple case, the bytes _are_ the cyphertext
+ cyphertext = eci.EncryptedContent.Bytes
+ }
+
+ var block cipher.Block
+ var err error
+
+ switch {
+ case alg.Equal(oidEncryptionAlgorithmDESCBC):
+ block, err = des.NewCipher(key)
+ case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC):
+ block, err = des.NewTripleDESCipher(key)
+ case alg.Equal(oidEncryptionAlgorithmAES256CBC):
+ fallthrough
+ case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC):
+ block, err = aes.NewCipher(key)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ if alg.Equal(oidEncryptionAlgorithmAES128GCM) {
+ params := aesGCMParameters{}
+ paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes
+
+ _, err := asn1.Unmarshal(paramBytes, &params)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(params.Nonce) != gcm.NonceSize() {
+ return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
+ }
+ if params.ICVLen != gcm.Overhead() {
+ return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
+ }
+
+ plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return plaintext, nil
+ }
+
+ iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes
+ if len(iv) != block.BlockSize() {
+ return nil, errors.New("pkcs7: encryption algorithm parameters are malformed")
+ }
+ mode := cipher.NewCBCDecrypter(block, iv)
+ plaintext := make([]byte, len(cyphertext))
+ mode.CryptBlocks(plaintext, cyphertext)
+ if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil {
+ return nil, err
+ }
+ return plaintext, nil
+}
+
+func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo {
+ for _, recp := range recipients {
+ if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
+ return recp
+ }
+ }
+ return recipientInfo{}
+}
+
+func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool {
+ return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Compare(cert.RawIssuer, ias.IssuerName.FullBytes) == 0
+}
+
+func pad(data []byte, blocklen int) ([]byte, error) {
+ if blocklen < 1 {
+ return nil, fmt.Errorf("invalid blocklen %d", blocklen)
+ }
+ padlen := blocklen - (len(data) % blocklen)
+ if padlen == 0 {
+ padlen = blocklen
+ }
+ pad := bytes.Repeat([]byte{byte(padlen)}, padlen)
+ return append(data, pad...), nil
+}
+
+func unpad(data []byte, blocklen int) ([]byte, error) {
+ if blocklen < 1 {
+ return nil, fmt.Errorf("invalid blocklen %d", blocklen)
+ }
+ if len(data)%blocklen != 0 || len(data) == 0 {
+ return nil, fmt.Errorf("invalid data len %d", len(data))
+ }
+
+ // the last byte is the length of padding
+ padlen := int(data[len(data)-1])
+
+ // check padding integrity, all bytes should be the same
+ pad := data[len(data)-padlen:]
+ for _, padbyte := range pad {
+ if padbyte != byte(padlen) {
+ return nil, errors.New("invalid padding")
+ }
+ }
+
+ return data[:len(data)-padlen], 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")
+}
+
+// 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)
+}
+
+// SignedData is an opaque data structure for creating signed data payloads
+type SignedData struct {
+ sd signedData
+ certs []*x509.Certificate
+ messageDigest []byte
+}
+
+// Attribute represents a key value pair attribute. Value must be marshalable byte
+// `encoding/asn1`
+type Attribute struct {
+ Type asn1.ObjectIdentifier
+ Value interface{}
+}
+
+// SignerInfoConfig are optional values to include when adding a signer
+type SignerInfoConfig struct {
+ ExtraSignedAttributes []Attribute
+}
+
+// NewSignedData initializes a SignedData with content
+func NewSignedData(data []byte) (*SignedData, error) {
+ content, err := asn1.Marshal(data)
+ if err != nil {
+ return nil, err
+ }
+ ci := contentInfo{
+ ContentType: oidData,
+ Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
+ }
+ digAlg := pkix.AlgorithmIdentifier{
+ Algorithm: oidDigestAlgorithmSHA1,
+ }
+ h := crypto.SHA1.New()
+ h.Write(data)
+ md := h.Sum(nil)
+ sd := signedData{
+ ContentInfo: ci,
+ Version: 1,
+ DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digAlg},
+ }
+ return &SignedData{sd: sd, messageDigest: md}, nil
+}
+
+type attributes struct {
+ types []asn1.ObjectIdentifier
+ values []interface{}
+}
+
+// Add adds the attribute, maintaining insertion order
+func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) {
+ attrs.types = append(attrs.types, attrType)
+ attrs.values = append(attrs.values, value)
+}
+
+type sortableAttribute struct {
+ SortKey []byte
+ Attribute attribute
+}
+
+type attributeSet []sortableAttribute
+
+func (sa attributeSet) Len() int {
+ return len(sa)
+}
+
+func (sa attributeSet) Less(i, j int) bool {
+ return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0
+}
+
+func (sa attributeSet) Swap(i, j int) {
+ sa[i], sa[j] = sa[j], sa[i]
+}
+
+func (sa attributeSet) Attributes() []attribute {
+ attrs := make([]attribute, len(sa))
+ for i, attr := range sa {
+ attrs[i] = attr.Attribute
+ }
+ return attrs
+}
+
+func (attrs *attributes) ForMarshaling() ([]attribute, error) {
+ sortables := make(attributeSet, len(attrs.types))
+ for i := range sortables {
+ attrType := attrs.types[i]
+ attrValue := attrs.values[i]
+ asn1Value, err := asn1.Marshal(attrValue)
+ if err != nil {
+ return nil, err
+ }
+ attr := attribute{
+ Type: attrType,
+ Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag
+ }
+ encoded, err := asn1.Marshal(attr)
+ if err != nil {
+ return nil, err
+ }
+ sortables[i] = sortableAttribute{
+ SortKey: encoded,
+ Attribute: attr,
+ }
+ }
+ sort.Sort(sortables)
+ return sortables.Attributes(), nil
+}
+
+// AddSigner signs attributes about the content and adds certificate to payload
+func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
+ attrs := &attributes{}
+ attrs.Add(oidAttributeContentType, sd.sd.ContentInfo.ContentType)
+ attrs.Add(oidAttributeMessageDigest, sd.messageDigest)
+ attrs.Add(oidAttributeSigningTime, time.Now())
+ for _, attr := range config.ExtraSignedAttributes {
+ attrs.Add(attr.Type, attr.Value)
+ }
+ finalAttrs, err := attrs.ForMarshaling()
+ if err != nil {
+ return err
+ }
+ signature, err := signAttributes(finalAttrs, pkey, crypto.SHA1)
+ if err != nil {
+ return err
+ }
+
+ ias, err := cert2issuerAndSerial(cert)
+ if err != nil {
+ return err
+ }
+
+ signer := signerInfo{
+ AuthenticatedAttributes: finalAttrs,
+ DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1},
+ DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA},
+ IssuerAndSerialNumber: ias,
+ EncryptedDigest: signature,
+ Version: 1,
+ }
+ // create signature of signed attributes
+ sd.certs = append(sd.certs, cert)
+ sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
+ return nil
+}
+
+// AddCertificate adds the certificate to the payload. Useful for parent certificates
+func (sd *SignedData) AddCertificate(cert *x509.Certificate) {
+ sd.certs = append(sd.certs, cert)
+}
+
+// Detach removes content from the signed data struct to make it a detached signature.
+// This must be called right before Finish()
+func (sd *SignedData) Detach() {
+ sd.sd.ContentInfo = contentInfo{ContentType: oidData}
+}
+
+// Finish marshals the content and its signers
+func (sd *SignedData) Finish() ([]byte, error) {
+ sd.sd.Certificates = marshalCertificates(sd.certs)
+ inner, err := asn1.Marshal(sd.sd)
+ if err != nil {
+ return nil, err
+ }
+ outer := contentInfo{
+ ContentType: oidSignedData,
+ Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
+ }
+ return asn1.Marshal(outer)
+}
+
+func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) {
+ var ias issuerAndSerial
+ // The issuer RDNSequence has to match exactly the sequence in the certificate
+ // We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence
+ ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer}
+ ias.SerialNumber = cert.SerialNumber
+
+ return ias, nil
+}
+
+// signs the DER encoded form of the attributes with the private key
+func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hash crypto.Hash) ([]byte, error) {
+ attrBytes, err := marshalAttributes(attrs)
+ if err != nil {
+ return nil, err
+ }
+ h := hash.New()
+ h.Write(attrBytes)
+ hashed := h.Sum(nil)
+ switch priv := pkey.(type) {
+ case *rsa.PrivateKey:
+ return rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hashed)
+ }
+ return nil, ErrUnsupportedAlgorithm
+}
+
+// concats and wraps the certificates in the RawValue structure
+func marshalCertificates(certs []*x509.Certificate) rawCertificates {
+ var buf bytes.Buffer
+ for _, cert := range certs {
+ buf.Write(cert.Raw)
+ }
+ rawCerts, _ := marshalCertificateBytes(buf.Bytes())
+ return rawCerts
+}
+
+// Even though, the tag & length are stripped out during marshalling the
+// RawContent, we have to encode it into the RawContent. If its missing,
+// then `asn1.Marshal()` will strip out the certificate wrapper instead.
+func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
+ var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
+ b, err := asn1.Marshal(val)
+ if err != nil {
+ return rawCertificates{}, err
+ }
+ return rawCertificates{Raw: b}, nil
+}
+
+// DegenerateCertificate creates a signed data structure containing only the
+// provided certificate or certificate chain.
+func DegenerateCertificate(cert []byte) ([]byte, error) {
+ rawCert, err := marshalCertificateBytes(cert)
+ if err != nil {
+ return nil, err
+ }
+ emptyContent := contentInfo{ContentType: oidData}
+ sd := signedData{
+ Version: 1,
+ ContentInfo: emptyContent,
+ Certificates: rawCert,
+ CRLs: []pkix.CertificateList{},
+ }
+ content, err := asn1.Marshal(sd)
+ if err != nil {
+ return nil, err
+ }
+ signedContent := contentInfo{
+ ContentType: oidSignedData,
+ Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
+ }
+ return asn1.Marshal(signedContent)
+}
+
+const (
+ EncryptionAlgorithmDESCBC = iota
+ EncryptionAlgorithmAES128GCM
+)
+
+// ContentEncryptionAlgorithm determines the algorithm used to encrypt the
+// plaintext message. Change the value of this variable to change which
+// algorithm is used in the Encrypt() function.
+var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC
+
+// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
+// content with an unsupported algorithm.
+var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported")
+
+const nonceSize = 12
+
+type aesGCMParameters struct {
+ Nonce []byte `asn1:"tag:4"`
+ ICVLen int
+}
+
+func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) {
+ // Create AES key and nonce
+ key := make([]byte, 16)
+ nonce := make([]byte, nonceSize)
+
+ _, err := rand.Read(key)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ _, err = rand.Read(nonce)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Encrypt content
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ gcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ ciphertext := gcm.Seal(nil, nonce, content, nil)
+
+ // Prepare ASN.1 Encrypted Content Info
+ paramSeq := aesGCMParameters{
+ Nonce: nonce,
+ ICVLen: gcm.Overhead(),
+ }
+
+ paramBytes, err := asn1.Marshal(paramSeq)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ eci := encryptedContentInfo{
+ ContentType: oidData,
+ ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
+ Algorithm: oidEncryptionAlgorithmAES128GCM,
+ Parameters: asn1.RawValue{
+ Tag: asn1.TagSequence,
+ Bytes: paramBytes,
+ },
+ },
+ EncryptedContent: marshalEncryptedContent(ciphertext),
+ }
+
+ return key, &eci, nil
+}
+
+func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) {
+ // Create DES key & CBC IV
+ key := make([]byte, 8)
+ iv := make([]byte, des.BlockSize)
+ _, err := rand.Read(key)
+ if err != nil {
+ return nil, nil, err
+ }
+ _, err = rand.Read(iv)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Encrypt padded content
+ block, err := des.NewCipher(key)
+ if err != nil {
+ return nil, nil, err
+ }
+ mode := cipher.NewCBCEncrypter(block, iv)
+ plaintext, err := pad(content, mode.BlockSize())
+ cyphertext := make([]byte, len(plaintext))
+ mode.CryptBlocks(cyphertext, plaintext)
+
+ // Prepare ASN.1 Encrypted Content Info
+ eci := encryptedContentInfo{
+ ContentType: oidData,
+ ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
+ Algorithm: oidEncryptionAlgorithmDESCBC,
+ Parameters: asn1.RawValue{Tag: 4, Bytes: iv},
+ },
+ EncryptedContent: marshalEncryptedContent(cyphertext),
+ }
+
+ return key, &eci, nil
+}
+
+// Encrypt creates and returns an envelope data PKCS7 structure with encrypted
+// recipient keys for each recipient public key.
+//
+// The algorithm used to perform encryption is determined by the current value
+// of the global ContentEncryptionAlgorithm package variable. By default, the
+// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the
+// value before calling Encrypt(). For example:
+//
+// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM
+//
+// TODO(fullsailor): Add support for encrypting content with other algorithms
+func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) {
+ var eci *encryptedContentInfo
+ var key []byte
+ var err error
+
+ // Apply chosen symmetric encryption method
+ switch ContentEncryptionAlgorithm {
+ case EncryptionAlgorithmDESCBC:
+ key, eci, err = encryptDESCBC(content)
+
+ case EncryptionAlgorithmAES128GCM:
+ key, eci, err = encryptAES128GCM(content)
+
+ default:
+ return nil, ErrUnsupportedEncryptionAlgorithm
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ // Prepare each recipient's encrypted cipher key
+ recipientInfos := make([]recipientInfo, len(recipients))
+ for i, recipient := range recipients {
+ encrypted, err := encryptKey(key, recipient)
+ if err != nil {
+ return nil, err
+ }
+ ias, err := cert2issuerAndSerial(recipient)
+ if err != nil {
+ return nil, err
+ }
+ info := recipientInfo{
+ Version: 0,
+ IssuerAndSerialNumber: ias,
+ KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
+ Algorithm: oidEncryptionAlgorithmRSA,
+ },
+ EncryptedKey: encrypted,
+ }
+ recipientInfos[i] = info
+ }
+
+ // Prepare envelope content
+ envelope := envelopedData{
+ EncryptedContentInfo: *eci,
+ Version: 0,
+ RecipientInfos: recipientInfos,
+ }
+ innerContent, err := asn1.Marshal(envelope)
+ if err != nil {
+ return nil, err
+ }
+
+ // Prepare outer payload structure
+ wrapper := contentInfo{
+ ContentType: oidEnvelopedData,
+ Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
+ }
+
+ return asn1.Marshal(wrapper)
+}
+
+func marshalEncryptedContent(content []byte) asn1.RawValue {
+ asn1Content, _ := asn1.Marshal(content)
+ return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
+}
+
+func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) {
+ if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
+ return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
+ }
+ return nil, ErrUnsupportedAlgorithm
+}
diff --git a/vendor/github.com/fullsailor/pkcs7/x509.go b/vendor/github.com/fullsailor/pkcs7/x509.go
new file mode 100644
index 000000000..195fd0e4b
--- /dev/null
+++ b/vendor/github.com/fullsailor/pkcs7/x509.go
@@ -0,0 +1,133 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the go/golang LICENSE file.
+
+package pkcs7
+
+// These are private constants and functions from the crypto/x509 package that
+// are useful when dealing with signatures verified by x509 certificates
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+)
+
+var (
+ oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
+ oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
+ oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
+ oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
+ oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
+ oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
+ oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
+ oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
+ oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
+ oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
+ oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
+ oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
+ oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
+
+ oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
+ oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
+ oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
+
+ oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
+
+ // oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
+ // but it's specified by ISO. Microsoft's makecert.exe has been known
+ // to produce certificates with this OID.
+ oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
+)
+
+var signatureAlgorithmDetails = []struct {
+ algo x509.SignatureAlgorithm
+ name string
+ oid asn1.ObjectIdentifier
+ pubKeyAlgo x509.PublicKeyAlgorithm
+ hash crypto.Hash
+}{
+ {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
+ {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
+ {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
+ {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
+ {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
+ {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
+ {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
+ {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
+ {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
+ {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
+ {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
+ {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
+ {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
+ {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
+ {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
+ {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
+}
+
+// pssParameters reflects the parameters in an AlgorithmIdentifier that
+// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
+type pssParameters struct {
+ // The following three fields are not marked as
+ // optional because the default values specify SHA-1,
+ // which is no longer suitable for use in signatures.
+ Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
+ MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
+ SaltLength int `asn1:"explicit,tag:2"`
+ TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
+}
+
+// asn1.NullBytes is not available prior to Go 1.9
+var nullBytes = []byte{5, 0}
+
+func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
+ if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
+ for _, details := range signatureAlgorithmDetails {
+ if ai.Algorithm.Equal(details.oid) {
+ return details.algo
+ }
+ }
+ return x509.UnknownSignatureAlgorithm
+ }
+
+ // RSA PSS is special because it encodes important parameters
+ // in the Parameters.
+
+ var params pssParameters
+ if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, &params); err != nil {
+ return x509.UnknownSignatureAlgorithm
+ }
+
+ var mgf1HashFunc pkix.AlgorithmIdentifier
+ if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
+ return x509.UnknownSignatureAlgorithm
+ }
+
+ // PSS is greatly overburdened with options. This code forces
+ // them into three buckets by requiring that the MGF1 hash
+ // function always match the message hash function (as
+ // recommended in
+ // https://tools.ietf.org/html/rfc3447#section-8.1), that the
+ // salt length matches the hash length, and that the trailer
+ // field has the default value.
+ if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) ||
+ !params.MGF.Algorithm.Equal(oidMGF1) ||
+ !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
+ !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) ||
+ params.TrailerField != 1 {
+ return x509.UnknownSignatureAlgorithm
+ }
+
+ switch {
+ case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
+ return x509.SHA256WithRSAPSS
+ case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
+ return x509.SHA384WithRSAPSS
+ case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
+ return x509.SHA512WithRSAPSS
+ }
+
+ return x509.UnknownSignatureAlgorithm
+}