From 465131fdfc33a6b0ecb2cf3e65ad912858288599 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Wed, 11 Jul 2018 03:41:03 -0700 Subject: [PATCH] vendor: bump google/certificate-transparency-go to 1.0.20 This is to include the Windows + Go1.11 fix (https://github.com/google/certificate-transparency-go/issues/284). Signed-off-by: Kir Kolyshkin --- vendor.conf | 2 +- .../client/configpb/multilog.pb.go | 88 ++++++++---- .../client/logclient.go | 60 ++++---- .../client/multilog.go | 2 +- .../jsonclient/client.go | 7 +- .../serialization.go | 56 ++++++++ .../certificate-transparency-go/signatures.go | 21 ++- .../certificate-transparency-go/tls/types.go | 23 ++- .../certificate-transparency-go/types.go | 49 +++++++ .../x509/ptr_sysptr_windows.go | 20 +++ .../x509/ptr_uint_windows.go | 17 +++ .../x509/root_windows.go | 2 +- .../x509/verify.go | 119 +++++++++++----- .../certificate-transparency-go/x509/x509.go | 133 +++++++++++++----- 14 files changed, 466 insertions(+), 133 deletions(-) create mode 100644 vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go create mode 100644 vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go diff --git a/vendor.conf b/vendor.conf index e1ff0b4bb781f..0a015862bddca 100644 --- a/vendor.conf +++ b/vendor.conf @@ -129,7 +129,7 @@ github.com/docker/swarmkit 68266392a176434d282760d2d6d0ab4c68edcae6 github.com/gogo/protobuf v1.0.0 github.com/cloudflare/cfssl 1.3.2 github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 -github.com/google/certificate-transparency-go 5ab67e519c93568ac3ee50fd6772a5bcf8aa460d +github.com/google/certificate-transparency-go v1.0.20 golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491 golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad diff --git a/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go b/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go index 889fbe259394e..2e55408452f3d 100644 --- a/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go +++ b/vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go @@ -1,22 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: multilog.proto -/* -Package configpb is a generated protocol buffer package. - -It is generated from these files: - multilog.proto - -It has these top-level messages: - TemporalLogConfig - LogShardConfig -*/ package configpb import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" -import google_protobuf "github.com/golang/protobuf/ptypes/timestamp" +import timestamp "github.com/golang/protobuf/ptypes/timestamp" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -32,13 +22,35 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // TemporalLogConfig is a set of LogShardConfig messages, whose // time limits should be contiguous. type TemporalLogConfig struct { - Shard []*LogShardConfig `protobuf:"bytes,1,rep,name=shard" json:"shard,omitempty"` + Shard []*LogShardConfig `protobuf:"bytes,1,rep,name=shard,proto3" json:"shard,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TemporalLogConfig) Reset() { *m = TemporalLogConfig{} } +func (m *TemporalLogConfig) String() string { return proto.CompactTextString(m) } +func (*TemporalLogConfig) ProtoMessage() {} +func (*TemporalLogConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_multilog_3c9b797b88da6f07, []int{0} +} +func (m *TemporalLogConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TemporalLogConfig.Unmarshal(m, b) +} +func (m *TemporalLogConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TemporalLogConfig.Marshal(b, m, deterministic) +} +func (dst *TemporalLogConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_TemporalLogConfig.Merge(dst, src) +} +func (m *TemporalLogConfig) XXX_Size() int { + return xxx_messageInfo_TemporalLogConfig.Size(m) +} +func (m *TemporalLogConfig) XXX_DiscardUnknown() { + xxx_messageInfo_TemporalLogConfig.DiscardUnknown(m) } -func (m *TemporalLogConfig) Reset() { *m = TemporalLogConfig{} } -func (m *TemporalLogConfig) String() string { return proto.CompactTextString(m) } -func (*TemporalLogConfig) ProtoMessage() {} -func (*TemporalLogConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +var xxx_messageInfo_TemporalLogConfig proto.InternalMessageInfo func (m *TemporalLogConfig) GetShard() []*LogShardConfig { if m != nil { @@ -50,23 +62,45 @@ func (m *TemporalLogConfig) GetShard() []*LogShardConfig { // LogShardConfig describes the acceptable date range for a single shard of a temporal // log. type LogShardConfig struct { - Uri string `protobuf:"bytes,1,opt,name=uri" json:"uri,omitempty"` + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` // The log's public key in DER-encoded PKIX form. PublicKeyDer []byte `protobuf:"bytes,2,opt,name=public_key_der,json=publicKeyDer,proto3" json:"public_key_der,omitempty"` // not_after_start defines the start of the range of acceptable NotAfter // values, inclusive. // Leaving this unset implies no lower bound to the range. - NotAfterStart *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=not_after_start,json=notAfterStart" json:"not_after_start,omitempty"` + NotAfterStart *timestamp.Timestamp `protobuf:"bytes,3,opt,name=not_after_start,json=notAfterStart,proto3" json:"not_after_start,omitempty"` // not_after_limit defines the end of the range of acceptable NotAfter values, // exclusive. // Leaving this unset implies no upper bound to the range. - NotAfterLimit *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=not_after_limit,json=notAfterLimit" json:"not_after_limit,omitempty"` + NotAfterLimit *timestamp.Timestamp `protobuf:"bytes,4,opt,name=not_after_limit,json=notAfterLimit,proto3" json:"not_after_limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *LogShardConfig) Reset() { *m = LogShardConfig{} } +func (m *LogShardConfig) String() string { return proto.CompactTextString(m) } +func (*LogShardConfig) ProtoMessage() {} +func (*LogShardConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_multilog_3c9b797b88da6f07, []int{1} +} +func (m *LogShardConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_LogShardConfig.Unmarshal(m, b) +} +func (m *LogShardConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_LogShardConfig.Marshal(b, m, deterministic) +} +func (dst *LogShardConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogShardConfig.Merge(dst, src) +} +func (m *LogShardConfig) XXX_Size() int { + return xxx_messageInfo_LogShardConfig.Size(m) +} +func (m *LogShardConfig) XXX_DiscardUnknown() { + xxx_messageInfo_LogShardConfig.DiscardUnknown(m) } -func (m *LogShardConfig) Reset() { *m = LogShardConfig{} } -func (m *LogShardConfig) String() string { return proto.CompactTextString(m) } -func (*LogShardConfig) ProtoMessage() {} -func (*LogShardConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +var xxx_messageInfo_LogShardConfig proto.InternalMessageInfo func (m *LogShardConfig) GetUri() string { if m != nil { @@ -82,14 +116,14 @@ func (m *LogShardConfig) GetPublicKeyDer() []byte { return nil } -func (m *LogShardConfig) GetNotAfterStart() *google_protobuf.Timestamp { +func (m *LogShardConfig) GetNotAfterStart() *timestamp.Timestamp { if m != nil { return m.NotAfterStart } return nil } -func (m *LogShardConfig) GetNotAfterLimit() *google_protobuf.Timestamp { +func (m *LogShardConfig) GetNotAfterLimit() *timestamp.Timestamp { if m != nil { return m.NotAfterLimit } @@ -101,9 +135,9 @@ func init() { proto.RegisterType((*LogShardConfig)(nil), "configpb.LogShardConfig") } -func init() { proto.RegisterFile("multilog.proto", fileDescriptor0) } +func init() { proto.RegisterFile("multilog.proto", fileDescriptor_multilog_3c9b797b88da6f07) } -var fileDescriptor0 = []byte{ +var fileDescriptor_multilog_3c9b797b88da6f07 = []byte{ // 241 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x8f, 0xb1, 0x4e, 0xc3, 0x30, 0x14, 0x45, 0x65, 0x02, 0x08, 0xdc, 0x12, 0xc0, 0x93, 0xd5, 0x85, 0xa8, 0x62, 0xc8, 0xe4, 0x4a, diff --git a/vendor/github.com/google/certificate-transparency-go/client/logclient.go b/vendor/github.com/google/certificate-transparency-go/client/logclient.go index b1031cf8a2c13..a79ef3083cf42 100644 --- a/vendor/github.com/google/certificate-transparency-go/client/logclient.go +++ b/vendor/github.com/google/certificate-transparency-go/client/logclient.go @@ -19,7 +19,6 @@ package client import ( "context" - "crypto/sha256" "encoding/base64" "fmt" "net/http" @@ -35,11 +34,19 @@ type LogClient struct { jsonclient.JSONClient } +// CheckLogClient is an interface that allows (just) checking of various log contents. +type CheckLogClient interface { + BaseURI() string + GetSTH(context.Context) (*ct.SignedTreeHead, error) + GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) + GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*ct.GetProofByHashResponse, error) +} + // New constructs a new LogClient instance. // |uri| is the base URI of the CT log instance to interact with, e.g. -// http://ct.googleapis.com/pilot +// https://ct.googleapis.com/pilot // |hc| is the underlying client to be used for HTTP requests to the CT log. -// |opts| can be used to provide a customer logger interface and a public key +// |opts| can be used to provide a custom logger interface and a public key // for signature verification. func New(uri string, hc *http.Client, opts jsonclient.Options) (*LogClient, error) { logClient, err := jsonclient.New(uri, hc, opts) @@ -169,35 +176,16 @@ func (c *LogClient) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) { } return nil, err } - sth := ct.SignedTreeHead{ - TreeSize: resp.TreeSize, - Timestamp: resp.Timestamp, - } - if len(resp.SHA256RootHash) != sha256.Size { - return nil, RspError{ - Err: fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(resp.SHA256RootHash)), - StatusCode: httpRsp.StatusCode, - Body: body, - } - } - copy(sth.SHA256RootHash[:], resp.SHA256RootHash) - - var ds ct.DigitallySigned - if rest, err := tls.Unmarshal(resp.TreeHeadSignature, &ds); err != nil { + sth, err := resp.ToSignedTreeHead() + if err != nil { return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} - } else if len(rest) > 0 { - return nil, RspError{ - Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)), - StatusCode: httpRsp.StatusCode, - Body: body, - } } - sth.TreeHeadSignature = ds - if err := c.VerifySTHSignature(sth); err != nil { + + if err := c.VerifySTHSignature(*sth); err != nil { return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} } - return &sth, nil + return sth, nil } // VerifySTHSignature checks the signature in sth, returning any error encountered or nil if verification is @@ -281,3 +269,21 @@ func (c *LogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) } return roots, nil } + +// GetEntryAndProof returns a log entry and audit path for the index of a leaf. +func (c *LogClient) GetEntryAndProof(ctx context.Context, index, treeSize uint64) (*ct.GetEntryAndProofResponse, error) { + base10 := 10 + params := map[string]string{ + "leaf_index": strconv.FormatUint(index, base10), + "tree_size": strconv.FormatUint(treeSize, base10), + } + var resp ct.GetEntryAndProofResponse + httpRsp, body, err := c.GetAndParse(ctx, ct.GetEntryAndProofPath, params, &resp) + if err != nil { + if httpRsp != nil { + return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body} + } + return nil, err + } + return &resp, nil +} diff --git a/vendor/github.com/google/certificate-transparency-go/client/multilog.go b/vendor/github.com/google/certificate-transparency-go/client/multilog.go index b345d380a3120..a4860b6d2050a 100644 --- a/vendor/github.com/google/certificate-transparency-go/client/multilog.go +++ b/vendor/github.com/google/certificate-transparency-go/client/multilog.go @@ -23,7 +23,7 @@ import ( "net/http" "time" - "github.com/gogo/protobuf/proto" + "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes" ct "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/client/configpb" diff --git a/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go b/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go index 45638fc69b49e..c34fa833d54a0 100644 --- a/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go +++ b/vendor/github.com/google/certificate-transparency-go/jsonclient/client.go @@ -53,7 +53,7 @@ type backoffer interface { // JSONClient provides common functionality for interacting with a JSON server // that uses cryptographic signatures. type JSONClient struct { - uri string // the base URI of the server. e.g. http://ct.googleapis/pilot + uri string // the base URI of the server. e.g. https://ct.googleapis/pilot httpClient *http.Client // used to interact with the server via HTTP Verifier *ct.SignatureVerifier // nil for no verification (e.g. no public key available) logger Logger // interface to use for logging warnings and errors @@ -139,6 +139,11 @@ func New(uri string, hc *http.Client, opts Options) (*JSONClient, error) { }, nil } +// BaseURI returns the base URI that the JSONClient makes queries to. +func (c *JSONClient) BaseURI() string { + return c.uri +} + // GetAndParse makes a HTTP GET call to the given path, and attempta to parse // the response as a JSON representation of the rsp structure. Returns the // http.Response, the body of the response, and an error. Note that the diff --git a/vendor/github.com/google/certificate-transparency-go/serialization.go b/vendor/github.com/google/certificate-transparency-go/serialization.go index 00b51c1c979da..39053ecd30955 100644 --- a/vendor/github.com/google/certificate-transparency-go/serialization.go +++ b/vendor/github.com/google/certificate-transparency-go/serialization.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "strings" + "time" "github.com/google/certificate-transparency-go/tls" "github.com/google/certificate-transparency-go/x509" @@ -189,6 +190,53 @@ func MerkleTreeLeafFromChain(chain []*x509.Certificate, etype LogEntryType, time return &leaf, nil } +// MerkleTreeLeafForEmbeddedSCT generates a MerkleTreeLeaf from a chain and an +// SCT timestamp, where the leaf certificate at chain[0] is a certificate that +// contains embedded SCTs. It is assumed that the timestamp provided is from +// one of the SCTs embedded within the leaf certificate. +func MerkleTreeLeafForEmbeddedSCT(chain []*x509.Certificate, timestamp uint64) (*MerkleTreeLeaf, error) { + // For building the leaf for a certificate and SCT where the SCT is embedded + // in the certificate, we need to build the original precertificate TBS + // data. First, parse the leaf cert and its issuer. + if len(chain) < 2 { + return nil, fmt.Errorf("no issuer cert available for precert leaf building") + } + issuer := chain[1] + cert := chain[0] + + // Next, post-process the DER-encoded TBSCertificate, to remove the SCTList + // extension. + tbs, err := x509.RemoveSCTList(cert.RawTBSCertificate) + if err != nil { + return nil, fmt.Errorf("failed to remove SCT List extension: %v", err) + } + + return &MerkleTreeLeaf{ + Version: V1, + LeafType: TimestampedEntryLeafType, + TimestampedEntry: &TimestampedEntry{ + EntryType: PrecertLogEntryType, + Timestamp: timestamp, + PrecertEntry: &PreCert{ + IssuerKeyHash: sha256.Sum256(issuer.RawSubjectPublicKeyInfo), + TBSCertificate: tbs, + }, + }, + }, nil +} + +// LeafHashForLeaf returns the leaf hash for a Merkle tree leaf. +func LeafHashForLeaf(leaf *MerkleTreeLeaf) ([sha256.Size]byte, error) { + leafData, err := tls.Marshal(*leaf) + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("failed to tls-encode MerkleTreeLeaf: %s", err) + } + + data := append([]byte{TreeLeafPrefix}, leafData...) + leafHash := sha256.Sum256(data) + return leafHash, nil +} + // IsPreIssuer indicates whether a certificate is a pre-cert issuer with the specific // certificate transparency extended key usage. func IsPreIssuer(issuer *x509.Certificate) bool { @@ -253,3 +301,11 @@ func LogEntryFromLeaf(index int64, leafEntry *LeafEntry) (*LogEntry, error) { // err may hold a x509.NonFatalErrors object. return &entry, err } + +// TimestampToTime converts a timestamp in the style of RFC 6962 (milliseconds +// since UNIX epoch) to a Go Time. +func TimestampToTime(ts uint64) time.Time { + secs := int64(ts / 1000) + msecs := int64(ts % 1000) + return time.Unix(secs, msecs*1000000) +} diff --git a/vendor/github.com/google/certificate-transparency-go/signatures.go b/vendor/github.com/google/certificate-transparency-go/signatures.go index 5129de5572bb0..b1000ba464032 100644 --- a/vendor/github.com/google/certificate-transparency-go/signatures.go +++ b/vendor/github.com/google/certificate-transparency-go/signatures.go @@ -20,8 +20,8 @@ import ( "crypto/elliptic" "crypto/rsa" "crypto/sha256" + "encoding/base64" "encoding/pem" - "flag" "fmt" "log" @@ -29,8 +29,10 @@ import ( "github.com/google/certificate-transparency-go/x509" ) -var allowVerificationWithNonCompliantKeys = flag.Bool("allow_verification_with_non_compliant_keys", false, - "Allow a SignatureVerifier to use keys which are technically non-compliant with RFC6962.") +// AllowVerificationWithNonCompliantKeys may be set to true in order to allow +// SignatureVerifier to use keys which are technically non-compliant with +// RFC6962. +var AllowVerificationWithNonCompliantKeys = false // PublicKeyFromPEM parses a PEM formatted block and returns the public key contained within and any remaining unread bytes, or an error. func PublicKeyFromPEM(b []byte) (crypto.PublicKey, SHA256Hash, []byte, error) { @@ -42,6 +44,15 @@ func PublicKeyFromPEM(b []byte) (crypto.PublicKey, SHA256Hash, []byte, error) { return k, sha256.Sum256(p.Bytes), rest, err } +// PublicKeyFromB64 parses a base64-encoded public key. +func PublicKeyFromB64(b64PubKey string) (crypto.PublicKey, error) { + der, err := base64.StdEncoding.DecodeString(b64PubKey) + if err != nil { + return nil, fmt.Errorf("error decoding public key: %s", err) + } + return x509.ParsePKIXPublicKey(der) +} + // SignatureVerifier can verify signatures on SCTs and STHs type SignatureVerifier struct { pubKey crypto.PublicKey @@ -53,7 +64,7 @@ func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) { case *rsa.PublicKey: if pkType.N.BitLen() < 2048 { e := fmt.Errorf("public key is RSA with < 2048 bits (size:%d)", pkType.N.BitLen()) - if !(*allowVerificationWithNonCompliantKeys) { + if !AllowVerificationWithNonCompliantKeys { return nil, e } log.Printf("WARNING: %v", e) @@ -62,7 +73,7 @@ func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) { params := *(pkType.Params()) if params != *elliptic.P256().Params() { e := fmt.Errorf("public is ECDSA, but not on the P256 curve") - if !(*allowVerificationWithNonCompliantKeys) { + if !AllowVerificationWithNonCompliantKeys { return nil, e } log.Printf("WARNING: %v", e) diff --git a/vendor/github.com/google/certificate-transparency-go/tls/types.go b/vendor/github.com/google/certificate-transparency-go/tls/types.go index 53d8ec7aa72f6..14471ad264c15 100644 --- a/vendor/github.com/google/certificate-transparency-go/tls/types.go +++ b/vendor/github.com/google/certificate-transparency-go/tls/types.go @@ -14,7 +14,13 @@ package tls -import "fmt" +import ( + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/rsa" + "fmt" +) // DigitallySigned gives information about a signature, including the algorithm used // and the signature value. Defined in RFC 5246 s4.7. @@ -94,3 +100,18 @@ func (s SignatureAlgorithm) String() string { return fmt.Sprintf("UNKNOWN(%d)", s) } } + +// SignatureAlgorithmFromPubKey returns the algorithm used for this public key. +// ECDSA, RSA, and DSA keys are supported. Other key types will return Anonymous. +func SignatureAlgorithmFromPubKey(k crypto.PublicKey) SignatureAlgorithm { + switch k.(type) { + case *ecdsa.PublicKey: + return ECDSA + case *rsa.PublicKey: + return RSA + case *dsa.PublicKey: + return DSA + default: + return Anonymous + } +} diff --git a/vendor/github.com/google/certificate-transparency-go/types.go b/vendor/github.com/google/certificate-transparency-go/types.go index c172ce03a7d13..bcdd7e9222d6d 100644 --- a/vendor/github.com/google/certificate-transparency-go/types.go +++ b/vendor/github.com/google/certificate-transparency-go/types.go @@ -54,6 +54,12 @@ func (e LogEntryType) String() string { } } +// RFC6962 section 2.1 requires a prefix byte on hash inputs for second preimage resistance. +const ( + TreeLeafPrefix = byte(0x00) + TreeNodePrefix = byte(0x01) +) + // MerkleLeafType represents the MerkleLeafType enum from section 3.4: // enum { timestamped_entry(0), (255) } MerkleLeafType; type MerkleLeafType tls.Enum // tls:"maxval:255" @@ -368,7 +374,27 @@ func (m *MerkleTreeLeaf) Precertificate() (*x509.Certificate, error) { return x509.ParseTBSCertificate(m.TimestampedEntry.PrecertEntry.TBSCertificate) } +// APIEndpoint is a string that represents one of the Certificate Transparency +// Log API endpoints. +type APIEndpoint string + +// Certificate Transparency Log API endpoints; see section 4. +// WARNING: Should match the URI paths without the "/ct/v1/" prefix. If +// changing these constants, may need to change those too. +const ( + AddChainStr APIEndpoint = "add-chain" + AddPreChainStr APIEndpoint = "add-pre-chain" + GetSTHStr APIEndpoint = "get-sth" + GetEntriesStr APIEndpoint = "get-entries" + GetProofByHashStr APIEndpoint = "get-proof-by-hash" + GetSTHConsistencyStr APIEndpoint = "get-sth-consistency" + GetRootsStr APIEndpoint = "get-roots" + GetEntryAndProofStr APIEndpoint = "get-entry-and-proof" +) + // URI paths for Log requests; see section 4. +// WARNING: Should match the API endpoints, with the "/ct/v1/" prefix. If +// changing these constants, may need to change those too. const ( AddChainPath = "/ct/v1/add-chain" AddPreChainPath = "/ct/v1/add-pre-chain" @@ -415,6 +441,29 @@ type GetSTHResponse struct { TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH } +// ToSignedTreeHead creates a SignedTreeHead from the GetSTHResponse. +func (r *GetSTHResponse) ToSignedTreeHead() (*SignedTreeHead, error) { + sth := SignedTreeHead{ + TreeSize: r.TreeSize, + Timestamp: r.Timestamp, + } + + if len(r.SHA256RootHash) != sha256.Size { + return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(r.SHA256RootHash)) + } + copy(sth.SHA256RootHash[:], r.SHA256RootHash) + + var ds DigitallySigned + if rest, err := tls.Unmarshal(r.TreeHeadSignature, &ds); err != nil { + return nil, fmt.Errorf("tls.Unmarshal(): %s", err) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)) + } + sth.TreeHeadSignature = ds + + return &sth, nil +} + // GetSTHConsistencyResponse represents the JSON response to the get-sth-consistency // GET method from section 4.4. (The corresponding GET request has parameters 'first' and // 'second'.) diff --git a/vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go b/vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go new file mode 100644 index 0000000000000..3543e3042cfde --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/ptr_sysptr_windows.go @@ -0,0 +1,20 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.11 + +package x509 + +import ( + "syscall" + "unsafe" +) + +// For Go versions >= 1.11, the ExtraPolicyPara field in +// syscall.CertChainPolicyPara is of type syscall.Pointer. See: +// https://github.com/golang/go/commit/4869ec00e87ef + +func convertToPolicyParaType(p unsafe.Pointer) syscall.Pointer { + return (syscall.Pointer)(p) +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go b/vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go new file mode 100644 index 0000000000000..3908833a89ded --- /dev/null +++ b/vendor/github.com/google/certificate-transparency-go/x509/ptr_uint_windows.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.11 + +package x509 + +import "unsafe" + +// For Go versions before 1.11, the ExtraPolicyPara field in +// syscall.CertChainPolicyPara was of type uintptr. See: +// https://github.com/golang/go/commit/4869ec00e87ef + +func convertToPolicyParaType(p unsafe.Pointer) uintptr { + return uintptr(p) +} diff --git a/vendor/github.com/google/certificate-transparency-go/x509/root_windows.go b/vendor/github.com/google/certificate-transparency-go/x509/root_windows.go index fc99411b375e9..304ad3a679f02 100644 --- a/vendor/github.com/google/certificate-transparency-go/x509/root_windows.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/root_windows.go @@ -109,7 +109,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) para := &syscall.CertChainPolicyPara{ - ExtraPolicyPara: syscall.Pointer(unsafe.Pointer(sslPara)), + ExtraPolicyPara: convertToPolicyParaType(unsafe.Pointer(sslPara)), } para.Size = uint32(unsafe.Sizeof(*para)) diff --git a/vendor/github.com/google/certificate-transparency-go/x509/verify.go b/vendor/github.com/google/certificate-transparency-go/x509/verify.go index ee4c97bfb99cb..beafc3b00085b 100644 --- a/vendor/github.com/google/certificate-transparency-go/x509/verify.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/verify.go @@ -12,9 +12,12 @@ import ( "net/url" "reflect" "runtime" + "strconv" "strings" "time" "unicode/utf8" + + "github.com/google/certificate-transparency-go/asn1" ) type InvalidReason int @@ -174,19 +177,29 @@ var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificat // VerifyOptions contains parameters for Certificate.Verify. It's a structure // because other PKIX verification APIs have ended up needing many options. type VerifyOptions struct { - DNSName string - Intermediates *CertPool - Roots *CertPool // if nil, the system roots are used - CurrentTime time.Time // if zero, the current time is used - DisableTimeChecks bool - // KeyUsage specifies which Extended Key Usage values are acceptable. - // An empty list means ExtKeyUsageServerAuth. Key usage is considered a - // constraint down the chain which mirrors Windows CryptoAPI behavior, - // but not the spec. To accept any key usage, include ExtKeyUsageAny. + DNSName string + Intermediates *CertPool + Roots *CertPool // if nil, the system roots are used + CurrentTime time.Time // if zero, the current time is used + // Options to disable various verification checks. + DisableTimeChecks bool + DisableCriticalExtensionChecks bool + DisableNameChecks bool + DisableEKUChecks bool + DisablePathLenChecks bool + DisableNameConstraintChecks bool + // KeyUsage specifies which Extended Key Usage values are acceptable. A leaf + // certificate is accepted if it contains any of the listed values. An empty + // list means ExtKeyUsageServerAuth. To accept any key usage, include + // ExtKeyUsageAny. + // + // Certificate chains are required to nest extended key usage values, + // irrespective of this value. This matches the Windows CryptoAPI behavior, + // but not the spec. KeyUsages []ExtKeyUsage // MaxConstraintComparisions is the maximum number of comparisons to // perform when checking a given certificate's name constraints. If - // zero, a sensible default is used. This limit prevents pathalogical + // zero, a sensible default is used. This limit prevents pathological // certificates from consuming excessive amounts of CPU time when // validating. MaxConstraintComparisions int @@ -544,11 +557,16 @@ func (c *Certificate) checkNameConstraints(count *int, return nil } +const ( + checkingAgainstIssuerCert = iota + checkingAgainstLeafCert +) + // ekuPermittedBy returns true iff the given extended key usage is permitted by // the given EKU from a certificate. Normally, this would be a simple // comparison plus a special case for the “any” EKU. But, in order to support // existing certificates, some exceptions are made. -func ekuPermittedBy(eku, certEKU ExtKeyUsage) bool { +func ekuPermittedBy(eku, certEKU ExtKeyUsage, context int) bool { if certEKU == ExtKeyUsageAny || eku == certEKU { return true } @@ -565,28 +583,33 @@ func ekuPermittedBy(eku, certEKU ExtKeyUsage) bool { eku = mapServerAuthEKUs(eku) certEKU = mapServerAuthEKUs(certEKU) - if eku == certEKU || - // ServerAuth in a CA permits ClientAuth in the leaf. - (eku == ExtKeyUsageClientAuth && certEKU == ExtKeyUsageServerAuth) || + if eku == certEKU { + return true + } + + // If checking a requested EKU against the list in a leaf certificate there + // are fewer exceptions. + if context == checkingAgainstLeafCert { + return false + } + + // ServerAuth in a CA permits ClientAuth in the leaf. + return (eku == ExtKeyUsageClientAuth && certEKU == ExtKeyUsageServerAuth) || // Any CA may issue an OCSP responder certificate. eku == ExtKeyUsageOCSPSigning || // Code-signing CAs can use Microsoft's commercial and // kernel-mode EKUs. - ((eku == ExtKeyUsageMicrosoftCommercialCodeSigning || eku == ExtKeyUsageMicrosoftKernelCodeSigning) && certEKU == ExtKeyUsageCodeSigning) { - return true - } - - return false + (eku == ExtKeyUsageMicrosoftCommercialCodeSigning || eku == ExtKeyUsageMicrosoftKernelCodeSigning) && certEKU == ExtKeyUsageCodeSigning } // isValid performs validity checks on c given that it is a candidate to append // to the chain in currentChain. func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error { - if len(c.UnhandledCriticalExtensions) > 0 { + if !opts.DisableCriticalExtensionChecks && len(c.UnhandledCriticalExtensions) > 0 { return UnhandledCriticalExtension{ID: c.UnhandledCriticalExtensions[0]} } - if len(currentChain) > 0 { + if !opts.DisableNameChecks && len(currentChain) > 0 { child := currentChain[len(currentChain)-1] if !bytes.Equal(child.RawIssuer, c.RawSubject) { return CertificateInvalidError{c, NameMismatch, ""} @@ -617,7 +640,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V leaf = currentChain[0] } - if (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() { + if !opts.DisableNameConstraintChecks && (certType == intermediateCertificate || certType == rootCertificate) && c.hasNameConstraints() { sanExtension, ok := leaf.getSANExtension() if !ok { // This is the deprecated, legacy case of depending on @@ -633,8 +656,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V name := string(data) mailbox, ok := parseRFC2821Mailbox(name) if !ok { - // This certificate should not have parsed. - return errors.New("x509: internal error: rfc822Name SAN failed to parse") + return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox) } if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox, @@ -646,6 +668,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V case nameTypeDNS: name := string(data) + if _, ok := domainToReverseLabels(name); !ok { + return fmt.Errorf("x509: cannot parse dnsName %q", name) + } + if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name, func(parsedName, constraint interface{}) (bool, error) { return matchDomainConstraint(parsedName.(string), constraint.(string)) @@ -692,7 +718,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V } } - checkEKUs := certType == intermediateCertificate + checkEKUs := !opts.DisableEKUChecks && certType == intermediateCertificate // If no extended key usages are specified, then all are acceptable. if checkEKUs && (len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0) { @@ -719,7 +745,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V for _, caEKU := range c.ExtKeyUsage { comparisonCount++ - if ekuPermittedBy(eku, caEKU) { + if ekuPermittedBy(eku, caEKU, checkingAgainstIssuerCert) { continue NextEKU } } @@ -766,7 +792,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V return CertificateInvalidError{c, NotAuthorizedToSign, ""} } - if c.BasicConstraintsValid && c.MaxPathLen >= 0 { + if !opts.DisablePathLenChecks && c.BasicConstraintsValid && c.MaxPathLen >= 0 { numIntermediates := len(currentChain) - 1 if numIntermediates > c.MaxPathLen { return CertificateInvalidError{c, TooManyIntermediates, ""} @@ -776,6 +802,18 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V return nil } +// formatOID formats an ASN.1 OBJECT IDENTIFER in the common, dotted style. +func formatOID(oid asn1.ObjectIdentifier) string { + ret := "" + for i, v := range oid { + if i > 0 { + ret += "." + } + ret += strconv.Itoa(v) + } + return ret +} + // Verify attempts to verify c by building one or more chains from c to a // certificate in opts.Roots, using certificates in opts.Intermediates if // needed. If successful, it returns one or more chains where the first @@ -840,7 +878,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e } // If no key usages are specified, then any are acceptable. - checkEKU := len(c.ExtKeyUsage) > 0 + checkEKU := !opts.DisableEKUChecks && len(c.ExtKeyUsage) > 0 for _, eku := range requestedKeyUsages { if eku == ExtKeyUsageAny { @@ -850,16 +888,33 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e } if checkEKU { + foundMatch := false NextUsage: for _, eku := range requestedKeyUsages { for _, leafEKU := range c.ExtKeyUsage { - if ekuPermittedBy(eku, leafEKU) { - continue NextUsage + if ekuPermittedBy(eku, leafEKU, checkingAgainstLeafCert) { + foundMatch = true + break NextUsage } } + } - oid, _ := oidFromExtKeyUsage(eku) - return nil, CertificateInvalidError{c, IncompatibleUsage, fmt.Sprintf("%#v", oid)} + if !foundMatch { + msg := "leaf contains the following, recognized EKUs: " + + for i, leafEKU := range c.ExtKeyUsage { + oid, ok := oidFromExtKeyUsage(leafEKU) + if !ok { + continue + } + + if i > 0 { + msg += ", " + } + msg += formatOID(oid) + } + + return nil, CertificateInvalidError{c, IncompatibleUsage, msg} } } diff --git a/vendor/github.com/google/certificate-transparency-go/x509/x509.go b/vendor/github.com/google/certificate-transparency-go/x509/x509.go index 33d1ed684e221..23f2a6a228f71 100644 --- a/vendor/github.com/google/certificate-transparency-go/x509/x509.go +++ b/vendor/github.com/google/certificate-transparency-go/x509/x509.go @@ -737,7 +737,9 @@ type Certificate struct { OCSPServer []string IssuingCertificateURL []string - // Subject Alternate Name values + // Subject Alternate Name values. (Note that these values may not be valid + // if invalid values were contained within a parsed certificate. For + // example, an element of DNSNames may not be a valid DNS domain name.) DNSNames []string EmailAddresses []string IPAddresses []net.IP @@ -792,6 +794,20 @@ func (c *Certificate) Equal(other *Certificate) bool { return bytes.Equal(c.Raw, other.Raw) } +// IsPrecertificate checks whether the certificate is a precertificate, by +// checking for the presence of the CT Poison extension. +func (c *Certificate) IsPrecertificate() bool { + if c == nil { + return false + } + for _, ext := range c.Extensions { + if ext.Id.Equal(OIDExtensionCTPoison) { + return true + } + } + return false +} + func (c *Certificate) hasSANExtension() bool { return oidInExtensions(OIDExtensionSubjectAltName, c.Extensions) } @@ -995,6 +1011,50 @@ func (h UnhandledCriticalExtension) Error() string { return fmt.Sprintf("x509: unhandled critical extension (%v)", h.ID) } +// removeExtension takes a DER-encoded TBSCertificate, removes the extension +// specified by oid (preserving the order of other extensions), and returns the +// result still as a DER-encoded TBSCertificate. This function will fail if +// there is not exactly 1 extension of the type specified by the oid present. +func removeExtension(tbsData []byte, oid asn1.ObjectIdentifier) ([]byte, error) { + var tbs tbsCertificate + rest, err := asn1.Unmarshal(tbsData, &tbs) + if err != nil { + return nil, fmt.Errorf("failed to parse TBSCertificate: %v", err) + } else if rLen := len(rest); rLen > 0 { + return nil, fmt.Errorf("trailing data (%d bytes) after TBSCertificate", rLen) + } + extAt := -1 + for i, ext := range tbs.Extensions { + if ext.Id.Equal(oid) { + if extAt != -1 { + return nil, errors.New("multiple extensions of specified type present") + } + extAt = i + } + } + if extAt == -1 { + return nil, errors.New("no extension of specified type present") + } + tbs.Extensions = append(tbs.Extensions[:extAt], tbs.Extensions[extAt+1:]...) + // Clear out the asn1.RawContent so the re-marshal operation sees the + // updated structure (rather than just copying the out-of-date DER data). + tbs.Raw = nil + + data, err := asn1.Marshal(tbs) + if err != nil { + return nil, fmt.Errorf("failed to re-marshal TBSCertificate: %v", err) + } + return data, nil +} + +// RemoveSCTList takes a DER-encoded TBSCertificate and removes the CT SCT +// extension that contains the SCT list (preserving the order of other +// extensions), and returns the result still as a DER-encoded TBSCertificate. +// This function will fail if there is not exactly 1 CT SCT extension present. +func RemoveSCTList(tbsData []byte) ([]byte, error) { + return removeExtension(tbsData, OIDExtensionCTSCT) +} + // RemoveCTPoison takes a DER-encoded TBSCertificate and removes the CT poison // extension (preserving the order of other extensions), and returns the result // still as a DER-encoded TBSCertificate. This function will fail if there is @@ -1019,27 +1079,18 @@ func RemoveCTPoison(tbsData []byte) ([]byte, error) { // - The precert's AuthorityKeyId is changed to the AuthorityKeyId of the // intermediate. func BuildPrecertTBS(tbsData []byte, preIssuer *Certificate) ([]byte, error) { + data, err := removeExtension(tbsData, OIDExtensionCTPoison) + if err != nil { + return nil, err + } + var tbs tbsCertificate - rest, err := asn1.Unmarshal(tbsData, &tbs) + rest, err := asn1.Unmarshal(data, &tbs) if err != nil { return nil, fmt.Errorf("failed to parse TBSCertificate: %v", err) } else if rLen := len(rest); rLen > 0 { return nil, fmt.Errorf("trailing data (%d bytes) after TBSCertificate", rLen) } - poisonAt := -1 - for i, ext := range tbs.Extensions { - if ext.Id.Equal(OIDExtensionCTPoison) { - if poisonAt != -1 { - return nil, errors.New("multiple CT poison extensions present") - } - poisonAt = i - } - } - if poisonAt == -1 { - return nil, errors.New("no CT poison extension present") - } - tbs.Extensions = append(tbs.Extensions[:poisonAt], tbs.Extensions[poisonAt+1:]...) - tbs.Raw = nil if preIssuer != nil { // Update the precert's Issuer field. Use the RawIssuer rather than the @@ -1092,9 +1143,13 @@ func BuildPrecertTBS(tbsData []byte, preIssuer *Certificate) ([]byte, error) { } tbs.Extensions = append(tbs.Extensions, authKeyIDExt) } + + // Clear out the asn1.RawContent so the re-marshal operation sees the + // updated structure (rather than just copying the out-of-date DER data). + tbs.Raw = nil } - data, err := asn1.Marshal(tbs) + data, err = asn1.Marshal(tbs) if err != nil { return nil, fmt.Errorf("failed to re-marshal TBSCertificate: %v", err) } @@ -1235,7 +1290,7 @@ type NonFatalErrors struct { Errors []error } -// Adds an error to the list of errors contained by NonFatalErrors. +// AddError adds an error to the list of errors contained by NonFatalErrors. func (e *NonFatalErrors) AddError(err error) { e.Errors = append(e.Errors, err) } @@ -1250,7 +1305,7 @@ func (e NonFatalErrors) Error() string { return r } -// Returns true if |e| contains at least one error +// HasError returns true if |e| contains at least one error func (e *NonFatalErrors) HasError() bool { return len(e.Errors) > 0 } @@ -1337,17 +1392,9 @@ func parseSANExtension(value []byte, nfe *NonFatalErrors) (dnsNames, emailAddres err = forEachSAN(value, func(tag int, data []byte) error { switch tag { case nameTypeEmail: - mailbox := string(data) - if _, ok := parseRFC2821Mailbox(mailbox); !ok { - return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox) - } - emailAddresses = append(emailAddresses, mailbox) + emailAddresses = append(emailAddresses, string(data)) case nameTypeDNS: - domain := string(data) - if _, ok := domainToReverseLabels(domain); !ok { - return fmt.Errorf("x509: cannot parse dnsName %q", string(data)) - } - dnsNames = append(dnsNames, domain) + dnsNames = append(dnsNames, string(data)) case nameTypeURI: uri, err := url.Parse(string(data)) if err != nil { @@ -1364,7 +1411,7 @@ func parseSANExtension(value []byte, nfe *NonFatalErrors) (dnsNames, emailAddres case net.IPv4len, net.IPv6len: ipAddresses = append(ipAddresses, data) default: - nfe.AddError(fmt.Errorf("x509: certificate contained IP address of length %d : %v", len(data), data)) + nfe.AddError(errors.New("x509: cannot parse IP address of length " + strconv.Itoa(len(data)))) } } @@ -1399,7 +1446,7 @@ func isValidIPMask(mask []byte) bool { return true } -func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandled bool, err error) { +func parseNameConstraintsExtension(out *Certificate, e pkix.Extension, nfe *NonFatalErrors) (unhandled bool, err error) { // RFC 5280, 4.2.1.10 // NameConstraints ::= SEQUENCE { @@ -1466,7 +1513,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle trimmedDomain = trimmedDomain[1:] } if _, ok := domainToReverseLabels(trimmedDomain); !ok { - return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain) + nfe.AddError(fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)) } dnsNames = append(dnsNames, domain) @@ -1503,7 +1550,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle // it specifies an exact mailbox name. if strings.Contains(constraint, "@") { if _, ok := parseRFC2821Mailbox(constraint); !ok { - return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint) + nfe.AddError(fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)) } } else { // Otherwise it's a domain name. @@ -1512,7 +1559,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle domain = domain[1:] } if _, ok := domainToReverseLabels(domain); !ok { - return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint) + nfe.AddError(fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)) } } emails = append(emails, constraint) @@ -1536,7 +1583,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle trimmedDomain = trimmedDomain[1:] } if _, ok := domainToReverseLabels(trimmedDomain); !ok { - return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain) + nfe.AddError(fmt.Errorf("x509: failed to parse URI constraint %q", domain)) } uriDomains = append(uriDomains, domain) @@ -1651,7 +1698,7 @@ func parseCertificate(in *certificate) (*Certificate, error) { } case OIDExtensionNameConstraints[3]: - unhandled, err = parseNameConstraintsExtension(out, e) + unhandled, err = parseNameConstraintsExtension(out, e, &nfe) if err != nil { return nil, err } @@ -1787,6 +1834,8 @@ func ParseTBSCertificate(asn1Data []byte) (*Certificate, error) { } // ParseCertificate parses a single certificate from the given ASN.1 DER data. +// This function can return both a Certificate and an error (in which case the +// error will be of type NonFatalErrors). func ParseCertificate(asn1Data []byte) (*Certificate, error) { var cert certificate rest, err := asn1.Unmarshal(asn1Data, &cert) @@ -1802,6 +1851,8 @@ func ParseCertificate(asn1Data []byte) (*Certificate, error) { // ParseCertificates parses one or more certificates from the given ASN.1 DER // data. The certificates must be concatenated with no intermediate padding. +// This function can return both a slice of Certificate and an error (in which +// case the error will be of type NonFatalErrors). func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { var v []*certificate @@ -1815,15 +1866,23 @@ func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { v = append(v, cert) } + var nfe NonFatalErrors ret := make([]*Certificate, len(v)) for i, ci := range v { cert, err := parseCertificate(ci) if err != nil { - return nil, err + if errs, ok := err.(NonFatalErrors); !ok { + return nil, err + } else { + nfe.Errors = append(nfe.Errors, errs.Errors...) + } } ret[i] = cert } + if nfe.HasError() { + return ret, nfe + } return ret, nil }