summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go
blob: d57daf51b5e5077294a835b6a52a50b9316f1f16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Package pkcs7 implements the subset of the CMS PKCS #7 datatype that is typically
// used to package certificates and CRLs.  Using openssl, every certificate converted
// to PKCS #7 format from another encoding such as PEM conforms to this implementation.
// reference: https://www.openssl.org/docs/man1.1.0/apps/crl2pkcs7.html
//
//			PKCS #7 Data type, reference: https://tools.ietf.org/html/rfc2315
//
// The full pkcs#7 cryptographic message syntax allows for cryptographic enhancements,
// for example data can be encrypted and signed and then packaged through pkcs#7 to be
// sent over a network and then verified and decrypted.  It is asn1, and the type of
// PKCS #7 ContentInfo, which comprises the PKCS #7 structure, is:
//
//			ContentInfo ::= SEQUENCE {
//				contentType ContentType,
//				content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
//			}
//
// There are 6 possible ContentTypes, data, signedData, envelopedData,
// signedAndEnvelopedData, digestedData, and encryptedData.  Here signedData, Data, and encrypted
// Data are implemented, as the degenerate case of signedData without a signature is the typical
// format for transferring certificates and CRLS, and Data and encryptedData are used in PKCS #12
// formats.
// The ContentType signedData has the form:
//
//
//			signedData ::= SEQUENCE {
//				version Version,
//				digestAlgorithms DigestAlgorithmIdentifiers,
//				contentInfo ContentInfo,
//				certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL
//				crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
//				signerInfos SignerInfos
//			}
//
// As of yet signerInfos and digestAlgorithms are not parsed, as they are not relevant to
// this system's use of PKCS #7 data.  Version is an integer type, note that PKCS #7 is
// recursive, this second layer of ContentInfo is similar ignored for our degenerate
// usage.  The ExtendedCertificatesAndCertificates type consists of a sequence of choices
// between PKCS #6 extended certificates and x509 certificates.  Any sequence consisting
// of any number of extended certificates is not yet supported in this implementation.
//
// The ContentType Data is simply a raw octet string and is parsed directly into a Go []byte slice.
//
// The ContentType encryptedData is the most complicated and its form can be gathered by
// the go type below.  It essentially contains a raw octet string of encrypted data and an
// algorithm identifier for use in decrypting this data.
package pkcs7

import (
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"errors"

	cferr "github.com/cloudflare/cfssl/errors"
)

// Types used for asn1 Unmarshaling.

type signedData struct {
	Version          int
	DigestAlgorithms asn1.RawValue
	ContentInfo      asn1.RawValue
	Certificates     asn1.RawValue `asn1:"optional" asn1:"tag:0"`
	Crls             asn1.RawValue `asn1:"optional"`
	SignerInfos      asn1.RawValue
}

type initPKCS7 struct {
	Raw         asn1.RawContent
	ContentType asn1.ObjectIdentifier
	Content     asn1.RawValue `asn1:"tag:0,explicit,optional"`
}

// Object identifier strings of the three implemented PKCS7 types.
const (
	ObjIDData          = "1.2.840.113549.1.7.1"
	ObjIDSignedData    = "1.2.840.113549.1.7.2"
	ObjIDEncryptedData = "1.2.840.113549.1.7.6"
)

// PKCS7 represents the ASN1 PKCS #7 Content type.  It contains one of three
// possible types of Content objects, as denoted by the object identifier in
// the ContentInfo field, the other two being nil.  SignedData
// is the degenerate SignedData Content info without signature used
// to hold certificates and crls.  Data is raw bytes, and EncryptedData
// is as defined in PKCS #7 standard.
type PKCS7 struct {
	Raw         asn1.RawContent
	ContentInfo string
	Content     Content
}

// Content implements three of the six possible PKCS7 data types.  Only one is non-nil.
type Content struct {
	Data          []byte
	SignedData    SignedData
	EncryptedData EncryptedData
}

// SignedData defines the typical carrier of certificates and crls.
type SignedData struct {
	Raw          asn1.RawContent
	Version      int
	Certificates []*x509.Certificate
	Crl          *pkix.CertificateList
}

// Data contains raw bytes.  Used as a subtype in PKCS12.
type Data struct {
	Bytes []byte
}

// EncryptedData contains encrypted data.  Used as a subtype in PKCS12.
type EncryptedData struct {
	Raw                  asn1.RawContent
	Version              int
	EncryptedContentInfo EncryptedContentInfo
}

// EncryptedContentInfo is a subtype of PKCS7EncryptedData.
type EncryptedContentInfo struct {
	Raw                        asn1.RawContent
	ContentType                asn1.ObjectIdentifier
	ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
	EncryptedContent           []byte `asn1:"tag:0,optional"`
}

// ParsePKCS7 attempts to parse the DER encoded bytes of a
// PKCS7 structure.
func ParsePKCS7(raw []byte) (msg *PKCS7, err error) {

	var pkcs7 initPKCS7
	_, err = asn1.Unmarshal(raw, &pkcs7)
	if err != nil {
		return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
	}

	msg = new(PKCS7)
	msg.Raw = pkcs7.Raw
	msg.ContentInfo = pkcs7.ContentType.String()
	switch {
	case msg.ContentInfo == ObjIDData:
		msg.ContentInfo = "Data"
		_, err = asn1.Unmarshal(pkcs7.Content.Bytes, &msg.Content.Data)
		if err != nil {
			return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
		}
	case msg.ContentInfo == ObjIDSignedData:
		msg.ContentInfo = "SignedData"
		var signedData signedData
		_, err = asn1.Unmarshal(pkcs7.Content.Bytes, &signedData)
		if err != nil {
			return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
		}
		if len(signedData.Certificates.Bytes) != 0 {
			msg.Content.SignedData.Certificates, err = x509.ParseCertificates(signedData.Certificates.Bytes)
			if err != nil {
				return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
			}
		}
		if len(signedData.Crls.Bytes) != 0 {
			msg.Content.SignedData.Crl, err = x509.ParseDERCRL(signedData.Crls.Bytes)
			if err != nil {
				return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
			}
		}
		msg.Content.SignedData.Version = signedData.Version
		msg.Content.SignedData.Raw = pkcs7.Content.Bytes
	case msg.ContentInfo == ObjIDEncryptedData:
		msg.ContentInfo = "EncryptedData"
		var encryptedData EncryptedData
		_, err = asn1.Unmarshal(pkcs7.Content.Bytes, &encryptedData)
		if err != nil {
			return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
		}
		if encryptedData.Version != 0 {
			return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Only support for PKCS #7 encryptedData version 0"))
		}
		msg.Content.EncryptedData = encryptedData

	default:
		return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Attempt to parse PKCS# 7 Content not of type data, signed data or encrypted data"))
	}

	return msg, nil

}