1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package pkix contains shared, low level structures used for ASN.1 parsing
6// and serialization of X.509 certificates, CRL and OCSP.
7package pkix
8
9import (
10	"encoding/hex"
11	"fmt"
12	"math/big"
13	"time"
14
15	"github.com/google/certificate-transparency-go/asn1"
16)
17
18// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
19// 5280, section 4.1.1.2.
20type AlgorithmIdentifier struct {
21	Algorithm  asn1.ObjectIdentifier
22	Parameters asn1.RawValue `asn1:"optional"`
23}
24
25type RDNSequence []RelativeDistinguishedNameSET
26
27var attributeTypeNames = map[string]string{
28	"2.5.4.6":  "C",
29	"2.5.4.10": "O",
30	"2.5.4.11": "OU",
31	"2.5.4.3":  "CN",
32	"2.5.4.5":  "SERIALNUMBER",
33	"2.5.4.7":  "L",
34	"2.5.4.8":  "ST",
35	"2.5.4.9":  "STREET",
36	"2.5.4.17": "POSTALCODE",
37}
38
39// String returns a string representation of the sequence r,
40// roughly following the RFC 2253 Distinguished Names syntax.
41func (r RDNSequence) String() string {
42	s := ""
43	for i := 0; i < len(r); i++ {
44		rdn := r[len(r)-1-i]
45		if i > 0 {
46			s += ","
47		}
48		for j, tv := range rdn {
49			if j > 0 {
50				s += "+"
51			}
52
53			oidString := tv.Type.String()
54			typeName, ok := attributeTypeNames[oidString]
55			if !ok {
56				derBytes, err := asn1.Marshal(tv.Value)
57				if err == nil {
58					s += oidString + "=#" + hex.EncodeToString(derBytes)
59					continue // No value escaping necessary.
60				}
61
62				typeName = oidString
63			}
64
65			valueString := fmt.Sprint(tv.Value)
66			escaped := make([]rune, 0, len(valueString))
67
68			for k, c := range valueString {
69				escape := false
70
71				switch c {
72				case ',', '+', '"', '\\', '<', '>', ';':
73					escape = true
74
75				case ' ':
76					escape = k == 0 || k == len(valueString)-1
77
78				case '#':
79					escape = k == 0
80				}
81
82				if escape {
83					escaped = append(escaped, '\\', c)
84				} else {
85					escaped = append(escaped, c)
86				}
87			}
88
89			s += typeName + "=" + string(escaped)
90		}
91	}
92
93	return s
94}
95
96type RelativeDistinguishedNameSET []AttributeTypeAndValue
97
98// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in
99// RFC 5280, Section 4.1.2.4.
100type AttributeTypeAndValue struct {
101	Type  asn1.ObjectIdentifier
102	Value interface{}
103}
104
105// AttributeTypeAndValueSET represents a set of ASN.1 sequences of
106// AttributeTypeAndValue sequences from RFC 2986 (PKCS #10).
107type AttributeTypeAndValueSET struct {
108	Type  asn1.ObjectIdentifier
109	Value [][]AttributeTypeAndValue `asn1:"set"`
110}
111
112// Extension represents the ASN.1 structure of the same name. See RFC
113// 5280, section 4.2.
114type Extension struct {
115	Id       asn1.ObjectIdentifier
116	Critical bool `asn1:"optional"`
117	Value    []byte
118}
119
120// Name represents an X.509 distinguished name. This only includes the common
121// elements of a DN. When parsing, all elements are stored in Names and
122// non-standard elements can be extracted from there. When marshaling, elements
123// in ExtraNames are appended and override other values with the same OID.
124type Name struct {
125	Country, Organization, OrganizationalUnit []string
126	Locality, Province                        []string
127	StreetAddress, PostalCode                 []string
128	SerialNumber, CommonName                  string
129
130	Names      []AttributeTypeAndValue
131	ExtraNames []AttributeTypeAndValue
132}
133
134func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
135	for _, rdn := range *rdns {
136		if len(rdn) == 0 {
137			continue
138		}
139
140		for _, atv := range rdn {
141			n.Names = append(n.Names, atv)
142			value, ok := atv.Value.(string)
143			if !ok {
144				continue
145			}
146
147			t := atv.Type
148			if len(t) == 4 && t[0] == OIDAttribute[0] && t[1] == OIDAttribute[1] && t[2] == OIDAttribute[2] {
149				switch t[3] {
150				case OIDCommonName[3]:
151					n.CommonName = value
152				case OIDSerialNumber[3]:
153					n.SerialNumber = value
154				case OIDCountry[3]:
155					n.Country = append(n.Country, value)
156				case OIDLocality[3]:
157					n.Locality = append(n.Locality, value)
158				case OIDProvince[3]:
159					n.Province = append(n.Province, value)
160				case OIDStreetAddress[3]:
161					n.StreetAddress = append(n.StreetAddress, value)
162				case OIDOrganization[3]:
163					n.Organization = append(n.Organization, value)
164				case OIDOrganizationalUnit[3]:
165					n.OrganizationalUnit = append(n.OrganizationalUnit, value)
166				case OIDPostalCode[3]:
167					n.PostalCode = append(n.PostalCode, value)
168				}
169			}
170		}
171	}
172}
173
174var (
175	OIDAttribute          = asn1.ObjectIdentifier{2, 5, 4}
176	OIDCountry            = asn1.ObjectIdentifier{2, 5, 4, 6}
177	OIDOrganization       = asn1.ObjectIdentifier{2, 5, 4, 10}
178	OIDOrganizationalUnit = asn1.ObjectIdentifier{2, 5, 4, 11}
179	OIDCommonName         = asn1.ObjectIdentifier{2, 5, 4, 3}
180	OIDSerialNumber       = asn1.ObjectIdentifier{2, 5, 4, 5}
181	OIDLocality           = asn1.ObjectIdentifier{2, 5, 4, 7}
182	OIDProvince           = asn1.ObjectIdentifier{2, 5, 4, 8}
183	OIDStreetAddress      = asn1.ObjectIdentifier{2, 5, 4, 9}
184	OIDPostalCode         = asn1.ObjectIdentifier{2, 5, 4, 17}
185
186	OIDPseudonym           = asn1.ObjectIdentifier{2, 5, 4, 65}
187	OIDTitle               = asn1.ObjectIdentifier{2, 5, 4, 12}
188	OIDDnQualifier         = asn1.ObjectIdentifier{2, 5, 4, 46}
189	OIDName                = asn1.ObjectIdentifier{2, 5, 4, 41}
190	OIDSurname             = asn1.ObjectIdentifier{2, 5, 4, 4}
191	OIDGivenName           = asn1.ObjectIdentifier{2, 5, 4, 42}
192	OIDInitials            = asn1.ObjectIdentifier{2, 5, 4, 43}
193	OIDGenerationQualifier = asn1.ObjectIdentifier{2, 5, 4, 44}
194)
195
196// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence
197// and returns the new value. The relativeDistinguishedNameSET contains an
198// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
199// search for AttributeTypeAndValue.
200func (n Name) appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence {
201	if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) {
202		return in
203	}
204
205	s := make([]AttributeTypeAndValue, len(values))
206	for i, value := range values {
207		s[i].Type = oid
208		s[i].Value = value
209	}
210
211	return append(in, s)
212}
213
214func (n Name) ToRDNSequence() (ret RDNSequence) {
215	ret = n.appendRDNs(ret, n.Country, OIDCountry)
216	ret = n.appendRDNs(ret, n.Province, OIDProvince)
217	ret = n.appendRDNs(ret, n.Locality, OIDLocality)
218	ret = n.appendRDNs(ret, n.StreetAddress, OIDStreetAddress)
219	ret = n.appendRDNs(ret, n.PostalCode, OIDPostalCode)
220	ret = n.appendRDNs(ret, n.Organization, OIDOrganization)
221	ret = n.appendRDNs(ret, n.OrganizationalUnit, OIDOrganizationalUnit)
222	if len(n.CommonName) > 0 {
223		ret = n.appendRDNs(ret, []string{n.CommonName}, OIDCommonName)
224	}
225	if len(n.SerialNumber) > 0 {
226		ret = n.appendRDNs(ret, []string{n.SerialNumber}, OIDSerialNumber)
227	}
228	for _, atv := range n.ExtraNames {
229		ret = append(ret, []AttributeTypeAndValue{atv})
230	}
231
232	return ret
233}
234
235// String returns the string form of n, roughly following
236// the RFC 2253 Distinguished Names syntax.
237func (n Name) String() string {
238	return n.ToRDNSequence().String()
239}
240
241// oidInAttributeTypeAndValue reports whether a type with the given OID exists
242// in atv.
243func oidInAttributeTypeAndValue(oid asn1.ObjectIdentifier, atv []AttributeTypeAndValue) bool {
244	for _, a := range atv {
245		if a.Type.Equal(oid) {
246			return true
247		}
248	}
249	return false
250}
251
252// CertificateList represents the ASN.1 structure of the same name. See RFC
253// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the
254// signature.
255type CertificateList struct {
256	TBSCertList        TBSCertificateList
257	SignatureAlgorithm AlgorithmIdentifier
258	SignatureValue     asn1.BitString
259}
260
261// HasExpired reports whether certList should have been updated by now.
262func (certList *CertificateList) HasExpired(now time.Time) bool {
263	return !now.Before(certList.TBSCertList.NextUpdate)
264}
265
266// TBSCertificateList represents the ASN.1 structure TBSCertList. See RFC
267// 5280, section 5.1.
268type TBSCertificateList struct {
269	Raw                 asn1.RawContent
270	Version             int `asn1:"optional,default:0"`
271	Signature           AlgorithmIdentifier
272	Issuer              RDNSequence
273	ThisUpdate          time.Time
274	NextUpdate          time.Time            `asn1:"optional"`
275	RevokedCertificates []RevokedCertificate `asn1:"optional"`
276	Extensions          []Extension          `asn1:"tag:0,optional,explicit"`
277}
278
279// RevokedCertificate represents the unnamed ASN.1 structure that makes up the
280// revokedCertificates member of the TBSCertList structure. See RFC
281// 5280, section 5.1.
282type RevokedCertificate struct {
283	SerialNumber   *big.Int
284	RevocationTime time.Time
285	Extensions     []Extension `asn1:"optional"`
286}
287