Remove encryption code from containerd core
We are separating out the encryption code and have designed a few new interfaces and APIs for processing content streams. This keep the core clean of encryption code but enables not only encryption but support of multiple content types ( custom media types ). Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
		
							
								
								
									
										22
									
								
								vendor/github.com/fullsailor/pkcs7/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/fullsailor/pkcs7/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/fullsailor/pkcs7/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/fullsailor/pkcs7/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
# pkcs7
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/fullsailor/pkcs7)
 | 
			
		||||
[](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)
 | 
			
		||||
							
								
								
									
										248
									
								
								vendor/github.com/fullsailor/pkcs7/ber.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										248
									
								
								vendor/github.com/fullsailor/pkcs7/ber.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,248 +0,0 @@
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										962
									
								
								vendor/github.com/fullsailor/pkcs7/pkcs7.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										962
									
								
								vendor/github.com/fullsailor/pkcs7/pkcs7.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,962 +0,0 @@
 | 
			
		||||
// 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, ¶ms)
 | 
			
		||||
		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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								vendor/github.com/fullsailor/pkcs7/x509.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										133
									
								
								vendor/github.com/fullsailor/pkcs7/x509.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,133 +0,0 @@
 | 
			
		||||
// 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, ¶ms); 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
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user