mirror of
				https://github.com/SSLMate/certspotter.git
				synced 2025-07-03 10:47:17 +02:00 
			
		
		
		
	Embed Google CT library, with my own changes
This commit is contained in:
		
							parent
							
								
									3c33dc8277
								
							
						
					
					
						commit
						16bf546258
					
				@ -1,7 +1,7 @@
 | 
			
		||||
package ctwatch
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/google/certificate-transparency/go"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,8 @@ import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/certificate-transparency/go"
 | 
			
		||||
	"src.agwa.name/ctwatch"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct"
 | 
			
		||||
	"src.agwa.name/ctwatch/cmd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										956
									
								
								ct/asn1/asn1.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										956
									
								
								ct/asn1/asn1.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,956 @@
 | 
			
		||||
// 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 LICENSE file.
 | 
			
		||||
 | 
			
		||||
// Package asn1 implements parsing of DER-encoded ASN.1 data structures,
 | 
			
		||||
// as defined in ITU-T Rec X.690.
 | 
			
		||||
//
 | 
			
		||||
// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
 | 
			
		||||
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
 | 
			
		||||
//
 | 
			
		||||
// START CT CHANGES
 | 
			
		||||
// This is a fork of the Go standard library ASN.1 implementation
 | 
			
		||||
// (encoding/asn1).  The main difference is that this version tries to correct
 | 
			
		||||
// for errors (e.g. use of tagPrintableString when the string data is really
 | 
			
		||||
// ISO8859-1 - a common error present in many x509 certificates in the wild.)
 | 
			
		||||
// END CT CHANGES
 | 
			
		||||
package asn1
 | 
			
		||||
 | 
			
		||||
// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
 | 
			
		||||
// are different encoding formats for those objects. Here, we'll be dealing
 | 
			
		||||
// with DER, the Distinguished Encoding Rules. DER is used in X.509 because
 | 
			
		||||
// it's fast to parse and, unlike BER, has a unique encoding for every object.
 | 
			
		||||
// When calculating hashes over objects, it's important that the resulting
 | 
			
		||||
// bytes be the same at both ends and DER removes this margin of error.
 | 
			
		||||
//
 | 
			
		||||
// ASN.1 is very complex and this package doesn't attempt to implement
 | 
			
		||||
// everything by any means.
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"strings"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A StructuralError suggests that the ASN.1 data is valid, but the Go type
 | 
			
		||||
// which is receiving it doesn't match.
 | 
			
		||||
type StructuralError struct {
 | 
			
		||||
	Msg string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
 | 
			
		||||
 | 
			
		||||
// A SyntaxError suggests that the ASN.1 data is invalid.
 | 
			
		||||
type SyntaxError struct {
 | 
			
		||||
	Msg string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
 | 
			
		||||
 | 
			
		||||
// We start by dealing with each of the primitive types in turn.
 | 
			
		||||
 | 
			
		||||
// BOOLEAN
 | 
			
		||||
 | 
			
		||||
func parseBool(bytes []byte) (ret bool, err error) {
 | 
			
		||||
	if len(bytes) != 1 {
 | 
			
		||||
		err = SyntaxError{"invalid boolean"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// DER demands that "If the encoding represents the boolean value TRUE,
 | 
			
		||||
	// its single contents octet shall have all eight bits set to one."
 | 
			
		||||
	// Thus only 0 and 255 are valid encoded values.
 | 
			
		||||
	switch bytes[0] {
 | 
			
		||||
	case 0:
 | 
			
		||||
		ret = false
 | 
			
		||||
	case 0xff:
 | 
			
		||||
		ret = true
 | 
			
		||||
	default:
 | 
			
		||||
		err = SyntaxError{"invalid boolean"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// INTEGER
 | 
			
		||||
 | 
			
		||||
// parseInt64 treats the given bytes as a big-endian, signed integer and
 | 
			
		||||
// returns the result.
 | 
			
		||||
func parseInt64(bytes []byte) (ret int64, err error) {
 | 
			
		||||
	if len(bytes) > 8 {
 | 
			
		||||
		// We'll overflow an int64 in this case.
 | 
			
		||||
		err = StructuralError{"integer too large"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
 | 
			
		||||
		ret <<= 8
 | 
			
		||||
		ret |= int64(bytes[bytesRead])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Shift up and down in order to sign extend the result.
 | 
			
		||||
	ret <<= 64 - uint8(len(bytes))*8
 | 
			
		||||
	ret >>= 64 - uint8(len(bytes))*8
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseInt treats the given bytes as a big-endian, signed integer and returns
 | 
			
		||||
// the result.
 | 
			
		||||
func parseInt32(bytes []byte) (int32, error) {
 | 
			
		||||
	ret64, err := parseInt64(bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if ret64 != int64(int32(ret64)) {
 | 
			
		||||
		return 0, StructuralError{"integer too large"}
 | 
			
		||||
	}
 | 
			
		||||
	return int32(ret64), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var bigOne = big.NewInt(1)
 | 
			
		||||
 | 
			
		||||
// parseBigInt treats the given bytes as a big-endian, signed integer and returns
 | 
			
		||||
// the result.
 | 
			
		||||
func parseBigInt(bytes []byte) *big.Int {
 | 
			
		||||
	ret := new(big.Int)
 | 
			
		||||
	if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
 | 
			
		||||
		// This is a negative number.
 | 
			
		||||
		notBytes := make([]byte, len(bytes))
 | 
			
		||||
		for i := range notBytes {
 | 
			
		||||
			notBytes[i] = ^bytes[i]
 | 
			
		||||
		}
 | 
			
		||||
		ret.SetBytes(notBytes)
 | 
			
		||||
		ret.Add(ret, bigOne)
 | 
			
		||||
		ret.Neg(ret)
 | 
			
		||||
		return ret
 | 
			
		||||
	}
 | 
			
		||||
	ret.SetBytes(bytes)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BIT STRING
 | 
			
		||||
 | 
			
		||||
// BitString is the structure to use when you want an ASN.1 BIT STRING type. A
 | 
			
		||||
// bit string is padded up to the nearest byte in memory and the number of
 | 
			
		||||
// valid bits is recorded. Padding bits will be zero.
 | 
			
		||||
type BitString struct {
 | 
			
		||||
	Bytes     []byte // bits packed into bytes.
 | 
			
		||||
	BitLength int    // length in bits.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// At returns the bit at the given index. If the index is out of range it
 | 
			
		||||
// returns false.
 | 
			
		||||
func (b BitString) At(i int) int {
 | 
			
		||||
	if i < 0 || i >= b.BitLength {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	x := i / 8
 | 
			
		||||
	y := 7 - uint(i%8)
 | 
			
		||||
	return int(b.Bytes[x]>>y) & 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RightAlign returns a slice where the padding bits are at the beginning. The
 | 
			
		||||
// slice may share memory with the BitString.
 | 
			
		||||
func (b BitString) RightAlign() []byte {
 | 
			
		||||
	shift := uint(8 - (b.BitLength % 8))
 | 
			
		||||
	if shift == 8 || len(b.Bytes) == 0 {
 | 
			
		||||
		return b.Bytes
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	a := make([]byte, len(b.Bytes))
 | 
			
		||||
	a[0] = b.Bytes[0] >> shift
 | 
			
		||||
	for i := 1; i < len(b.Bytes); i++ {
 | 
			
		||||
		a[i] = b.Bytes[i-1] << (8 - shift)
 | 
			
		||||
		a[i] |= b.Bytes[i] >> shift
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return a
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
 | 
			
		||||
func parseBitString(bytes []byte) (ret BitString, err error) {
 | 
			
		||||
	if len(bytes) == 0 {
 | 
			
		||||
		err = SyntaxError{"zero length BIT STRING"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	paddingBits := int(bytes[0])
 | 
			
		||||
	if paddingBits > 7 ||
 | 
			
		||||
		len(bytes) == 1 && paddingBits > 0 ||
 | 
			
		||||
		bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
 | 
			
		||||
		err = SyntaxError{"invalid padding bits in BIT STRING"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ret.BitLength = (len(bytes)-1)*8 - paddingBits
 | 
			
		||||
	ret.Bytes = bytes[1:]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OBJECT IDENTIFIER
 | 
			
		||||
 | 
			
		||||
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
 | 
			
		||||
type ObjectIdentifier []int
 | 
			
		||||
 | 
			
		||||
// Equal reports whether oi and other represent the same identifier.
 | 
			
		||||
func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
 | 
			
		||||
	if len(oi) != len(other) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < len(oi); i++ {
 | 
			
		||||
		if oi[i] != other[i] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
 | 
			
		||||
// returns it. An object identifier is a sequence of variable length integers
 | 
			
		||||
// that are assigned in a hierarchy.
 | 
			
		||||
func parseObjectIdentifier(bytes []byte) (s []int, err error) {
 | 
			
		||||
	if len(bytes) == 0 {
 | 
			
		||||
		err = SyntaxError{"zero length OBJECT IDENTIFIER"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// In the worst case, we get two elements from the first byte (which is
 | 
			
		||||
	// encoded differently) and then every varint is a single byte long.
 | 
			
		||||
	s = make([]int, len(bytes)+1)
 | 
			
		||||
 | 
			
		||||
	// The first varint is 40*value1 + value2:
 | 
			
		||||
	// According to this packing, value1 can take the values 0, 1 and 2 only.
 | 
			
		||||
	// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
 | 
			
		||||
	// then there are no restrictions on value2.
 | 
			
		||||
	v, offset, err := parseBase128Int(bytes, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if v < 80 {
 | 
			
		||||
		s[0] = v / 40
 | 
			
		||||
		s[1] = v % 40
 | 
			
		||||
	} else {
 | 
			
		||||
		s[0] = 2
 | 
			
		||||
		s[1] = v - 80
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := 2
 | 
			
		||||
	for ; offset < len(bytes); i++ {
 | 
			
		||||
		v, offset, err = parseBase128Int(bytes, offset)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		s[i] = v
 | 
			
		||||
	}
 | 
			
		||||
	s = s[0:i]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ENUMERATED
 | 
			
		||||
 | 
			
		||||
// An Enumerated is represented as a plain int.
 | 
			
		||||
type Enumerated int
 | 
			
		||||
 | 
			
		||||
// FLAG
 | 
			
		||||
 | 
			
		||||
// A Flag accepts any data and is set to true if present.
 | 
			
		||||
type Flag bool
 | 
			
		||||
 | 
			
		||||
// parseBase128Int parses a base-128 encoded int from the given offset in the
 | 
			
		||||
// given byte slice. It returns the value and the new offset.
 | 
			
		||||
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
 | 
			
		||||
	offset = initOffset
 | 
			
		||||
	for shifted := 0; offset < len(bytes); shifted++ {
 | 
			
		||||
		if shifted > 4 {
 | 
			
		||||
			err = StructuralError{"base 128 integer too large"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ret <<= 7
 | 
			
		||||
		b := bytes[offset]
 | 
			
		||||
		ret |= int(b & 0x7f)
 | 
			
		||||
		offset++
 | 
			
		||||
		if b&0x80 == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = SyntaxError{"truncated base 128 integer"}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UTCTime
 | 
			
		||||
 | 
			
		||||
func parseUTCTime(bytes []byte) (ret time.Time, err error) {
 | 
			
		||||
	s := string(bytes)
 | 
			
		||||
	ret, err = time.Parse("0601021504Z0700", s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ret, err = time.Parse("060102150405Z0700", s)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil && ret.Year() >= 2050 {
 | 
			
		||||
		// UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
 | 
			
		||||
		ret = ret.AddDate(-100, 0, 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
 | 
			
		||||
// and returns the resulting time.
 | 
			
		||||
func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
 | 
			
		||||
	return time.Parse("20060102150405Z0700", string(bytes))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrintableString
 | 
			
		||||
 | 
			
		||||
// parsePrintableString parses a ASN.1 PrintableString from the given byte
 | 
			
		||||
// array and returns it.
 | 
			
		||||
func parsePrintableString(bytes []byte) (ret string, err error) {
 | 
			
		||||
	for _, b := range bytes {
 | 
			
		||||
		if !isPrintable(b) {
 | 
			
		||||
			err = SyntaxError{"PrintableString contains invalid character"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ret = string(bytes)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isPrintable returns true iff the given b is in the ASN.1 PrintableString set.
 | 
			
		||||
func isPrintable(b byte) bool {
 | 
			
		||||
	return 'a' <= b && b <= 'z' ||
 | 
			
		||||
		'A' <= b && b <= 'Z' ||
 | 
			
		||||
		'0' <= b && b <= '9' ||
 | 
			
		||||
		'\'' <= b && b <= ')' ||
 | 
			
		||||
		'+' <= b && b <= '/' ||
 | 
			
		||||
		b == ' ' ||
 | 
			
		||||
		b == ':' ||
 | 
			
		||||
		b == '=' ||
 | 
			
		||||
		b == '?' ||
 | 
			
		||||
		// This is technically not allowed in a PrintableString.
 | 
			
		||||
		// However, x509 certificates with wildcard strings don't
 | 
			
		||||
		// always use the correct string type so we permit it.
 | 
			
		||||
		b == '*'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IA5String
 | 
			
		||||
 | 
			
		||||
// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
 | 
			
		||||
// byte slice and returns it.
 | 
			
		||||
func parseIA5String(bytes []byte) (ret string, err error) {
 | 
			
		||||
	for _, b := range bytes {
 | 
			
		||||
		if b >= 0x80 {
 | 
			
		||||
			err = SyntaxError{"IA5String contains invalid character"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ret = string(bytes)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// T61String
 | 
			
		||||
 | 
			
		||||
// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
 | 
			
		||||
// byte slice and returns it.
 | 
			
		||||
func parseT61String(bytes []byte) (ret string, err error) {
 | 
			
		||||
	return string(bytes), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UTF8String
 | 
			
		||||
 | 
			
		||||
// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
 | 
			
		||||
// array and returns it.
 | 
			
		||||
func parseUTF8String(bytes []byte) (ret string, err error) {
 | 
			
		||||
	return string(bytes), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A RawValue represents an undecoded ASN.1 object.
 | 
			
		||||
type RawValue struct {
 | 
			
		||||
	Class, Tag int
 | 
			
		||||
	IsCompound bool
 | 
			
		||||
	Bytes      []byte
 | 
			
		||||
	FullBytes  []byte // includes the tag and length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RawContent is used to signal that the undecoded, DER data needs to be
 | 
			
		||||
// preserved for a struct. To use it, the first field of the struct must have
 | 
			
		||||
// this type. It's an error for any of the other fields to have this type.
 | 
			
		||||
type RawContent []byte
 | 
			
		||||
 | 
			
		||||
// Tagging
 | 
			
		||||
 | 
			
		||||
// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
 | 
			
		||||
// into a byte slice. It returns the parsed data and the new offset. SET and
 | 
			
		||||
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
 | 
			
		||||
// don't distinguish between ordered and unordered objects in this code.
 | 
			
		||||
func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err error) {
 | 
			
		||||
	offset = initOffset
 | 
			
		||||
	b := bytes[offset]
 | 
			
		||||
	offset++
 | 
			
		||||
	ret.class = int(b >> 6)
 | 
			
		||||
	ret.isCompound = b&0x20 == 0x20
 | 
			
		||||
	ret.tag = int(b & 0x1f)
 | 
			
		||||
 | 
			
		||||
	// If the bottom five bits are set, then the tag number is actually base 128
 | 
			
		||||
	// encoded afterwards
 | 
			
		||||
	if ret.tag == 0x1f {
 | 
			
		||||
		ret.tag, offset, err = parseBase128Int(bytes, offset)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if offset >= len(bytes) {
 | 
			
		||||
		err = SyntaxError{"truncated tag or length"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	b = bytes[offset]
 | 
			
		||||
	offset++
 | 
			
		||||
	if b&0x80 == 0 {
 | 
			
		||||
		// The length is encoded in the bottom 7 bits.
 | 
			
		||||
		ret.length = int(b & 0x7f)
 | 
			
		||||
	} else {
 | 
			
		||||
		// Bottom 7 bits give the number of length bytes to follow.
 | 
			
		||||
		numBytes := int(b & 0x7f)
 | 
			
		||||
		if numBytes == 0 {
 | 
			
		||||
			err = SyntaxError{"indefinite length found (not DER)"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		ret.length = 0
 | 
			
		||||
		for i := 0; i < numBytes; i++ {
 | 
			
		||||
			if offset >= len(bytes) {
 | 
			
		||||
				err = SyntaxError{"truncated tag or length"}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			b = bytes[offset]
 | 
			
		||||
			offset++
 | 
			
		||||
			if ret.length >= 1<<23 {
 | 
			
		||||
				// We can't shift ret.length up without
 | 
			
		||||
				// overflowing.
 | 
			
		||||
				err = StructuralError{"length too large"}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ret.length <<= 8
 | 
			
		||||
			ret.length |= int(b)
 | 
			
		||||
			if ret.length == 0 {
 | 
			
		||||
				// DER requires that lengths be minimal.
 | 
			
		||||
				err = StructuralError{"superfluous leading zeros in length"}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
 | 
			
		||||
// a number of ASN.1 values from the given byte slice and returns them as a
 | 
			
		||||
// slice of Go values of the given type.
 | 
			
		||||
func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) {
 | 
			
		||||
	expectedTag, compoundType, ok := getUniversalType(elemType)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		err = StructuralError{"unknown Go type for slice"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// First we iterate over the input and count the number of elements,
 | 
			
		||||
	// checking that the types are correct in each case.
 | 
			
		||||
	numElements := 0
 | 
			
		||||
	for offset := 0; offset < len(bytes); {
 | 
			
		||||
		var t tagAndLength
 | 
			
		||||
		t, offset, err = parseTagAndLength(bytes, offset)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		// We pretend that GENERAL STRINGs are PRINTABLE STRINGs so
 | 
			
		||||
		// that a sequence of them can be parsed into a []string.
 | 
			
		||||
		if t.tag == tagGeneralString {
 | 
			
		||||
			t.tag = tagPrintableString
 | 
			
		||||
		}
 | 
			
		||||
		if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
 | 
			
		||||
			err = StructuralError{"sequence tag mismatch"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if invalidLength(offset, t.length, len(bytes)) {
 | 
			
		||||
			err = SyntaxError{"truncated sequence"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		offset += t.length
 | 
			
		||||
		numElements++
 | 
			
		||||
	}
 | 
			
		||||
	ret = reflect.MakeSlice(sliceType, numElements, numElements)
 | 
			
		||||
	params := fieldParameters{}
 | 
			
		||||
	offset := 0
 | 
			
		||||
	for i := 0; i < numElements; i++ {
 | 
			
		||||
		offset, err = parseField(ret.Index(i), bytes, offset, params)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	bitStringType        = reflect.TypeOf(BitString{})
 | 
			
		||||
	objectIdentifierType = reflect.TypeOf(ObjectIdentifier{})
 | 
			
		||||
	enumeratedType       = reflect.TypeOf(Enumerated(0))
 | 
			
		||||
	flagType             = reflect.TypeOf(Flag(false))
 | 
			
		||||
	timeType             = reflect.TypeOf(time.Time{})
 | 
			
		||||
	rawValueType         = reflect.TypeOf(RawValue{})
 | 
			
		||||
	rawContentsType      = reflect.TypeOf(RawContent(nil))
 | 
			
		||||
	bigIntType           = reflect.TypeOf(new(big.Int))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// invalidLength returns true iff offset + length > sliceLength, or if the
 | 
			
		||||
// addition would overflow.
 | 
			
		||||
func invalidLength(offset, length, sliceLength int) bool {
 | 
			
		||||
	return offset+length < offset || offset+length > sliceLength
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// START CT CHANGES
 | 
			
		||||
 | 
			
		||||
// Tests whether the data in |bytes| would be a valid ISO8859-1 string.
 | 
			
		||||
// Clearly, a sequence of bytes comprised solely of valid ISO8859-1
 | 
			
		||||
// codepoints does not imply that the encoding MUST be ISO8859-1, rather that
 | 
			
		||||
// you would not encounter an error trying to interpret the data as such.
 | 
			
		||||
func couldBeISO8859_1(bytes []byte) bool {
 | 
			
		||||
	for _, b := range bytes {
 | 
			
		||||
		if b < 0x20 || (b >= 0x7F && b < 0xA0) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Checks whether the data in |bytes| would be a valid T.61 string.
 | 
			
		||||
// Clearly, a sequence of bytes comprised solely of valid T.61
 | 
			
		||||
// codepoints does not imply that the encoding MUST be T.61, rather that
 | 
			
		||||
// you would not encounter an error trying to interpret the data as such.
 | 
			
		||||
func couldBeT61(bytes []byte) bool {
 | 
			
		||||
	for _, b := range bytes {
 | 
			
		||||
		switch b {
 | 
			
		||||
		case 0x00:
 | 
			
		||||
			// Since we're guessing at (incorrect) encodings for a
 | 
			
		||||
			// PrintableString, we'll err on the side of caution and disallow
 | 
			
		||||
			// strings with a NUL in them, don't want to re-create a PayPal NUL
 | 
			
		||||
			// situation in monitors.
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case 0x23, 0x24, 0x5C, 0x5E, 0x60, 0x7B, 0x7D, 0x7E, 0xA5, 0xA6, 0xAC, 0xAD, 0xAE, 0xAF,
 | 
			
		||||
			0xB9, 0xBA, 0xC0, 0xC9, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
 | 
			
		||||
			0xDA, 0xDB, 0xDC, 0xDE, 0xDF, 0xE5, 0xFF:
 | 
			
		||||
			// These are all invalid code points in T.61, so it can't be a T.61 string.
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Converts the data in |bytes| to the equivalent UTF-8 string.
 | 
			
		||||
func iso8859_1ToUTF8(bytes []byte) string {
 | 
			
		||||
	buf := make([]rune, len(bytes))
 | 
			
		||||
	for i, b := range bytes {
 | 
			
		||||
		buf[i] = rune(b)
 | 
			
		||||
	}
 | 
			
		||||
	return string(buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// END CT CHANGES
 | 
			
		||||
 | 
			
		||||
// parseField is the main parsing function. Given a byte slice and an offset
 | 
			
		||||
// into the array, it will try to parse a suitable ASN.1 value out and store it
 | 
			
		||||
// in the given Value.
 | 
			
		||||
func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err error) {
 | 
			
		||||
	offset = initOffset
 | 
			
		||||
	fieldType := v.Type()
 | 
			
		||||
 | 
			
		||||
	// If we have run out of data, it may be that there are optional elements at the end.
 | 
			
		||||
	if offset == len(bytes) {
 | 
			
		||||
		if !setDefaultValue(v, params) {
 | 
			
		||||
			err = SyntaxError{"sequence truncated"}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Deal with raw values.
 | 
			
		||||
	if fieldType == rawValueType {
 | 
			
		||||
		var t tagAndLength
 | 
			
		||||
		t, offset, err = parseTagAndLength(bytes, offset)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if invalidLength(offset, t.length, len(bytes)) {
 | 
			
		||||
			err = SyntaxError{"data truncated"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]}
 | 
			
		||||
		offset += t.length
 | 
			
		||||
		v.Set(reflect.ValueOf(result))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Deal with the ANY type.
 | 
			
		||||
	if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
 | 
			
		||||
		var t tagAndLength
 | 
			
		||||
		t, offset, err = parseTagAndLength(bytes, offset)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if invalidLength(offset, t.length, len(bytes)) {
 | 
			
		||||
			err = SyntaxError{"data truncated"}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		var result interface{}
 | 
			
		||||
		if !t.isCompound && t.class == classUniversal {
 | 
			
		||||
			innerBytes := bytes[offset : offset+t.length]
 | 
			
		||||
			switch t.tag {
 | 
			
		||||
			case tagPrintableString:
 | 
			
		||||
				result, err = parsePrintableString(innerBytes)
 | 
			
		||||
				// START CT CHANGES
 | 
			
		||||
				if err != nil && strings.Contains(err.Error(), "PrintableString contains invalid character") {
 | 
			
		||||
					// Probably an ISO8859-1 string stuffed in, check if it
 | 
			
		||||
					// would be valid and assume that's what's happened if so,
 | 
			
		||||
					// otherwise try T.61, failing that give up and just assign
 | 
			
		||||
					// the bytes
 | 
			
		||||
					switch {
 | 
			
		||||
					case couldBeISO8859_1(innerBytes):
 | 
			
		||||
						result, err = iso8859_1ToUTF8(innerBytes), nil
 | 
			
		||||
					case couldBeT61(innerBytes):
 | 
			
		||||
						result, err = parseT61String(innerBytes)
 | 
			
		||||
					default:
 | 
			
		||||
						result = nil
 | 
			
		||||
						err = errors.New("PrintableString contains invalid character, but couldn't determine correct String type.")
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				// END CT CHANGES
 | 
			
		||||
			case tagIA5String:
 | 
			
		||||
				result, err = parseIA5String(innerBytes)
 | 
			
		||||
			case tagT61String:
 | 
			
		||||
				result, err = parseT61String(innerBytes)
 | 
			
		||||
			case tagUTF8String:
 | 
			
		||||
				result, err = parseUTF8String(innerBytes)
 | 
			
		||||
			case tagInteger:
 | 
			
		||||
				result, err = parseInt64(innerBytes)
 | 
			
		||||
			case tagBitString:
 | 
			
		||||
				result, err = parseBitString(innerBytes)
 | 
			
		||||
			case tagOID:
 | 
			
		||||
				result, err = parseObjectIdentifier(innerBytes)
 | 
			
		||||
			case tagUTCTime:
 | 
			
		||||
				result, err = parseUTCTime(innerBytes)
 | 
			
		||||
			case tagOctetString:
 | 
			
		||||
				result = innerBytes
 | 
			
		||||
			default:
 | 
			
		||||
				// If we don't know how to handle the type, we just leave Value as nil.
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		offset += t.length
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if result != nil {
 | 
			
		||||
			v.Set(reflect.ValueOf(result))
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	universalTag, compoundType, ok1 := getUniversalType(fieldType)
 | 
			
		||||
	if !ok1 {
 | 
			
		||||
		err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t, offset, err := parseTagAndLength(bytes, offset)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if params.explicit {
 | 
			
		||||
		expectedClass := classContextSpecific
 | 
			
		||||
		if params.application {
 | 
			
		||||
			expectedClass = classApplication
 | 
			
		||||
		}
 | 
			
		||||
		if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
 | 
			
		||||
			if t.length > 0 {
 | 
			
		||||
				t, offset, err = parseTagAndLength(bytes, offset)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if fieldType != flagType {
 | 
			
		||||
					err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				v.SetBool(true)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// The tags didn't match, it might be an optional element.
 | 
			
		||||
			ok := setDefaultValue(v, params)
 | 
			
		||||
			if ok {
 | 
			
		||||
				offset = initOffset
 | 
			
		||||
			} else {
 | 
			
		||||
				err = StructuralError{"explicitly tagged member didn't match"}
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Special case for strings: all the ASN.1 string types map to the Go
 | 
			
		||||
	// type string. getUniversalType returns the tag for PrintableString
 | 
			
		||||
	// when it sees a string, so if we see a different string type on the
 | 
			
		||||
	// wire, we change the universal type to match.
 | 
			
		||||
	if universalTag == tagPrintableString {
 | 
			
		||||
		switch t.tag {
 | 
			
		||||
		case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
 | 
			
		||||
			universalTag = t.tag
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Special case for time: UTCTime and GeneralizedTime both map to the
 | 
			
		||||
	// Go type time.Time.
 | 
			
		||||
	if universalTag == tagUTCTime && t.tag == tagGeneralizedTime {
 | 
			
		||||
		universalTag = tagGeneralizedTime
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expectedClass := classUniversal
 | 
			
		||||
	expectedTag := universalTag
 | 
			
		||||
 | 
			
		||||
	if !params.explicit && params.tag != nil {
 | 
			
		||||
		expectedClass = classContextSpecific
 | 
			
		||||
		expectedTag = *params.tag
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !params.explicit && params.application && params.tag != nil {
 | 
			
		||||
		expectedClass = classApplication
 | 
			
		||||
		expectedTag = *params.tag
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We have unwrapped any explicit tagging at this point.
 | 
			
		||||
	if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
 | 
			
		||||
		// Tags don't match. Again, it could be an optional element.
 | 
			
		||||
		ok := setDefaultValue(v, params)
 | 
			
		||||
		if ok {
 | 
			
		||||
			offset = initOffset
 | 
			
		||||
		} else {
 | 
			
		||||
			err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if invalidLength(offset, t.length, len(bytes)) {
 | 
			
		||||
		err = SyntaxError{"data truncated"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	innerBytes := bytes[offset : offset+t.length]
 | 
			
		||||
	offset += t.length
 | 
			
		||||
 | 
			
		||||
	// We deal with the structures defined in this package first.
 | 
			
		||||
	switch fieldType {
 | 
			
		||||
	case objectIdentifierType:
 | 
			
		||||
		newSlice, err1 := parseObjectIdentifier(innerBytes)
 | 
			
		||||
		v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice)))
 | 
			
		||||
		if err1 == nil {
 | 
			
		||||
			reflect.Copy(v, reflect.ValueOf(newSlice))
 | 
			
		||||
		}
 | 
			
		||||
		err = err1
 | 
			
		||||
		return
 | 
			
		||||
	case bitStringType:
 | 
			
		||||
		bs, err1 := parseBitString(innerBytes)
 | 
			
		||||
		if err1 == nil {
 | 
			
		||||
			v.Set(reflect.ValueOf(bs))
 | 
			
		||||
		}
 | 
			
		||||
		err = err1
 | 
			
		||||
		return
 | 
			
		||||
	case timeType:
 | 
			
		||||
		var time time.Time
 | 
			
		||||
		var err1 error
 | 
			
		||||
		if universalTag == tagUTCTime {
 | 
			
		||||
			time, err1 = parseUTCTime(innerBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			time, err1 = parseGeneralizedTime(innerBytes)
 | 
			
		||||
		}
 | 
			
		||||
		if err1 == nil {
 | 
			
		||||
			v.Set(reflect.ValueOf(time))
 | 
			
		||||
		}
 | 
			
		||||
		err = err1
 | 
			
		||||
		return
 | 
			
		||||
	case enumeratedType:
 | 
			
		||||
		parsedInt, err1 := parseInt32(innerBytes)
 | 
			
		||||
		if err1 == nil {
 | 
			
		||||
			v.SetInt(int64(parsedInt))
 | 
			
		||||
		}
 | 
			
		||||
		err = err1
 | 
			
		||||
		return
 | 
			
		||||
	case flagType:
 | 
			
		||||
		v.SetBool(true)
 | 
			
		||||
		return
 | 
			
		||||
	case bigIntType:
 | 
			
		||||
		parsedInt := parseBigInt(innerBytes)
 | 
			
		||||
		v.Set(reflect.ValueOf(parsedInt))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	switch val := v; val.Kind() {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		parsedBool, err1 := parseBool(innerBytes)
 | 
			
		||||
		if err1 == nil {
 | 
			
		||||
			val.SetBool(parsedBool)
 | 
			
		||||
		}
 | 
			
		||||
		err = err1
 | 
			
		||||
		return
 | 
			
		||||
	case reflect.Int, reflect.Int32, reflect.Int64:
 | 
			
		||||
		if val.Type().Size() == 4 {
 | 
			
		||||
			parsedInt, err1 := parseInt32(innerBytes)
 | 
			
		||||
			if err1 == nil {
 | 
			
		||||
				val.SetInt(int64(parsedInt))
 | 
			
		||||
			}
 | 
			
		||||
			err = err1
 | 
			
		||||
		} else {
 | 
			
		||||
			parsedInt, err1 := parseInt64(innerBytes)
 | 
			
		||||
			if err1 == nil {
 | 
			
		||||
				val.SetInt(parsedInt)
 | 
			
		||||
			}
 | 
			
		||||
			err = err1
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	// TODO(dfc) Add support for the remaining integer types
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		structType := fieldType
 | 
			
		||||
 | 
			
		||||
		if structType.NumField() > 0 &&
 | 
			
		||||
			structType.Field(0).Type == rawContentsType {
 | 
			
		||||
			bytes := bytes[initOffset:offset]
 | 
			
		||||
			val.Field(0).Set(reflect.ValueOf(RawContent(bytes)))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		innerOffset := 0
 | 
			
		||||
		for i := 0; i < structType.NumField(); i++ {
 | 
			
		||||
			field := structType.Field(i)
 | 
			
		||||
			if i == 0 && field.Type == rawContentsType {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// We allow extra bytes at the end of the SEQUENCE because
 | 
			
		||||
		// adding elements to the end has been used in X.509 as the
 | 
			
		||||
		// version numbers have increased.
 | 
			
		||||
		return
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		sliceType := fieldType
 | 
			
		||||
		if sliceType.Elem().Kind() == reflect.Uint8 {
 | 
			
		||||
			val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes)))
 | 
			
		||||
			reflect.Copy(val, reflect.ValueOf(innerBytes))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem())
 | 
			
		||||
		if err1 == nil {
 | 
			
		||||
			val.Set(newSlice)
 | 
			
		||||
		}
 | 
			
		||||
		err = err1
 | 
			
		||||
		return
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		var v string
 | 
			
		||||
		switch universalTag {
 | 
			
		||||
		case tagPrintableString:
 | 
			
		||||
			v, err = parsePrintableString(innerBytes)
 | 
			
		||||
		case tagIA5String:
 | 
			
		||||
			v, err = parseIA5String(innerBytes)
 | 
			
		||||
		case tagT61String:
 | 
			
		||||
			v, err = parseT61String(innerBytes)
 | 
			
		||||
		case tagUTF8String:
 | 
			
		||||
			v, err = parseUTF8String(innerBytes)
 | 
			
		||||
		case tagGeneralString:
 | 
			
		||||
			// GeneralString is specified in ISO-2022/ECMA-35,
 | 
			
		||||
			// A brief review suggests that it includes structures
 | 
			
		||||
			// that allow the encoding to change midstring and
 | 
			
		||||
			// such. We give up and pass it as an 8-bit string.
 | 
			
		||||
			v, err = parseT61String(innerBytes)
 | 
			
		||||
		default:
 | 
			
		||||
			err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			val.SetString(v)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = StructuralError{"unsupported: " + v.Type().String()}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// setDefaultValue is used to install a default value, from a tag string, into
 | 
			
		||||
// a Value. It is successful is the field was optional, even if a default value
 | 
			
		||||
// wasn't provided or it failed to install it into the Value.
 | 
			
		||||
func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
 | 
			
		||||
	if !params.optional {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ok = true
 | 
			
		||||
	if params.defaultValue == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	switch val := v; val.Kind() {
 | 
			
		||||
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
		val.SetInt(*params.defaultValue)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unmarshal parses the DER-encoded ASN.1 data structure b
 | 
			
		||||
// and uses the reflect package to fill in an arbitrary value pointed at by val.
 | 
			
		||||
// Because Unmarshal uses the reflect package, the structs
 | 
			
		||||
// being written to must use upper case field names.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 INTEGER can be written to an int, int32, int64,
 | 
			
		||||
// or *big.Int (from the math/big package).
 | 
			
		||||
// If the encoded value does not fit in the Go type,
 | 
			
		||||
// Unmarshal returns a parse error.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 BIT STRING can be written to a BitString.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 OCTET STRING can be written to a []byte.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 OBJECT IDENTIFIER can be written to an
 | 
			
		||||
// ObjectIdentifier.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 ENUMERATED can be written to an Enumerated.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 PrintableString or IA5String can be written to a string.
 | 
			
		||||
//
 | 
			
		||||
// Any of the above ASN.1 values can be written to an interface{}.
 | 
			
		||||
// The value stored in the interface has the corresponding Go type.
 | 
			
		||||
// For integers, that type is int64.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 SEQUENCE OF x or SET OF x can be written
 | 
			
		||||
// to a slice if an x can be written to the slice's element type.
 | 
			
		||||
//
 | 
			
		||||
// An ASN.1 SEQUENCE or SET can be written to a struct
 | 
			
		||||
// if each of the elements in the sequence can be
 | 
			
		||||
// written to the corresponding element in the struct.
 | 
			
		||||
//
 | 
			
		||||
// The following tags on struct fields have special meaning to Unmarshal:
 | 
			
		||||
//
 | 
			
		||||
//	optional		marks the field as ASN.1 OPTIONAL
 | 
			
		||||
//	[explicit] tag:x	specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
 | 
			
		||||
//	default:x		sets the default value for optional integer fields
 | 
			
		||||
//
 | 
			
		||||
// If the type of the first field of a structure is RawContent then the raw
 | 
			
		||||
// ASN1 contents of the struct will be stored in it.
 | 
			
		||||
//
 | 
			
		||||
// Other ASN.1 types are not supported; if it encounters them,
 | 
			
		||||
// Unmarshal returns a parse error.
 | 
			
		||||
func Unmarshal(b []byte, val interface{}) (rest []byte, err error) {
 | 
			
		||||
	return UnmarshalWithParams(b, val, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalWithParams allows field parameters to be specified for the
 | 
			
		||||
// top-level element. The form of the params is the same as the field tags.
 | 
			
		||||
func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err error) {
 | 
			
		||||
	v := reflect.ValueOf(val).Elem()
 | 
			
		||||
	offset, err := parseField(v, b, 0, parseFieldParameters(params))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return b[offset:], nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										790
									
								
								ct/asn1/asn1_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										790
									
								
								ct/asn1/asn1_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,790 @@
 | 
			
		||||
// 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 LICENSE file.
 | 
			
		||||
 | 
			
		||||
package asn1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type boolTest struct {
 | 
			
		||||
	in  []byte
 | 
			
		||||
	ok  bool
 | 
			
		||||
	out bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var boolTestData = []boolTest{
 | 
			
		||||
	{[]byte{0x00}, true, false},
 | 
			
		||||
	{[]byte{0xff}, true, true},
 | 
			
		||||
	{[]byte{0x00, 0x00}, false, false},
 | 
			
		||||
	{[]byte{0xff, 0xff}, false, false},
 | 
			
		||||
	{[]byte{0x01}, false, false},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseBool(t *testing.T) {
 | 
			
		||||
	for i, test := range boolTestData {
 | 
			
		||||
		ret, err := parseBool(test.in)
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if test.ok && ret != test.out {
 | 
			
		||||
			t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type int64Test struct {
 | 
			
		||||
	in  []byte
 | 
			
		||||
	ok  bool
 | 
			
		||||
	out int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var int64TestData = []int64Test{
 | 
			
		||||
	{[]byte{0x00}, true, 0},
 | 
			
		||||
	{[]byte{0x7f}, true, 127},
 | 
			
		||||
	{[]byte{0x00, 0x80}, true, 128},
 | 
			
		||||
	{[]byte{0x01, 0x00}, true, 256},
 | 
			
		||||
	{[]byte{0x80}, true, -128},
 | 
			
		||||
	{[]byte{0xff, 0x7f}, true, -129},
 | 
			
		||||
	{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1},
 | 
			
		||||
	{[]byte{0xff}, true, -1},
 | 
			
		||||
	{[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808},
 | 
			
		||||
	{[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseInt64(t *testing.T) {
 | 
			
		||||
	for i, test := range int64TestData {
 | 
			
		||||
		ret, err := parseInt64(test.in)
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if test.ok && ret != test.out {
 | 
			
		||||
			t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type int32Test struct {
 | 
			
		||||
	in  []byte
 | 
			
		||||
	ok  bool
 | 
			
		||||
	out int32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var int32TestData = []int32Test{
 | 
			
		||||
	{[]byte{0x00}, true, 0},
 | 
			
		||||
	{[]byte{0x7f}, true, 127},
 | 
			
		||||
	{[]byte{0x00, 0x80}, true, 128},
 | 
			
		||||
	{[]byte{0x01, 0x00}, true, 256},
 | 
			
		||||
	{[]byte{0x80}, true, -128},
 | 
			
		||||
	{[]byte{0xff, 0x7f}, true, -129},
 | 
			
		||||
	{[]byte{0xff, 0xff, 0xff, 0xff}, true, -1},
 | 
			
		||||
	{[]byte{0xff}, true, -1},
 | 
			
		||||
	{[]byte{0x80, 0x00, 0x00, 0x00}, true, -2147483648},
 | 
			
		||||
	{[]byte{0x80, 0x00, 0x00, 0x00, 0x00}, false, 0},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseInt32(t *testing.T) {
 | 
			
		||||
	for i, test := range int32TestData {
 | 
			
		||||
		ret, err := parseInt32(test.in)
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if test.ok && int32(ret) != test.out {
 | 
			
		||||
			t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var bigIntTests = []struct {
 | 
			
		||||
	in     []byte
 | 
			
		||||
	base10 string
 | 
			
		||||
}{
 | 
			
		||||
	{[]byte{0xff}, "-1"},
 | 
			
		||||
	{[]byte{0x00}, "0"},
 | 
			
		||||
	{[]byte{0x01}, "1"},
 | 
			
		||||
	{[]byte{0x00, 0xff}, "255"},
 | 
			
		||||
	{[]byte{0xff, 0x00}, "-256"},
 | 
			
		||||
	{[]byte{0x01, 0x00}, "256"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseBigInt(t *testing.T) {
 | 
			
		||||
	for i, test := range bigIntTests {
 | 
			
		||||
		ret := parseBigInt(test.in)
 | 
			
		||||
		if ret.String() != test.base10 {
 | 
			
		||||
			t.Errorf("#%d: bad result from %x, got %s want %s", i, test.in, ret.String(), test.base10)
 | 
			
		||||
		}
 | 
			
		||||
		fw := newForkableWriter()
 | 
			
		||||
		marshalBigInt(fw, ret)
 | 
			
		||||
		result := fw.Bytes()
 | 
			
		||||
		if !bytes.Equal(result, test.in) {
 | 
			
		||||
			t.Errorf("#%d: got %x from marshaling %s, want %x", i, result, ret, test.in)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type bitStringTest struct {
 | 
			
		||||
	in        []byte
 | 
			
		||||
	ok        bool
 | 
			
		||||
	out       []byte
 | 
			
		||||
	bitLength int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var bitStringTestData = []bitStringTest{
 | 
			
		||||
	{[]byte{}, false, []byte{}, 0},
 | 
			
		||||
	{[]byte{0x00}, true, []byte{}, 0},
 | 
			
		||||
	{[]byte{0x07, 0x00}, true, []byte{0x00}, 1},
 | 
			
		||||
	{[]byte{0x07, 0x01}, false, []byte{}, 0},
 | 
			
		||||
	{[]byte{0x07, 0x40}, false, []byte{}, 0},
 | 
			
		||||
	{[]byte{0x08, 0x00}, false, []byte{}, 0},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBitString(t *testing.T) {
 | 
			
		||||
	for i, test := range bitStringTestData {
 | 
			
		||||
		ret, err := parseBitString(test.in)
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			if test.bitLength != ret.BitLength || !bytes.Equal(ret.Bytes, test.out) {
 | 
			
		||||
				t.Errorf("#%d: Bad result: %v (expected %v %v)", i, ret, test.out, test.bitLength)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBitStringAt(t *testing.T) {
 | 
			
		||||
	bs := BitString{[]byte{0x82, 0x40}, 16}
 | 
			
		||||
	if bs.At(0) != 1 {
 | 
			
		||||
		t.Error("#1: Failed")
 | 
			
		||||
	}
 | 
			
		||||
	if bs.At(1) != 0 {
 | 
			
		||||
		t.Error("#2: Failed")
 | 
			
		||||
	}
 | 
			
		||||
	if bs.At(6) != 1 {
 | 
			
		||||
		t.Error("#3: Failed")
 | 
			
		||||
	}
 | 
			
		||||
	if bs.At(9) != 1 {
 | 
			
		||||
		t.Error("#4: Failed")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type bitStringRightAlignTest struct {
 | 
			
		||||
	in    []byte
 | 
			
		||||
	inlen int
 | 
			
		||||
	out   []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var bitStringRightAlignTests = []bitStringRightAlignTest{
 | 
			
		||||
	{[]byte{0x80}, 1, []byte{0x01}},
 | 
			
		||||
	{[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}},
 | 
			
		||||
	{[]byte{}, 0, []byte{}},
 | 
			
		||||
	{[]byte{0xce}, 8, []byte{0xce}},
 | 
			
		||||
	{[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}},
 | 
			
		||||
	{[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBitStringRightAlign(t *testing.T) {
 | 
			
		||||
	for i, test := range bitStringRightAlignTests {
 | 
			
		||||
		bs := BitString{test.in, test.inlen}
 | 
			
		||||
		out := bs.RightAlign()
 | 
			
		||||
		if !bytes.Equal(out, test.out) {
 | 
			
		||||
			t.Errorf("#%d got: %x want: %x", i, out, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type objectIdentifierTest struct {
 | 
			
		||||
	in  []byte
 | 
			
		||||
	ok  bool
 | 
			
		||||
	out []int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var objectIdentifierTestData = []objectIdentifierTest{
 | 
			
		||||
	{[]byte{}, false, []int{}},
 | 
			
		||||
	{[]byte{85}, true, []int{2, 5}},
 | 
			
		||||
	{[]byte{85, 0x02}, true, []int{2, 5, 2}},
 | 
			
		||||
	{[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}},
 | 
			
		||||
	{[]byte{0x81, 0x34, 0x03}, true, []int{2, 100, 3}},
 | 
			
		||||
	{[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestObjectIdentifier(t *testing.T) {
 | 
			
		||||
	for i, test := range objectIdentifierTestData {
 | 
			
		||||
		ret, err := parseObjectIdentifier(test.in)
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			if !reflect.DeepEqual(test.out, ret) {
 | 
			
		||||
				t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type timeTest struct {
 | 
			
		||||
	in  string
 | 
			
		||||
	ok  bool
 | 
			
		||||
	out time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var utcTestData = []timeTest{
 | 
			
		||||
	{"910506164540-0700", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", -7*60*60))},
 | 
			
		||||
	{"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))},
 | 
			
		||||
	{"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)},
 | 
			
		||||
	{"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)},
 | 
			
		||||
	{"a10506234540Z", false, time.Time{}},
 | 
			
		||||
	{"91a506234540Z", false, time.Time{}},
 | 
			
		||||
	{"9105a6234540Z", false, time.Time{}},
 | 
			
		||||
	{"910506a34540Z", false, time.Time{}},
 | 
			
		||||
	{"910506334a40Z", false, time.Time{}},
 | 
			
		||||
	{"91050633444aZ", false, time.Time{}},
 | 
			
		||||
	{"910506334461Z", false, time.Time{}},
 | 
			
		||||
	{"910506334400Za", false, time.Time{}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUTCTime(t *testing.T) {
 | 
			
		||||
	for i, test := range utcTestData {
 | 
			
		||||
		ret, err := parseUTCTime([]byte(test.in))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if test.ok {
 | 
			
		||||
				t.Errorf("#%d: parseUTCTime(%q) = error %v", i, test.in, err)
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !test.ok {
 | 
			
		||||
			t.Errorf("#%d: parseUTCTime(%q) succeeded, should have failed", i, test.in)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		const format = "Jan _2 15:04:05 -0700 2006" // ignore zone name, just offset
 | 
			
		||||
		have := ret.Format(format)
 | 
			
		||||
		want := test.out.Format(format)
 | 
			
		||||
		if have != want {
 | 
			
		||||
			t.Errorf("#%d: parseUTCTime(%q) = %s, want %s", i, test.in, have, want)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var generalizedTimeTestData = []timeTest{
 | 
			
		||||
	{"20100102030405Z", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.UTC)},
 | 
			
		||||
	{"20100102030405", false, time.Time{}},
 | 
			
		||||
	{"20100102030405+0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", 6*60*60+7*60))},
 | 
			
		||||
	{"20100102030405-0607", true, time.Date(2010, 01, 02, 03, 04, 05, 0, time.FixedZone("", -6*60*60-7*60))},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGeneralizedTime(t *testing.T) {
 | 
			
		||||
	for i, test := range generalizedTimeTestData {
 | 
			
		||||
		ret, err := parseGeneralizedTime([]byte(test.in))
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			if !reflect.DeepEqual(test.out, ret) {
 | 
			
		||||
				t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tagAndLengthTest struct {
 | 
			
		||||
	in  []byte
 | 
			
		||||
	ok  bool
 | 
			
		||||
	out tagAndLength
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tagAndLengthData = []tagAndLengthTest{
 | 
			
		||||
	{[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}},
 | 
			
		||||
	{[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}},
 | 
			
		||||
	{[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}},
 | 
			
		||||
	{[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}},
 | 
			
		||||
	{[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}},
 | 
			
		||||
	{[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}},
 | 
			
		||||
	{[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}},
 | 
			
		||||
	{[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}},
 | 
			
		||||
	{[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}},
 | 
			
		||||
	{[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}},
 | 
			
		||||
	{[]byte{0x1f, 0x85}, false, tagAndLength{}},
 | 
			
		||||
	{[]byte{0x30, 0x80}, false, tagAndLength{}},
 | 
			
		||||
	// Superfluous zeros in the length should be an error.
 | 
			
		||||
	{[]byte{0xa0, 0x82, 0x00, 0x01}, false, tagAndLength{}},
 | 
			
		||||
	// Lengths up to the maximum size of an int should work.
 | 
			
		||||
	{[]byte{0xa0, 0x84, 0x7f, 0xff, 0xff, 0xff}, true, tagAndLength{2, 0, 0x7fffffff, true}},
 | 
			
		||||
	// Lengths that would overflow an int should be rejected.
 | 
			
		||||
	{[]byte{0xa0, 0x84, 0x80, 0x00, 0x00, 0x00}, false, tagAndLength{}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseTagAndLength(t *testing.T) {
 | 
			
		||||
	for i, test := range tagAndLengthData {
 | 
			
		||||
		tagAndLength, _, err := parseTagAndLength(test.in, 0)
 | 
			
		||||
		if (err == nil) != test.ok {
 | 
			
		||||
			t.Errorf("#%d: Incorrect error result (did pass? %v, expected: %v)", i, err == nil, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil && !reflect.DeepEqual(test.out, tagAndLength) {
 | 
			
		||||
			t.Errorf("#%d: Bad result: %v (expected %v)", i, tagAndLength, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type parseFieldParametersTest struct {
 | 
			
		||||
	in  string
 | 
			
		||||
	out fieldParameters
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newInt(n int) *int { return &n }
 | 
			
		||||
 | 
			
		||||
func newInt64(n int64) *int64 { return &n }
 | 
			
		||||
 | 
			
		||||
func newString(s string) *string { return &s }
 | 
			
		||||
 | 
			
		||||
func newBool(b bool) *bool { return &b }
 | 
			
		||||
 | 
			
		||||
var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{
 | 
			
		||||
	{"", fieldParameters{}},
 | 
			
		||||
	{"ia5", fieldParameters{stringType: tagIA5String}},
 | 
			
		||||
	{"printable", fieldParameters{stringType: tagPrintableString}},
 | 
			
		||||
	{"optional", fieldParameters{optional: true}},
 | 
			
		||||
	{"explicit", fieldParameters{explicit: true, tag: new(int)}},
 | 
			
		||||
	{"application", fieldParameters{application: true, tag: new(int)}},
 | 
			
		||||
	{"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
 | 
			
		||||
	{"default:42", fieldParameters{defaultValue: newInt64(42)}},
 | 
			
		||||
	{"tag:17", fieldParameters{tag: newInt(17)}},
 | 
			
		||||
	{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
 | 
			
		||||
	{"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false, false}},
 | 
			
		||||
	{"set", fieldParameters{set: true}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseFieldParameters(t *testing.T) {
 | 
			
		||||
	for i, test := range parseFieldParametersTestData {
 | 
			
		||||
		f := parseFieldParameters(test.in)
 | 
			
		||||
		if !reflect.DeepEqual(f, test.out) {
 | 
			
		||||
			t.Errorf("#%d: Bad result: %v (expected %v)", i, f, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TestObjectIdentifierStruct struct {
 | 
			
		||||
	OID ObjectIdentifier
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TestContextSpecificTags struct {
 | 
			
		||||
	A int `asn1:"tag:1"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TestContextSpecificTags2 struct {
 | 
			
		||||
	A int `asn1:"explicit,tag:1"`
 | 
			
		||||
	B int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TestElementsAfterString struct {
 | 
			
		||||
	S    string
 | 
			
		||||
	A, B int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TestBigInt struct {
 | 
			
		||||
	X *big.Int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var unmarshalTestData = []struct {
 | 
			
		||||
	in  []byte
 | 
			
		||||
	out interface{}
 | 
			
		||||
}{
 | 
			
		||||
	{[]byte{0x02, 0x01, 0x42}, newInt(0x42)},
 | 
			
		||||
	{[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}},
 | 
			
		||||
	{[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}},
 | 
			
		||||
	{[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}},
 | 
			
		||||
	{[]byte{0x02, 0x01, 0x10}, newInt(16)},
 | 
			
		||||
	{[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")},
 | 
			
		||||
	{[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")},
 | 
			
		||||
	{[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}},
 | 
			
		||||
	{[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}},
 | 
			
		||||
	{[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}},
 | 
			
		||||
	{[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}},
 | 
			
		||||
	{[]byte{0x01, 0x01, 0x00}, newBool(false)},
 | 
			
		||||
	{[]byte{0x01, 0x01, 0xff}, newBool(true)},
 | 
			
		||||
	{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
 | 
			
		||||
	{[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUnmarshal(t *testing.T) {
 | 
			
		||||
	for i, test := range unmarshalTestData {
 | 
			
		||||
		pv := reflect.New(reflect.TypeOf(test.out).Elem())
 | 
			
		||||
		val := pv.Interface()
 | 
			
		||||
		_, err := Unmarshal(test.in, val)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("Unmarshal failed at index %d %v", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		if !reflect.DeepEqual(val, test.out) {
 | 
			
		||||
			t.Errorf("#%d:\nhave %#v\nwant %#v", i, val, test.out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Certificate struct {
 | 
			
		||||
	TBSCertificate     TBSCertificate
 | 
			
		||||
	SignatureAlgorithm AlgorithmIdentifier
 | 
			
		||||
	SignatureValue     BitString
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TBSCertificate struct {
 | 
			
		||||
	Version            int `asn1:"optional,explicit,default:0,tag:0"`
 | 
			
		||||
	SerialNumber       RawValue
 | 
			
		||||
	SignatureAlgorithm AlgorithmIdentifier
 | 
			
		||||
	Issuer             RDNSequence
 | 
			
		||||
	Validity           Validity
 | 
			
		||||
	Subject            RDNSequence
 | 
			
		||||
	PublicKey          PublicKeyInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AlgorithmIdentifier struct {
 | 
			
		||||
	Algorithm ObjectIdentifier
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RDNSequence []RelativeDistinguishedNameSET
 | 
			
		||||
 | 
			
		||||
type RelativeDistinguishedNameSET []AttributeTypeAndValue
 | 
			
		||||
 | 
			
		||||
type AttributeTypeAndValue struct {
 | 
			
		||||
	Type  ObjectIdentifier
 | 
			
		||||
	Value interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Validity struct {
 | 
			
		||||
	NotBefore, NotAfter time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PublicKeyInfo struct {
 | 
			
		||||
	Algorithm AlgorithmIdentifier
 | 
			
		||||
	PublicKey BitString
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCertificate(t *testing.T) {
 | 
			
		||||
	// This is a minimal, self-signed certificate that should parse correctly.
 | 
			
		||||
	var cert Certificate
 | 
			
		||||
	if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil {
 | 
			
		||||
		t.Errorf("Unmarshal failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) {
 | 
			
		||||
		t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCertificateWithNUL(t *testing.T) {
 | 
			
		||||
	// This is the paypal NUL-hack certificate. It should fail to parse because
 | 
			
		||||
	// NUL isn't a permitted character in a PrintableString.
 | 
			
		||||
 | 
			
		||||
	var cert Certificate
 | 
			
		||||
	if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil {
 | 
			
		||||
		t.Error("Unmarshal succeeded, should not have")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rawStructTest struct {
 | 
			
		||||
	Raw RawContent
 | 
			
		||||
	A   int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRawStructs(t *testing.T) {
 | 
			
		||||
	var s rawStructTest
 | 
			
		||||
	input := []byte{0x30, 0x03, 0x02, 0x01, 0x50}
 | 
			
		||||
 | 
			
		||||
	rest, err := Unmarshal(input, &s)
 | 
			
		||||
	if len(rest) != 0 {
 | 
			
		||||
		t.Errorf("incomplete parse: %x", rest)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if s.A != 0x50 {
 | 
			
		||||
		t.Errorf("bad value for A: got %d want %d", s.A, 0x50)
 | 
			
		||||
	}
 | 
			
		||||
	if !bytes.Equal([]byte(s.Raw), input) {
 | 
			
		||||
		t.Errorf("bad value for Raw: got %x want %x", s.Raw, input)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCouleBeISO8859_1(t *testing.T) {
 | 
			
		||||
	for i := 0; i < 0xff; i++ {
 | 
			
		||||
		b := []byte("StringWithA")
 | 
			
		||||
		b = append(b, byte(i))
 | 
			
		||||
		switch {
 | 
			
		||||
		// These values are disallowed:
 | 
			
		||||
		case i < 0x20, i >= 0x7f && i < 0xa0:
 | 
			
		||||
			if couldBeISO8859_1(b) {
 | 
			
		||||
				t.Fatalf("Allowed invalid value %d", i)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		// These values are allowed:
 | 
			
		||||
		case i >= 0x20 && i < 0x7f, i >= 0xa0 && i <= 0xff:
 | 
			
		||||
			if !couldBeISO8859_1(b) {
 | 
			
		||||
				t.Fatalf("Disallowed valid value %d", i)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			t.Fatalf("Test logic error - value %d not covered above", i)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCouleBeT61(t *testing.T) {
 | 
			
		||||
	for i := 0; i < 255; i++ {
 | 
			
		||||
		b := []byte("StringWithA")
 | 
			
		||||
		b = append(b, byte(i))
 | 
			
		||||
 | 
			
		||||
		if couldBeT61(b) {
 | 
			
		||||
			switch i {
 | 
			
		||||
			case 0x00:
 | 
			
		||||
				fallthrough
 | 
			
		||||
			case 0x23, 0x24, 0x5C, 0x5E, 0x60, 0x7B, 0x7D, 0x7E, 0xA5, 0xA6, 0xAC, 0xAD, 0xAE, 0xAF,
 | 
			
		||||
				0xB9, 0xBA, 0xC0, 0xC9, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
 | 
			
		||||
				0xDA, 0xDB, 0xDC, 0xDE, 0xDF, 0xE5, 0xFF:
 | 
			
		||||
				t.Fatalf("Allowed string with byte %d", i)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestISO8859_1ToUTF8(t *testing.T) {
 | 
			
		||||
	b := []byte{'c', 'a', 'f', 0xE9} // 0xE9 == é in ISO8859-1, but is invalid in UTF8
 | 
			
		||||
	if string(b) == "café" {
 | 
			
		||||
		t.Fatal("Sanity failure: that shouldn't have matched")
 | 
			
		||||
	}
 | 
			
		||||
	if iso8859_1ToUTF8(b) != "café" {
 | 
			
		||||
		t.Fatalf("Failed to convert properly, got %v", iso8859_1ToUTF8(b))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var derEncodedSelfSignedCert = Certificate{
 | 
			
		||||
	TBSCertificate: TBSCertificate{
 | 
			
		||||
		Version:            0,
 | 
			
		||||
		SerialNumber:       RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}},
 | 
			
		||||
		SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}},
 | 
			
		||||
		Issuer: RDNSequence{
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
 | 
			
		||||
		},
 | 
			
		||||
		Validity: Validity{
 | 
			
		||||
			NotBefore: time.Date(2009, 10, 8, 00, 25, 53, 0, time.UTC),
 | 
			
		||||
			NotAfter:  time.Date(2010, 10, 8, 00, 25, 53, 0, time.UTC),
 | 
			
		||||
		},
 | 
			
		||||
		Subject: RDNSequence{
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 7}, Value: "City"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 10}, Value: "Internet Widgits Pty Ltd"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
 | 
			
		||||
			RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
 | 
			
		||||
		},
 | 
			
		||||
		PublicKey: PublicKeyInfo{
 | 
			
		||||
			Algorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}},
 | 
			
		||||
			PublicKey: BitString{
 | 
			
		||||
				Bytes: []uint8{
 | 
			
		||||
					0x30, 0x48, 0x2, 0x41, 0x0, 0xcd, 0xb7,
 | 
			
		||||
					0x63, 0x9c, 0x32, 0x78, 0xf0, 0x6, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42,
 | 
			
		||||
					0x90, 0x2b, 0x59, 0x2d, 0x8c, 0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4,
 | 
			
		||||
					0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea, 0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2,
 | 
			
		||||
					0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88, 0x96, 0x57, 0x72, 0x2a, 0x4f,
 | 
			
		||||
					0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45, 0xdc, 0x8f, 0xde, 0xec,
 | 
			
		||||
					0x35, 0x7d, 0x2, 0x3, 0x1, 0x0, 0x1,
 | 
			
		||||
				},
 | 
			
		||||
				BitLength: 592,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}},
 | 
			
		||||
	SignatureValue: BitString{
 | 
			
		||||
		Bytes: []uint8{
 | 
			
		||||
			0xa6, 0x7b, 0x6, 0xec, 0x5e, 0xce,
 | 
			
		||||
			0x92, 0x77, 0x2c, 0xa4, 0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c,
 | 
			
		||||
			0x7b, 0x45, 0x11, 0xcd, 0x40, 0xa7, 0xf6, 0x59, 0x98, 0x4, 0x2, 0xdf, 0x2b,
 | 
			
		||||
			0x99, 0x8b, 0xb9, 0xa4, 0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8,
 | 
			
		||||
			0xd9, 0x1e, 0xde, 0x14, 0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa,
 | 
			
		||||
			0xfa, 0x88, 0x21, 0x49, 0x4, 0x35,
 | 
			
		||||
		},
 | 
			
		||||
		BitLength: 512,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var derEncodedSelfSignedCertBytes = []byte{
 | 
			
		||||
	0x30, 0x82, 0x02, 0x18, 0x30,
 | 
			
		||||
	0x82, 0x01, 0xc2, 0x02, 0x09, 0x00, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c,
 | 
			
		||||
	0x98, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
 | 
			
		||||
	0x05, 0x05, 0x00, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
 | 
			
		||||
	0x04, 0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
 | 
			
		||||
	0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
 | 
			
		||||
	0x65, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43,
 | 
			
		||||
	0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
 | 
			
		||||
	0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
 | 
			
		||||
	0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31,
 | 
			
		||||
	0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c,
 | 
			
		||||
	0x73, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
 | 
			
		||||
	0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
 | 
			
		||||
	0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78,
 | 
			
		||||
	0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d,
 | 
			
		||||
	0x30, 0x39, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35, 0x33, 0x5a,
 | 
			
		||||
	0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x32, 0x35, 0x35,
 | 
			
		||||
	0x33, 0x5a, 0x30, 0x81, 0x92, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
 | 
			
		||||
	0x06, 0x13, 0x02, 0x58, 0x58, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
 | 
			
		||||
	0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
 | 
			
		||||
	0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x04, 0x43, 0x69,
 | 
			
		||||
	0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
 | 
			
		||||
	0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
 | 
			
		||||
	0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x1a,
 | 
			
		||||
	0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x66, 0x61, 0x6c, 0x73,
 | 
			
		||||
	0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
 | 
			
		||||
	0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
 | 
			
		||||
	0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x40, 0x65, 0x78, 0x61,
 | 
			
		||||
	0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x5c, 0x30, 0x0d, 0x06,
 | 
			
		||||
	0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
 | 
			
		||||
	0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xcd, 0xb7, 0x63, 0x9c, 0x32, 0x78,
 | 
			
		||||
	0xf0, 0x06, 0xaa, 0x27, 0x7f, 0x6e, 0xaf, 0x42, 0x90, 0x2b, 0x59, 0x2d, 0x8c,
 | 
			
		||||
	0xbc, 0xbe, 0x38, 0xa1, 0xc9, 0x2b, 0xa4, 0x69, 0x5a, 0x33, 0x1b, 0x1d, 0xea,
 | 
			
		||||
	0xde, 0xad, 0xd8, 0xe9, 0xa5, 0xc2, 0x7e, 0x8c, 0x4c, 0x2f, 0xd0, 0xa8, 0x88,
 | 
			
		||||
	0x96, 0x57, 0x72, 0x2a, 0x4f, 0x2a, 0xf7, 0x58, 0x9c, 0xf2, 0xc7, 0x70, 0x45,
 | 
			
		||||
	0xdc, 0x8f, 0xde, 0xec, 0x35, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d,
 | 
			
		||||
	0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
 | 
			
		||||
	0x03, 0x41, 0x00, 0xa6, 0x7b, 0x06, 0xec, 0x5e, 0xce, 0x92, 0x77, 0x2c, 0xa4,
 | 
			
		||||
	0x13, 0xcb, 0xa3, 0xca, 0x12, 0x56, 0x8f, 0xdc, 0x6c, 0x7b, 0x45, 0x11, 0xcd,
 | 
			
		||||
	0x40, 0xa7, 0xf6, 0x59, 0x98, 0x04, 0x02, 0xdf, 0x2b, 0x99, 0x8b, 0xb9, 0xa4,
 | 
			
		||||
	0xa8, 0xcb, 0xeb, 0x34, 0xc0, 0xf0, 0xa7, 0x8c, 0xf8, 0xd9, 0x1e, 0xde, 0x14,
 | 
			
		||||
	0xa5, 0xed, 0x76, 0xbf, 0x11, 0x6f, 0xe3, 0x60, 0xaa, 0xfa, 0x88, 0x21, 0x49,
 | 
			
		||||
	0x04, 0x35,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var derEncodedPaypalNULCertBytes = []byte{
 | 
			
		||||
	0x30, 0x82, 0x06, 0x44, 0x30,
 | 
			
		||||
	0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b,
 | 
			
		||||
	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
 | 
			
		||||
	0x05, 0x00, 0x30, 0x82, 0x01, 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
 | 
			
		||||
	0x04, 0x06, 0x13, 0x02, 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55,
 | 
			
		||||
	0x04, 0x08, 0x13, 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61,
 | 
			
		||||
	0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61,
 | 
			
		||||
	0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03,
 | 
			
		||||
	0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, 0x74,
 | 
			
		||||
	0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
 | 
			
		||||
	0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, 0x2e, 0x31, 0x2e,
 | 
			
		||||
	0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, 0x67, 0x65, 0x6e, 0x65,
 | 
			
		||||
	0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d,
 | 
			
		||||
	0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36,
 | 
			
		||||
	0x32, 0x32, 0x31, 0x30, 0x36, 0x39, 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03,
 | 
			
		||||
	0x55, 0x04, 0x0b, 0x13, 0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c,
 | 
			
		||||
	0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
 | 
			
		||||
	0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
 | 
			
		||||
	0x69, 0x74, 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
 | 
			
		||||
	0x25, 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41,
 | 
			
		||||
	0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
 | 
			
		||||
	0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
 | 
			
		||||
	0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
 | 
			
		||||
	0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70,
 | 
			
		||||
	0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39,
 | 
			
		||||
	0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d,
 | 
			
		||||
	0x31, 0x31, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a,
 | 
			
		||||
	0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
 | 
			
		||||
	0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
 | 
			
		||||
	0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16,
 | 
			
		||||
	0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20,
 | 
			
		||||
	0x46, 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f,
 | 
			
		||||
	0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
 | 
			
		||||
	0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0b,
 | 
			
		||||
	0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, 0x74, 0x31, 0x2f,
 | 
			
		||||
	0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x77, 0x77, 0x77, 0x2e,
 | 
			
		||||
	0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x73, 0x73,
 | 
			
		||||
	0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
 | 
			
		||||
	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x63, 0x63, 0x30, 0x81, 0x9f, 0x30, 0x0d,
 | 
			
		||||
	0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
 | 
			
		||||
	0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd2, 0x69,
 | 
			
		||||
	0xfa, 0x6f, 0x3a, 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19,
 | 
			
		||||
	0xb2, 0xc4, 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e,
 | 
			
		||||
	0x3c, 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29,
 | 
			
		||||
	0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, 0x41,
 | 
			
		||||
	0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, 0x26, 0xce,
 | 
			
		||||
	0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, 0xba, 0xa3, 0x96,
 | 
			
		||||
	0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, 0x14, 0xa4, 0xf4, 0xe2,
 | 
			
		||||
	0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, 0x2f, 0x7a, 0xe5, 0xb6, 0x10,
 | 
			
		||||
	0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49,
 | 
			
		||||
	0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00,
 | 
			
		||||
	0x01, 0xa3, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03,
 | 
			
		||||
	0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86,
 | 
			
		||||
	0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40,
 | 
			
		||||
	0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8,
 | 
			
		||||
	0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08,
 | 
			
		||||
	0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55,
 | 
			
		||||
	0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, 0x55, 0x14,
 | 
			
		||||
	0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, 0x25, 0xbc, 0x6e,
 | 
			
		||||
	0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
 | 
			
		||||
	0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, 0x5d, 0x90, 0x7b, 0x23, 0xc8,
 | 
			
		||||
	0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d,
 | 
			
		||||
	0x11, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04,
 | 
			
		||||
	0x15, 0x30, 0x13, 0x81, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40,
 | 
			
		||||
	0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09,
 | 
			
		||||
	0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63,
 | 
			
		||||
	0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
 | 
			
		||||
	0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e,
 | 
			
		||||
	0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, 0x2e,
 | 
			
		||||
	0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76,
 | 
			
		||||
	0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
 | 
			
		||||
	0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x68,
 | 
			
		||||
	0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70,
 | 
			
		||||
	0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60,
 | 
			
		||||
	0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68,
 | 
			
		||||
	0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70,
 | 
			
		||||
	0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x32, 0x30, 0x30, 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
 | 
			
		||||
	0x86, 0xf8, 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70,
 | 
			
		||||
	0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30,
 | 
			
		||||
	0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c,
 | 
			
		||||
	0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x09,
 | 
			
		||||
	0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, 0x04, 0x39, 0x16, 0x37,
 | 
			
		||||
	0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
 | 
			
		||||
	0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63,
 | 
			
		||||
	0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74,
 | 
			
		||||
	0x69, 0x6f, 0x6e, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74,
 | 
			
		||||
	0x6d, 0x6c, 0x3f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
 | 
			
		||||
	0x42, 0x01, 0x07, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
 | 
			
		||||
	0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63,
 | 
			
		||||
	0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f,
 | 
			
		||||
	0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41,
 | 
			
		||||
	0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, 0x60, 0x86,
 | 
			
		||||
	0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, 0x32, 0x68, 0x74,
 | 
			
		||||
	0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73,
 | 
			
		||||
	0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32,
 | 
			
		||||
	0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41,
 | 
			
		||||
	0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06,
 | 
			
		||||
	0x03, 0x55, 0x1d, 0x1f, 0x04, 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0,
 | 
			
		||||
	0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
 | 
			
		||||
	0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70,
 | 
			
		||||
	0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61,
 | 
			
		||||
	0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63,
 | 
			
		||||
	0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, 0x74,
 | 
			
		||||
	0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x69,
 | 
			
		||||
	0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63,
 | 
			
		||||
	0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30,
 | 
			
		||||
	0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c,
 | 
			
		||||
	0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
 | 
			
		||||
	0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
 | 
			
		||||
	0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
 | 
			
		||||
	0x73, 0x70, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
 | 
			
		||||
	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
 | 
			
		||||
	0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b,
 | 
			
		||||
	0xef, 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa,
 | 
			
		||||
	0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, 0x9e,
 | 
			
		||||
	0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, 0x90, 0x1d,
 | 
			
		||||
	0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, 0x41, 0x5b, 0xf7,
 | 
			
		||||
	0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, 0x1e, 0x45, 0x38, 0xa1,
 | 
			
		||||
	0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, 0x07, 0xba, 0x44, 0xcc, 0xe5,
 | 
			
		||||
	0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07,
 | 
			
		||||
	0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e,
 | 
			
		||||
	0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43,
 | 
			
		||||
	0x96, 0x07, 0xa8, 0xbb,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								ct/asn1/common.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								ct/asn1/common.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,163 @@
 | 
			
		||||
// 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 LICENSE file.
 | 
			
		||||
 | 
			
		||||
package asn1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ASN.1 objects have metadata preceding them:
 | 
			
		||||
//   the tag: the type of the object
 | 
			
		||||
//   a flag denoting if this object is compound or not
 | 
			
		||||
//   the class type: the namespace of the tag
 | 
			
		||||
//   the length of the object, in bytes
 | 
			
		||||
 | 
			
		||||
// Here are some standard tags and classes
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	tagBoolean         = 1
 | 
			
		||||
	tagInteger         = 2
 | 
			
		||||
	tagBitString       = 3
 | 
			
		||||
	tagOctetString     = 4
 | 
			
		||||
	tagOID             = 6
 | 
			
		||||
	tagEnum            = 10
 | 
			
		||||
	tagUTF8String      = 12
 | 
			
		||||
	tagSequence        = 16
 | 
			
		||||
	tagSet             = 17
 | 
			
		||||
	tagPrintableString = 19
 | 
			
		||||
	tagT61String       = 20
 | 
			
		||||
	tagIA5String       = 22
 | 
			
		||||
	tagUTCTime         = 23
 | 
			
		||||
	tagGeneralizedTime = 24
 | 
			
		||||
	tagGeneralString   = 27
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	classUniversal       = 0
 | 
			
		||||
	classApplication     = 1
 | 
			
		||||
	classContextSpecific = 2
 | 
			
		||||
	classPrivate         = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tagAndLength struct {
 | 
			
		||||
	class, tag, length int
 | 
			
		||||
	isCompound         bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead
 | 
			
		||||
// of" and "in addition to". When not specified, every primitive type has a
 | 
			
		||||
// default tag in the UNIVERSAL class.
 | 
			
		||||
//
 | 
			
		||||
// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1
 | 
			
		||||
// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT
 | 
			
		||||
// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another.
 | 
			
		||||
//
 | 
			
		||||
// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an
 | 
			
		||||
// /additional/ tag would wrap the default tag. This explicit tag will have the
 | 
			
		||||
// compound flag set.
 | 
			
		||||
//
 | 
			
		||||
// (This is used in order to remove ambiguity with optional elements.)
 | 
			
		||||
//
 | 
			
		||||
// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we
 | 
			
		||||
// don't support that here. We support a single layer of EXPLICIT or IMPLICIT
 | 
			
		||||
// tagging with tag strings on the fields of a structure.
 | 
			
		||||
 | 
			
		||||
// fieldParameters is the parsed representation of tag string from a structure field.
 | 
			
		||||
type fieldParameters struct {
 | 
			
		||||
	optional     bool   // true iff the field is OPTIONAL
 | 
			
		||||
	explicit     bool   // true iff an EXPLICIT tag is in use.
 | 
			
		||||
	application  bool   // true iff an APPLICATION tag is in use.
 | 
			
		||||
	defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
 | 
			
		||||
	tag          *int   // the EXPLICIT or IMPLICIT tag (maybe nil).
 | 
			
		||||
	stringType   int    // the string tag to use when marshaling.
 | 
			
		||||
	set          bool   // true iff this should be encoded as a SET
 | 
			
		||||
	omitEmpty    bool   // true iff this should be omitted if empty when marshaling.
 | 
			
		||||
 | 
			
		||||
	// Invariants:
 | 
			
		||||
	//   if explicit is set, tag is non-nil.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a tag string with the format specified in the package comment,
 | 
			
		||||
// parseFieldParameters will parse it into a fieldParameters structure,
 | 
			
		||||
// ignoring unknown parts of the string.
 | 
			
		||||
func parseFieldParameters(str string) (ret fieldParameters) {
 | 
			
		||||
	for _, part := range strings.Split(str, ",") {
 | 
			
		||||
		switch {
 | 
			
		||||
		case part == "optional":
 | 
			
		||||
			ret.optional = true
 | 
			
		||||
		case part == "explicit":
 | 
			
		||||
			ret.explicit = true
 | 
			
		||||
			if ret.tag == nil {
 | 
			
		||||
				ret.tag = new(int)
 | 
			
		||||
			}
 | 
			
		||||
		case part == "ia5":
 | 
			
		||||
			ret.stringType = tagIA5String
 | 
			
		||||
		case part == "printable":
 | 
			
		||||
			ret.stringType = tagPrintableString
 | 
			
		||||
		case part == "utf8":
 | 
			
		||||
			ret.stringType = tagUTF8String
 | 
			
		||||
		case strings.HasPrefix(part, "default:"):
 | 
			
		||||
			i, err := strconv.ParseInt(part[8:], 10, 64)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				ret.defaultValue = new(int64)
 | 
			
		||||
				*ret.defaultValue = i
 | 
			
		||||
			}
 | 
			
		||||
		case strings.HasPrefix(part, "tag:"):
 | 
			
		||||
			i, err := strconv.Atoi(part[4:])
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				ret.tag = new(int)
 | 
			
		||||
				*ret.tag = i
 | 
			
		||||
			}
 | 
			
		||||
		case part == "set":
 | 
			
		||||
			ret.set = true
 | 
			
		||||
		case part == "application":
 | 
			
		||||
			ret.application = true
 | 
			
		||||
			if ret.tag == nil {
 | 
			
		||||
				ret.tag = new(int)
 | 
			
		||||
			}
 | 
			
		||||
		case part == "omitempty":
 | 
			
		||||
			ret.omitEmpty = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Given a reflected Go type, getUniversalType returns the default tag number
 | 
			
		||||
// and expected compound flag.
 | 
			
		||||
func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
 | 
			
		||||
	switch t {
 | 
			
		||||
	case objectIdentifierType:
 | 
			
		||||
		return tagOID, false, true
 | 
			
		||||
	case bitStringType:
 | 
			
		||||
		return tagBitString, false, true
 | 
			
		||||
	case timeType:
 | 
			
		||||
		return tagUTCTime, false, true
 | 
			
		||||
	case enumeratedType:
 | 
			
		||||
		return tagEnum, false, true
 | 
			
		||||
	case bigIntType:
 | 
			
		||||
		return tagInteger, false, true
 | 
			
		||||
	}
 | 
			
		||||
	switch t.Kind() {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		return tagBoolean, false, true
 | 
			
		||||
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
		return tagInteger, false, true
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		return tagSequence, true, true
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		if t.Elem().Kind() == reflect.Uint8 {
 | 
			
		||||
			return tagOctetString, false, true
 | 
			
		||||
		}
 | 
			
		||||
		if strings.HasSuffix(t.Name(), "SET") {
 | 
			
		||||
			return tagSet, true, true
 | 
			
		||||
		}
 | 
			
		||||
		return tagSequence, true, true
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		return tagPrintableString, false, true
 | 
			
		||||
	}
 | 
			
		||||
	return 0, false, false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										581
									
								
								ct/asn1/marshal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										581
									
								
								ct/asn1/marshal.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,581 @@
 | 
			
		||||
// 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 LICENSE file.
 | 
			
		||||
 | 
			
		||||
package asn1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A forkableWriter is an in-memory buffer that can be
 | 
			
		||||
// 'forked' to create new forkableWriters that bracket the
 | 
			
		||||
// original.  After
 | 
			
		||||
//    pre, post := w.fork();
 | 
			
		||||
// the overall sequence of bytes represented is logically w+pre+post.
 | 
			
		||||
type forkableWriter struct {
 | 
			
		||||
	*bytes.Buffer
 | 
			
		||||
	pre, post *forkableWriter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newForkableWriter() *forkableWriter {
 | 
			
		||||
	return &forkableWriter{new(bytes.Buffer), nil, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *forkableWriter) fork() (pre, post *forkableWriter) {
 | 
			
		||||
	if f.pre != nil || f.post != nil {
 | 
			
		||||
		panic("have already forked")
 | 
			
		||||
	}
 | 
			
		||||
	f.pre = newForkableWriter()
 | 
			
		||||
	f.post = newForkableWriter()
 | 
			
		||||
	return f.pre, f.post
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *forkableWriter) Len() (l int) {
 | 
			
		||||
	l += f.Buffer.Len()
 | 
			
		||||
	if f.pre != nil {
 | 
			
		||||
		l += f.pre.Len()
 | 
			
		||||
	}
 | 
			
		||||
	if f.post != nil {
 | 
			
		||||
		l += f.post.Len()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) {
 | 
			
		||||
	n, err = out.Write(f.Bytes())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var nn int
 | 
			
		||||
 | 
			
		||||
	if f.pre != nil {
 | 
			
		||||
		nn, err = f.pre.writeTo(out)
 | 
			
		||||
		n += nn
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if f.post != nil {
 | 
			
		||||
		nn, err = f.post.writeTo(out)
 | 
			
		||||
		n += nn
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalBase128Int(out *forkableWriter, n int64) (err error) {
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		err = out.WriteByte(0)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l := 0
 | 
			
		||||
	for i := n; i > 0; i >>= 7 {
 | 
			
		||||
		l++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := l - 1; i >= 0; i-- {
 | 
			
		||||
		o := byte(n >> uint(i*7))
 | 
			
		||||
		o &= 0x7f
 | 
			
		||||
		if i != 0 {
 | 
			
		||||
			o |= 0x80
 | 
			
		||||
		}
 | 
			
		||||
		err = out.WriteByte(o)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalInt64(out *forkableWriter, i int64) (err error) {
 | 
			
		||||
	n := int64Length(i)
 | 
			
		||||
 | 
			
		||||
	for ; n > 0; n-- {
 | 
			
		||||
		err = out.WriteByte(byte(i >> uint((n-1)*8)))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func int64Length(i int64) (numBytes int) {
 | 
			
		||||
	numBytes = 1
 | 
			
		||||
 | 
			
		||||
	for i > 127 {
 | 
			
		||||
		numBytes++
 | 
			
		||||
		i >>= 8
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i < -128 {
 | 
			
		||||
		numBytes++
 | 
			
		||||
		i >>= 8
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalBigInt(out *forkableWriter, n *big.Int) (err error) {
 | 
			
		||||
	if n.Sign() < 0 {
 | 
			
		||||
		// A negative number has to be converted to two's-complement
 | 
			
		||||
		// form. So we'll subtract 1 and invert. If the
 | 
			
		||||
		// most-significant-bit isn't set then we'll need to pad the
 | 
			
		||||
		// beginning with 0xff in order to keep the number negative.
 | 
			
		||||
		nMinus1 := new(big.Int).Neg(n)
 | 
			
		||||
		nMinus1.Sub(nMinus1, bigOne)
 | 
			
		||||
		bytes := nMinus1.Bytes()
 | 
			
		||||
		for i := range bytes {
 | 
			
		||||
			bytes[i] ^= 0xff
 | 
			
		||||
		}
 | 
			
		||||
		if len(bytes) == 0 || bytes[0]&0x80 == 0 {
 | 
			
		||||
			err = out.WriteByte(0xff)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		_, err = out.Write(bytes)
 | 
			
		||||
	} else if n.Sign() == 0 {
 | 
			
		||||
		// Zero is written as a single 0 zero rather than no bytes.
 | 
			
		||||
		err = out.WriteByte(0x00)
 | 
			
		||||
	} else {
 | 
			
		||||
		bytes := n.Bytes()
 | 
			
		||||
		if len(bytes) > 0 && bytes[0]&0x80 != 0 {
 | 
			
		||||
			// We'll have to pad this with 0x00 in order to stop it
 | 
			
		||||
			// looking like a negative number.
 | 
			
		||||
			err = out.WriteByte(0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		_, err = out.Write(bytes)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalLength(out *forkableWriter, 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func lengthLength(i int) (numBytes int) {
 | 
			
		||||
	numBytes = 1
 | 
			
		||||
	for i > 255 {
 | 
			
		||||
		numBytes++
 | 
			
		||||
		i >>= 8
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
 | 
			
		||||
	b := uint8(t.class) << 6
 | 
			
		||||
	if t.isCompound {
 | 
			
		||||
		b |= 0x20
 | 
			
		||||
	}
 | 
			
		||||
	if t.tag >= 31 {
 | 
			
		||||
		b |= 0x1f
 | 
			
		||||
		err = out.WriteByte(b)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = marshalBase128Int(out, int64(t.tag))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		b |= uint8(t.tag)
 | 
			
		||||
		err = out.WriteByte(b)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.length >= 128 {
 | 
			
		||||
		l := lengthLength(t.length)
 | 
			
		||||
		err = out.WriteByte(0x80 | byte(l))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = marshalLength(out, t.length)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		err = out.WriteByte(byte(t.length))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalBitString(out *forkableWriter, b BitString) (err error) {
 | 
			
		||||
	paddingBits := byte((8 - b.BitLength%8) % 8)
 | 
			
		||||
	err = out.WriteByte(paddingBits)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	_, err = out.Write(b.Bytes)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) {
 | 
			
		||||
	if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
 | 
			
		||||
		return StructuralError{"invalid object identifier"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalBase128Int(out, int64(oid[0]*40+oid[1]))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for i := 2; i < len(oid); i++ {
 | 
			
		||||
		err = marshalBase128Int(out, int64(oid[i]))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalPrintableString(out *forkableWriter, s string) (err error) {
 | 
			
		||||
	b := []byte(s)
 | 
			
		||||
	for _, c := range b {
 | 
			
		||||
		if !isPrintable(c) {
 | 
			
		||||
			return StructuralError{"PrintableString contains invalid character"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = out.Write(b)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalIA5String(out *forkableWriter, s string) (err error) {
 | 
			
		||||
	b := []byte(s)
 | 
			
		||||
	for _, c := range b {
 | 
			
		||||
		if c > 127 {
 | 
			
		||||
			return StructuralError{"IA5String contains invalid character"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = out.Write(b)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalUTF8String(out *forkableWriter, s string) (err error) {
 | 
			
		||||
	_, err = out.Write([]byte(s))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalTwoDigits(out *forkableWriter, v int) (err error) {
 | 
			
		||||
	err = out.WriteByte(byte('0' + (v/10)%10))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return out.WriteByte(byte('0' + v%10))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
 | 
			
		||||
	year, month, day := t.Date()
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case 1950 <= year && year < 2000:
 | 
			
		||||
		err = marshalTwoDigits(out, int(year-1900))
 | 
			
		||||
	case 2000 <= year && year < 2050:
 | 
			
		||||
		err = marshalTwoDigits(out, int(year-2000))
 | 
			
		||||
	default:
 | 
			
		||||
		return StructuralError{"cannot represent time as UTCTime"}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, int(month))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, day)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hour, min, sec := t.Clock()
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, hour)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, min)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, sec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, offset := t.Zone()
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case offset/60 == 0:
 | 
			
		||||
		err = out.WriteByte('Z')
 | 
			
		||||
		return
 | 
			
		||||
	case offset > 0:
 | 
			
		||||
		err = out.WriteByte('+')
 | 
			
		||||
	case offset < 0:
 | 
			
		||||
		err = out.WriteByte('-')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	offsetMinutes := offset / 60
 | 
			
		||||
	if offsetMinutes < 0 {
 | 
			
		||||
		offsetMinutes = -offsetMinutes
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, offsetMinutes/60)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTwoDigits(out, offsetMinutes%60)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func stripTagAndLength(in []byte) []byte {
 | 
			
		||||
	_, offset, err := parseTagAndLength(in, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return in
 | 
			
		||||
	}
 | 
			
		||||
	return in[offset:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
 | 
			
		||||
	switch value.Type() {
 | 
			
		||||
	case timeType:
 | 
			
		||||
		return marshalUTCTime(out, value.Interface().(time.Time))
 | 
			
		||||
	case bitStringType:
 | 
			
		||||
		return marshalBitString(out, value.Interface().(BitString))
 | 
			
		||||
	case objectIdentifierType:
 | 
			
		||||
		return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
 | 
			
		||||
	case bigIntType:
 | 
			
		||||
		return marshalBigInt(out, value.Interface().(*big.Int))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch v := value; v.Kind() {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		if v.Bool() {
 | 
			
		||||
			return out.WriteByte(255)
 | 
			
		||||
		} else {
 | 
			
		||||
			return out.WriteByte(0)
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
		return marshalInt64(out, int64(v.Int()))
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		t := v.Type()
 | 
			
		||||
 | 
			
		||||
		startingField := 0
 | 
			
		||||
 | 
			
		||||
		// If the first element of the structure is a non-empty
 | 
			
		||||
		// RawContents, then we don't bother serializing the rest.
 | 
			
		||||
		if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
 | 
			
		||||
			s := v.Field(0)
 | 
			
		||||
			if s.Len() > 0 {
 | 
			
		||||
				bytes := make([]byte, s.Len())
 | 
			
		||||
				for i := 0; i < s.Len(); i++ {
 | 
			
		||||
					bytes[i] = uint8(s.Index(i).Uint())
 | 
			
		||||
				}
 | 
			
		||||
				/* The RawContents will contain the tag and
 | 
			
		||||
				 * length fields but we'll also be writing
 | 
			
		||||
				 * those ourselves, so we strip them out of
 | 
			
		||||
				 * bytes */
 | 
			
		||||
				_, err = out.Write(stripTagAndLength(bytes))
 | 
			
		||||
				return
 | 
			
		||||
			} else {
 | 
			
		||||
				startingField = 1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for i := startingField; i < t.NumField(); i++ {
 | 
			
		||||
			var pre *forkableWriter
 | 
			
		||||
			pre, out = out.fork()
 | 
			
		||||
			err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		sliceType := v.Type()
 | 
			
		||||
		if sliceType.Elem().Kind() == reflect.Uint8 {
 | 
			
		||||
			bytes := make([]byte, v.Len())
 | 
			
		||||
			for i := 0; i < v.Len(); i++ {
 | 
			
		||||
				bytes[i] = uint8(v.Index(i).Uint())
 | 
			
		||||
			}
 | 
			
		||||
			_, err = out.Write(bytes)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var fp fieldParameters
 | 
			
		||||
		for i := 0; i < v.Len(); i++ {
 | 
			
		||||
			var pre *forkableWriter
 | 
			
		||||
			pre, out = out.fork()
 | 
			
		||||
			err = marshalField(pre, v.Index(i), fp)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		switch params.stringType {
 | 
			
		||||
		case tagIA5String:
 | 
			
		||||
			return marshalIA5String(out, v.String())
 | 
			
		||||
		case tagPrintableString:
 | 
			
		||||
			return marshalPrintableString(out, v.String())
 | 
			
		||||
		default:
 | 
			
		||||
			return marshalUTF8String(out, v.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return StructuralError{"unknown Go type"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) {
 | 
			
		||||
	// If the field is an interface{} then recurse into it.
 | 
			
		||||
	if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
 | 
			
		||||
		return marshalField(out, v.Elem(), params)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Type() == rawValueType {
 | 
			
		||||
		rv := v.Interface().(RawValue)
 | 
			
		||||
		if len(rv.FullBytes) != 0 {
 | 
			
		||||
			_, err = out.Write(rv.FullBytes)
 | 
			
		||||
		} else {
 | 
			
		||||
			err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			_, err = out.Write(rv.Bytes)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tag, isCompound, ok := getUniversalType(v.Type())
 | 
			
		||||
	if !ok {
 | 
			
		||||
		err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	class := classUniversal
 | 
			
		||||
 | 
			
		||||
	if params.stringType != 0 && tag != tagPrintableString {
 | 
			
		||||
		return StructuralError{"explicit string type given to non-string member"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tag == tagPrintableString {
 | 
			
		||||
		if params.stringType == 0 {
 | 
			
		||||
			// This is a string without an explicit string type. We'll use
 | 
			
		||||
			// a PrintableString if the character set in the string is
 | 
			
		||||
			// sufficiently limited, otherwise we'll use a UTF8String.
 | 
			
		||||
			for _, r := range v.String() {
 | 
			
		||||
				if r >= utf8.RuneSelf || !isPrintable(byte(r)) {
 | 
			
		||||
					if !utf8.ValidString(v.String()) {
 | 
			
		||||
						return errors.New("asn1: string not valid UTF-8")
 | 
			
		||||
					}
 | 
			
		||||
					tag = tagUTF8String
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			tag = params.stringType
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if params.set {
 | 
			
		||||
		if tag != tagSequence {
 | 
			
		||||
			return StructuralError{"non sequence tagged as set"}
 | 
			
		||||
		}
 | 
			
		||||
		tag = tagSet
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tags, body := out.fork()
 | 
			
		||||
 | 
			
		||||
	err = marshalBody(body, v, params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bodyLen := body.Len()
 | 
			
		||||
 | 
			
		||||
	var explicitTag *forkableWriter
 | 
			
		||||
	if params.explicit {
 | 
			
		||||
		explicitTag, tags = tags.fork()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !params.explicit && params.tag != nil {
 | 
			
		||||
		// implicit tag.
 | 
			
		||||
		tag = *params.tag
 | 
			
		||||
		class = classContextSpecific
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if params.explicit {
 | 
			
		||||
		err = marshalTagAndLength(explicitTag, tagAndLength{
 | 
			
		||||
			class:      classContextSpecific,
 | 
			
		||||
			tag:        *params.tag,
 | 
			
		||||
			length:     bodyLen + tags.Len(),
 | 
			
		||||
			isCompound: true,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal returns the ASN.1 encoding of val.
 | 
			
		||||
func Marshal(val interface{}) ([]byte, error) {
 | 
			
		||||
	var out bytes.Buffer
 | 
			
		||||
	v := reflect.ValueOf(val)
 | 
			
		||||
	f := newForkableWriter()
 | 
			
		||||
	err := marshalField(f, v, fieldParameters{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	_, err = f.writeTo(&out)
 | 
			
		||||
	return out.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								ct/asn1/marshal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								ct/asn1/marshal_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,148 @@
 | 
			
		||||
// 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 LICENSE file.
 | 
			
		||||
 | 
			
		||||
package asn1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type intStruct struct {
 | 
			
		||||
	A int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type twoIntStruct struct {
 | 
			
		||||
	A int
 | 
			
		||||
	B int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type bigIntStruct struct {
 | 
			
		||||
	A *big.Int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nestedStruct struct {
 | 
			
		||||
	A intStruct
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rawContentsStruct struct {
 | 
			
		||||
	Raw RawContent
 | 
			
		||||
	A   int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type implicitTagTest struct {
 | 
			
		||||
	A int `asn1:"implicit,tag:5"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type explicitTagTest struct {
 | 
			
		||||
	A int `asn1:"explicit,tag:5"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ia5StringTest struct {
 | 
			
		||||
	A string `asn1:"ia5"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type printableStringTest struct {
 | 
			
		||||
	A string `asn1:"printable"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type optionalRawValueTest struct {
 | 
			
		||||
	A RawValue `asn1:"optional"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type omitEmptyTest struct {
 | 
			
		||||
	A []string `asn1:"omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type testSET []int
 | 
			
		||||
 | 
			
		||||
var PST = time.FixedZone("PST", -8*60*60)
 | 
			
		||||
 | 
			
		||||
type marshalTest struct {
 | 
			
		||||
	in  interface{}
 | 
			
		||||
	out string // hex encoded
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var marshalTests = []marshalTest{
 | 
			
		||||
	{10, "02010a"},
 | 
			
		||||
	{127, "02017f"},
 | 
			
		||||
	{128, "02020080"},
 | 
			
		||||
	{-128, "020180"},
 | 
			
		||||
	{-129, "0202ff7f"},
 | 
			
		||||
	{intStruct{64}, "3003020140"},
 | 
			
		||||
	{bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},
 | 
			
		||||
	{twoIntStruct{64, 65}, "3006020140020141"},
 | 
			
		||||
	{nestedStruct{intStruct{127}}, "3005300302017f"},
 | 
			
		||||
	{[]byte{1, 2, 3}, "0403010203"},
 | 
			
		||||
	{implicitTagTest{64}, "3003850140"},
 | 
			
		||||
	{explicitTagTest{64}, "3005a503020140"},
 | 
			
		||||
	{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
 | 
			
		||||
	{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
 | 
			
		||||
	{time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
 | 
			
		||||
	{BitString{[]byte{0x80}, 1}, "03020780"},
 | 
			
		||||
	{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
 | 
			
		||||
	{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
 | 
			
		||||
	{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"},
 | 
			
		||||
	{ObjectIdentifier([]int{2, 100, 3}), "0603813403"},
 | 
			
		||||
	{"test", "130474657374"},
 | 
			
		||||
	{
 | 
			
		||||
		"" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x'
 | 
			
		||||
		"137f" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878" +
 | 
			
		||||
			"78787878787878787878787878787878787878787878787878787878787878",
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
 | 
			
		||||
			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x'
 | 
			
		||||
		"138180" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878" +
 | 
			
		||||
			"7878787878787878787878787878787878787878787878787878787878787878",
 | 
			
		||||
	},
 | 
			
		||||
	{ia5StringTest{"test"}, "3006160474657374"},
 | 
			
		||||
	{optionalRawValueTest{}, "3000"},
 | 
			
		||||
	{printableStringTest{"test"}, "3006130474657374"},
 | 
			
		||||
	{printableStringTest{"test*"}, "30071305746573742a"},
 | 
			
		||||
	{rawContentsStruct{nil, 64}, "3003020140"},
 | 
			
		||||
	{rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"},
 | 
			
		||||
	{RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"},
 | 
			
		||||
	{testSET([]int{10}), "310302010a"},
 | 
			
		||||
	{omitEmptyTest{[]string{}}, "3000"},
 | 
			
		||||
	{omitEmptyTest{[]string{"1"}}, "30053003130131"},
 | 
			
		||||
	{"Σ", "0c02cea3"},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMarshal(t *testing.T) {
 | 
			
		||||
	for i, test := range marshalTests {
 | 
			
		||||
		data, err := Marshal(test.in)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("#%d failed: %s", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		out, _ := hex.DecodeString(test.out)
 | 
			
		||||
		if !bytes.Equal(out, data) {
 | 
			
		||||
			t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInvalidUTF8(t *testing.T) {
 | 
			
		||||
	_, err := Marshal(string([]byte{0xff, 0xff}))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("invalid UTF8 string was accepted")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										210
									
								
								ct/client/logclient.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								ct/client/logclient.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,210 @@
 | 
			
		||||
// Package client is a CT log client implementation and contains types and code
 | 
			
		||||
// for interacting with RFC6962-compliant CT Log instances.
 | 
			
		||||
// See http://tools.ietf.org/html/rfc6962 for details
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"src.agwa.name/ctwatch/ct"
 | 
			
		||||
	"github.com/mreiferson/go-httpclient"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// URI paths for CT Log endpoints
 | 
			
		||||
const (
 | 
			
		||||
	GetSTHPath		= "/ct/v1/get-sth"
 | 
			
		||||
	GetEntriesPath		= "/ct/v1/get-entries"
 | 
			
		||||
	GetSTHConsistencyPath	= "/ct/v1/get-sth-consistency"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LogClient represents a client for a given CT Log instance
 | 
			
		||||
type LogClient struct {
 | 
			
		||||
	uri        string       // the base URI of the log. e.g. http://ct.googleapis/pilot
 | 
			
		||||
	httpClient *http.Client // used to interact with the log via HTTP
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// JSON structures follow.
 | 
			
		||||
// These represent the structures returned by the CT Log server.
 | 
			
		||||
//////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// getSTHResponse respresents the JSON response to the get-sth CT method
 | 
			
		||||
type getSTHResponse struct {
 | 
			
		||||
	TreeSize          uint64 `json:"tree_size"`           // Number of certs in the current tree
 | 
			
		||||
	Timestamp         uint64 `json:"timestamp"`           // Time that the tree was created
 | 
			
		||||
	SHA256RootHash    string `json:"sha256_root_hash"`    // Root hash of the tree
 | 
			
		||||
	TreeHeadSignature string `json:"tree_head_signature"` // Log signature for this STH
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// base64LeafEntry respresents a Base64 encoded leaf entry
 | 
			
		||||
type base64LeafEntry struct {
 | 
			
		||||
	LeafInput string `json:"leaf_input"`
 | 
			
		||||
	ExtraData string `json:"extra_data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getEntriesReponse respresents the JSON response to the CT get-entries method
 | 
			
		||||
type getEntriesResponse struct {
 | 
			
		||||
	Entries []base64LeafEntry `json:"entries"` // the list of returned entries
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getConsistencyProofResponse represents the JSON response to the CT get-consistency-proof method
 | 
			
		||||
type getConsistencyProofResponse struct {
 | 
			
		||||
	Consistency []string `json:"consistency"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
func New(uri string) *LogClient {
 | 
			
		||||
	var c LogClient
 | 
			
		||||
	c.uri = uri
 | 
			
		||||
	transport := &httpclient.Transport{
 | 
			
		||||
		ConnectTimeout:        10 * time.Second,
 | 
			
		||||
		RequestTimeout:        30 * time.Second,
 | 
			
		||||
		ResponseHeaderTimeout: 30 * time.Second,
 | 
			
		||||
		MaxIdleConnsPerHost:   10,
 | 
			
		||||
		DisableKeepAlives:     false,
 | 
			
		||||
	}
 | 
			
		||||
	c.httpClient = &http.Client{Transport: transport}
 | 
			
		||||
	return &c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Makes a HTTP call to |uri|, and attempts to parse the response as a JSON
 | 
			
		||||
// representation of the structure in |res|.
 | 
			
		||||
// Returns a non-nil |error| if there was a problem.
 | 
			
		||||
func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
 | 
			
		||||
	req, err := http.NewRequest("GET", uri, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Keep-Alive", "timeout=15, max=100")
 | 
			
		||||
	resp, err := c.httpClient.Do(req)
 | 
			
		||||
	var body []byte
 | 
			
		||||
	if resp != nil {
 | 
			
		||||
		body, err = ioutil.ReadAll(resp.Body)
 | 
			
		||||
		resp.Body.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = json.Unmarshal(body, &res); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetSTH retrieves the current STH from the log.
 | 
			
		||||
// Returns a populated SignedTreeHead, or a non-nil error.
 | 
			
		||||
func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) {
 | 
			
		||||
	var resp getSTHResponse
 | 
			
		||||
	if err = c.fetchAndParse(c.uri+GetSTHPath, &resp); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	sth = &ct.SignedTreeHead{
 | 
			
		||||
		TreeSize:  resp.TreeSize,
 | 
			
		||||
		Timestamp: resp.Timestamp,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rawRootHash, err := base64.StdEncoding.DecodeString(resp.SHA256RootHash)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("invalid base64 encoding in sha256_root_hash: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(rawRootHash) != sha256.Size {
 | 
			
		||||
		return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(rawRootHash))
 | 
			
		||||
	}
 | 
			
		||||
	copy(sth.SHA256RootHash[:], rawRootHash)
 | 
			
		||||
 | 
			
		||||
	rawSignature, err := base64.StdEncoding.DecodeString(resp.TreeHeadSignature)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New("invalid base64 encoding in tree_head_signature")
 | 
			
		||||
	}
 | 
			
		||||
	ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// TODO(alcutter): Verify signature
 | 
			
		||||
	sth.TreeHeadSignature = *ds
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetEntries attempts to retrieve the entries in the sequence [|start|, |end|] from the CT
 | 
			
		||||
// log server. (see section 4.6.)
 | 
			
		||||
// Returns a slice of LeafInputs or a non-nil error.
 | 
			
		||||
func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) {
 | 
			
		||||
	if end < 0 {
 | 
			
		||||
		return nil, errors.New("end should be >= 0")
 | 
			
		||||
	}
 | 
			
		||||
	if end < start {
 | 
			
		||||
		return nil, errors.New("start should be <= end")
 | 
			
		||||
	}
 | 
			
		||||
	var resp getEntriesResponse
 | 
			
		||||
	err := c.fetchAndParse(fmt.Sprintf("%s%s?start=%d&end=%d", c.uri, GetEntriesPath, start, end), &resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	entries := make([]ct.LogEntry, len(resp.Entries))
 | 
			
		||||
	for index, entry := range resp.Entries {
 | 
			
		||||
		leafBytes, err := base64.StdEncoding.DecodeString(entry.LeafInput)
 | 
			
		||||
		leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(leafBytes))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		entries[index].LeafBytes = leafBytes
 | 
			
		||||
		entries[index].Leaf = *leaf
 | 
			
		||||
		chainBytes, err := base64.StdEncoding.DecodeString(entry.ExtraData)
 | 
			
		||||
 | 
			
		||||
		var chain []ct.ASN1Cert
 | 
			
		||||
		switch leaf.TimestampedEntry.EntryType {
 | 
			
		||||
		case ct.X509LogEntryType:
 | 
			
		||||
			chain, err = ct.UnmarshalX509ChainArray(chainBytes)
 | 
			
		||||
 | 
			
		||||
		case ct.PrecertLogEntryType:
 | 
			
		||||
			chain, err = ct.UnmarshalPrecertChainArray(chainBytes)
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, fmt.Errorf("saw unknown entry type: %v", leaf.TimestampedEntry.EntryType)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		entries[index].Chain = chain
 | 
			
		||||
		entries[index].Index = start + int64(index)
 | 
			
		||||
	}
 | 
			
		||||
	return entries, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConsistencyProof retrieves a Merkle Consistency Proof between two STHs (|first| and |second|)
 | 
			
		||||
// from the log.  Returns a slice of MerkleTreeNodes (a ct.ConsistencyProof) or a non-nil error.
 | 
			
		||||
func (c *LogClient) GetConsistencyProof(first, second int64) (ct.ConsistencyProof, error) {
 | 
			
		||||
	if second < 0 {
 | 
			
		||||
		return nil, errors.New("second should be >= 0")
 | 
			
		||||
	}
 | 
			
		||||
	if second < first {
 | 
			
		||||
		return nil, errors.New("first should be <= second")
 | 
			
		||||
	}
 | 
			
		||||
	var resp getConsistencyProofResponse
 | 
			
		||||
	err := c.fetchAndParse(fmt.Sprintf("%s%s?first=%d&second=%d", c.uri, GetSTHConsistencyPath, first, second), &resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	nodes := make([]ct.MerkleTreeNode, len(resp.Consistency))
 | 
			
		||||
	for index, nodeString := range resp.Consistency {
 | 
			
		||||
		nodeBytes, err := base64.StdEncoding.DecodeString(nodeString)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		nodes[index] = nodeBytes
 | 
			
		||||
	}
 | 
			
		||||
	return nodes, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										463
									
								
								ct/serialization.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										463
									
								
								ct/serialization.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,463 @@
 | 
			
		||||
package ct
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"container/list"
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Variable size structure prefix-header byte lengths
 | 
			
		||||
const (
 | 
			
		||||
	CertificateLengthBytes      = 3
 | 
			
		||||
	PreCertificateLengthBytes   = 3
 | 
			
		||||
	ExtensionsLengthBytes       = 2
 | 
			
		||||
	CertificateChainLengthBytes = 3
 | 
			
		||||
	SignatureLengthBytes        = 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Max lengths
 | 
			
		||||
const (
 | 
			
		||||
	MaxCertificateLength = (1 << 24) - 1
 | 
			
		||||
	MaxExtensionsLength  = (1 << 16) - 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func writeUint(w io.Writer, value uint64, numBytes int) error {
 | 
			
		||||
	buf := make([]uint8, numBytes)
 | 
			
		||||
	for i := 0; i < numBytes; i++ {
 | 
			
		||||
		buf[numBytes-i-1] = uint8(value & 0xff)
 | 
			
		||||
		value >>= 8
 | 
			
		||||
	}
 | 
			
		||||
	if value != 0 {
 | 
			
		||||
		return errors.New("numBytes was insufficiently large to represent value")
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := w.Write(buf); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeVarBytes(w io.Writer, value []byte, numLenBytes int) error {
 | 
			
		||||
	if err := writeUint(w, uint64(len(value)), numLenBytes); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := w.Write(value); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readUint(r io.Reader, numBytes int) (uint64, error) {
 | 
			
		||||
	var l uint64
 | 
			
		||||
	for i := 0; i < numBytes; i++ {
 | 
			
		||||
		l <<= 8
 | 
			
		||||
		var t uint8
 | 
			
		||||
		if err := binary.Read(r, binary.BigEndian, &t); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		l |= uint64(t)
 | 
			
		||||
	}
 | 
			
		||||
	return l, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reads a variable length array of bytes from |r|. |numLenBytes| specifies the
 | 
			
		||||
// number of (BigEndian) prefix-bytes which contain the length of the actual
 | 
			
		||||
// array data bytes that follow.
 | 
			
		||||
// Allocates an array to hold the contents and returns a slice view into it if
 | 
			
		||||
// the read was successful, or an error otherwise.
 | 
			
		||||
func readVarBytes(r io.Reader, numLenBytes int) ([]byte, error) {
 | 
			
		||||
	switch {
 | 
			
		||||
	case numLenBytes > 8:
 | 
			
		||||
		return nil, fmt.Errorf("numLenBytes too large (%d)", numLenBytes)
 | 
			
		||||
	case numLenBytes == 0:
 | 
			
		||||
		return nil, errors.New("numLenBytes should be > 0")
 | 
			
		||||
	}
 | 
			
		||||
	l, err := readUint(r, numLenBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	data := make([]byte, l)
 | 
			
		||||
	n, err := r.Read(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if n != int(l) {
 | 
			
		||||
		return nil, fmt.Errorf("short read: expected %d but got %d", l, n)
 | 
			
		||||
	}
 | 
			
		||||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reads a list of ASN1Cert types from |r|
 | 
			
		||||
func readASN1CertList(r io.Reader, totalLenBytes int, elementLenBytes int) ([]ASN1Cert, error) {
 | 
			
		||||
	listBytes, err := readVarBytes(r, totalLenBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []ASN1Cert{}, err
 | 
			
		||||
	}
 | 
			
		||||
	list := list.New()
 | 
			
		||||
	listReader := bytes.NewReader(listBytes)
 | 
			
		||||
	var entry []byte
 | 
			
		||||
	for err == nil {
 | 
			
		||||
		entry, err = readVarBytes(listReader, elementLenBytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != io.EOF {
 | 
			
		||||
				return []ASN1Cert{}, err
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			list.PushBack(entry)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ret := make([]ASN1Cert, list.Len())
 | 
			
		||||
	i := 0
 | 
			
		||||
	for e := list.Front(); e != nil; e = e.Next() {
 | 
			
		||||
		ret[i] = e.Value.([]byte)
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	return ret, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadTimestampedEntryInto parses the byte-stream representation of a
 | 
			
		||||
// TimestampedEntry from |r| and populates the struct |t| with the data.  See
 | 
			
		||||
// RFC section 3.4 for details on the format.
 | 
			
		||||
// Returns a non-nil error if there was a problem.
 | 
			
		||||
func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	if err = binary.Read(r, binary.BigEndian, &t.Timestamp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err = binary.Read(r, binary.BigEndian, &t.EntryType); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	switch t.EntryType {
 | 
			
		||||
	case X509LogEntryType:
 | 
			
		||||
		if t.X509Entry, err = readVarBytes(r, CertificateLengthBytes); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	case PrecertLogEntryType:
 | 
			
		||||
		if err := binary.Read(r, binary.BigEndian, &t.PrecertEntry.IssuerKeyHash); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if t.PrecertEntry.TBSCertificate, err = readVarBytes(r, PreCertificateLengthBytes); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("unknown EntryType: %d", t.EntryType)
 | 
			
		||||
	}
 | 
			
		||||
	t.Extensions, err = readVarBytes(r, ExtensionsLengthBytes)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadMerkleTreeLeaf parses the byte-stream representation of a MerkleTreeLeaf
 | 
			
		||||
// and returns a pointer to a new MerkleTreeLeaf structure containing the
 | 
			
		||||
// parsed data.
 | 
			
		||||
// See RFC section 3.4 for details on the format.
 | 
			
		||||
// Returns a pointer to a new MerkleTreeLeaf or non-nil error if there was a
 | 
			
		||||
// problem
 | 
			
		||||
func ReadMerkleTreeLeaf(r io.Reader) (*MerkleTreeLeaf, error) {
 | 
			
		||||
	var m MerkleTreeLeaf
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &m.Version); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if m.Version != V1 {
 | 
			
		||||
		return nil, fmt.Errorf("unknown Version %d", m.Version)
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &m.LeafType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if m.LeafType != TimestampedEntryLeafType {
 | 
			
		||||
		return nil, fmt.Errorf("unknown LeafType %d", m.LeafType)
 | 
			
		||||
	}
 | 
			
		||||
	if err := ReadTimestampedEntryInto(r, &m.TimestampedEntry); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &m, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalX509ChainArray unmarshalls the contents of the "chain:" entry in a
 | 
			
		||||
// GetEntries response in the case where the entry refers to an X509 leaf.
 | 
			
		||||
func UnmarshalX509ChainArray(b []byte) ([]ASN1Cert, error) {
 | 
			
		||||
	return readASN1CertList(bytes.NewReader(b), CertificateChainLengthBytes, CertificateLengthBytes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalPrecertChainArray unmarshalls the contents of the "chain:" entry in
 | 
			
		||||
// a GetEntries response in the case where the entry refers to a Precertificate
 | 
			
		||||
// leaf.
 | 
			
		||||
func UnmarshalPrecertChainArray(b []byte) ([]ASN1Cert, error) {
 | 
			
		||||
	var chain []ASN1Cert
 | 
			
		||||
 | 
			
		||||
	reader := bytes.NewReader(b)
 | 
			
		||||
	// read the pre-cert entry:
 | 
			
		||||
	precert, err := readVarBytes(reader, CertificateLengthBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return chain, err
 | 
			
		||||
	}
 | 
			
		||||
	chain = append(chain, precert)
 | 
			
		||||
	// and then read and return the chain up to the root:
 | 
			
		||||
	remainingChain, err := readASN1CertList(reader, CertificateChainLengthBytes, CertificateLengthBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return chain, err
 | 
			
		||||
	}
 | 
			
		||||
	chain = append(chain, remainingChain...)
 | 
			
		||||
	return chain, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalDigitallySigned reconstructs a DigitallySigned structure from a Reader
 | 
			
		||||
func UnmarshalDigitallySigned(r io.Reader) (*DigitallySigned, error) {
 | 
			
		||||
	var h byte
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &h); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to read HashAlgorithm: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var s byte
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &s); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to read SignatureAlgorithm: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sig, err := readVarBytes(r, SignatureLengthBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to read Signature bytes: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &DigitallySigned{
 | 
			
		||||
		HashAlgorithm:      HashAlgorithm(h),
 | 
			
		||||
		SignatureAlgorithm: SignatureAlgorithm(s),
 | 
			
		||||
		Signature:          sig,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalDigitallySigned marshalls a DigitallySigned structure into a byte array
 | 
			
		||||
func MarshalDigitallySigned(ds DigitallySigned) ([]byte, error) {
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	if err := b.WriteByte(byte(ds.HashAlgorithm)); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to write HashAlgorithm: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := b.WriteByte(byte(ds.SignatureAlgorithm)); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to write SignatureAlgorithm: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := writeVarBytes(&b, ds.Signature, SignatureLengthBytes); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to write HashAlgorithm: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return b.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkCertificateFormat(cert ASN1Cert) error {
 | 
			
		||||
	if len(cert) == 0 {
 | 
			
		||||
		return errors.New("certificate is zero length")
 | 
			
		||||
	}
 | 
			
		||||
	if len(cert) > MaxCertificateLength {
 | 
			
		||||
		return errors.New("certificate too large")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkExtensionsFormat(ext CTExtensions) error {
 | 
			
		||||
	if len(ext) > MaxExtensionsLength {
 | 
			
		||||
		return errors.New("extensions too large")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serializeV1CertSCTSignatureInput(timestamp uint64, cert ASN1Cert, ext CTExtensions) ([]byte, error) {
 | 
			
		||||
	if err := checkCertificateFormat(cert); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := checkExtensionsFormat(ext); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, X509LogEntryType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writeVarBytes(&buf, cert, CertificateLengthBytes); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writeVarBytes(&buf, ext, ExtensionsLengthBytes); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serializeV1PrecertSCTSignatureInput(timestamp uint64, issuerKeyHash [issuerKeyHashLength]byte, tbs []byte, ext CTExtensions) ([]byte, error) {
 | 
			
		||||
	if err := checkCertificateFormat(tbs); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := checkExtensionsFormat(ext); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, PrecertLogEntryType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := buf.Write(issuerKeyHash[:]); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writeVarBytes(&buf, tbs, CertificateLengthBytes); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writeVarBytes(&buf, ext, ExtensionsLengthBytes); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
 | 
			
		||||
	if sct.SCTVersion != V1 {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported SCT version, expected V1, but got %s", sct.SCTVersion)
 | 
			
		||||
	}
 | 
			
		||||
	if entry.Leaf.LeafType != TimestampedEntryLeafType {
 | 
			
		||||
		return nil, fmt.Errorf("Unsupported leaf type %s", entry.Leaf.LeafType)
 | 
			
		||||
	}
 | 
			
		||||
	switch entry.Leaf.TimestampedEntry.EntryType {
 | 
			
		||||
	case X509LogEntryType:
 | 
			
		||||
		return serializeV1CertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.X509Entry, entry.Leaf.TimestampedEntry.Extensions)
 | 
			
		||||
	case PrecertLogEntryType:
 | 
			
		||||
		return serializeV1PrecertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash,
 | 
			
		||||
			entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate,
 | 
			
		||||
			entry.Leaf.TimestampedEntry.Extensions)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("unknown TimestampedEntryLeafType %s", entry.Leaf.TimestampedEntry.EntryType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SerializeSCTSignatureInput serializes the passed in sct and log entry into
 | 
			
		||||
// the correct format for signing.
 | 
			
		||||
func SerializeSCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
 | 
			
		||||
	switch sct.SCTVersion {
 | 
			
		||||
	case V1:
 | 
			
		||||
		return serializeV1SCTSignatureInput(sct, entry)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serializeV1SCT(sct SignedCertificateTimestamp) ([]byte, error) {
 | 
			
		||||
	if err := checkExtensionsFormat(sct.Extensions); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, sct.LogID); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, sct.Timestamp); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writeVarBytes(&buf, sct.Extensions, ExtensionsLengthBytes); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	sig, err := MarshalDigitallySigned(sct.Signature)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, sig); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SerializeSCT serializes the passed in sct into the format specified
 | 
			
		||||
// by RFC6962 section 3.2
 | 
			
		||||
func SerializeSCT(sct SignedCertificateTimestamp) ([]byte, error) {
 | 
			
		||||
	switch sct.SCTVersion {
 | 
			
		||||
	case V1:
 | 
			
		||||
		return serializeV1SCT(sct)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deserializeSCTV1(r io.Reader, sct *SignedCertificateTimestamp) error {
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &sct.LogID); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &sct.Timestamp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	ext, err := readVarBytes(r, ExtensionsLengthBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	sct.Extensions = ext
 | 
			
		||||
	ds, err := UnmarshalDigitallySigned(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	sct.Signature = *ds
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DeserializeSCT(r io.Reader) (*SignedCertificateTimestamp, error) {
 | 
			
		||||
	var sct SignedCertificateTimestamp
 | 
			
		||||
	if err := binary.Read(r, binary.BigEndian, &sct.SCTVersion); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	switch sct.SCTVersion {
 | 
			
		||||
	case V1:
 | 
			
		||||
		return &sct, deserializeSCTV1(r, &sct)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serializeV1STHSignatureInput(sth SignedTreeHead) ([]byte, error) {
 | 
			
		||||
	if sth.Version != V1 {
 | 
			
		||||
		return nil, fmt.Errorf("invalid STH version %d", sth.Version)
 | 
			
		||||
	}
 | 
			
		||||
	if sth.TreeSize < 0 {
 | 
			
		||||
		return nil, fmt.Errorf("invalid tree size %d", sth.TreeSize)
 | 
			
		||||
	}
 | 
			
		||||
	if len(sth.SHA256RootHash) != crypto.SHA256.Size() {
 | 
			
		||||
		return nil, fmt.Errorf("invalid TreeHash length, got %d expected %d", len(sth.SHA256RootHash), crypto.SHA256.Size())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, TreeHashSignatureType); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, sth.Timestamp); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, sth.TreeSize); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := binary.Write(&buf, binary.BigEndian, sth.SHA256RootHash); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return buf.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SerializeSTHSignatureInput serializes the passed in sth into the correct
 | 
			
		||||
// format for signing.
 | 
			
		||||
func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) {
 | 
			
		||||
	switch sth.Version {
 | 
			
		||||
	case V1:
 | 
			
		||||
		return serializeV1STHSignatureInput(sth)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("unsupported STH version %d", sth.Version)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								ct/signatures.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								ct/signatures.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,131 @@
 | 
			
		||||
package ct
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/asn1"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math/big"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var allowVerificationWithNonCompliantKeys = flag.Bool("allow_verification_with_non_compliant_keys", false,
 | 
			
		||||
	"Allow a SignatureVerifier to use keys which are technically non-compliant with RFC6962.")
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	p, rest := pem.Decode(b)
 | 
			
		||||
	if p == nil {
 | 
			
		||||
		return nil, [sha256.Size]byte{}, rest, fmt.Errorf("no PEM block found in %s", string(b))
 | 
			
		||||
	}
 | 
			
		||||
	k, err := x509.ParsePKIXPublicKey(p.Bytes)
 | 
			
		||||
	return k, sha256.Sum256(p.Bytes), rest, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignatureVerifier can verify signatures on SCTs and STHs
 | 
			
		||||
type SignatureVerifier struct {
 | 
			
		||||
	pubKey crypto.PublicKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSignatureVerifier creates a new SignatureVerifier using the passed in PublicKey.
 | 
			
		||||
func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) {
 | 
			
		||||
	switch pkType := pk.(type) {
 | 
			
		||||
	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) {
 | 
			
		||||
				return nil, e
 | 
			
		||||
			}
 | 
			
		||||
			log.Printf("WARNING: %v", e)
 | 
			
		||||
		}
 | 
			
		||||
	case *ecdsa.PublicKey:
 | 
			
		||||
		params := *(pkType.Params())
 | 
			
		||||
		if params != *elliptic.P256().Params() {
 | 
			
		||||
			e := fmt.Errorf("public is ECDSA, but not on the P256 curve")
 | 
			
		||||
			if !(*allowVerificationWithNonCompliantKeys) {
 | 
			
		||||
				return nil, e
 | 
			
		||||
			}
 | 
			
		||||
			log.Printf("WARNING: %v", e)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("Unsupported public key type %v", pkType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &SignatureVerifier{
 | 
			
		||||
		pubKey: pk,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// verifySignature verifies that the passed in signature over data was created by our PublicKey.
 | 
			
		||||
// Currently, only SHA256 is supported as a HashAlgorithm, and only ECDSA and RSA signatures are supported.
 | 
			
		||||
func (s SignatureVerifier) verifySignature(data []byte, sig DigitallySigned) error {
 | 
			
		||||
	if sig.HashAlgorithm != SHA256 {
 | 
			
		||||
		return fmt.Errorf("unsupported HashAlgorithm in signature: %v", sig.HashAlgorithm)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hasherType := crypto.SHA256
 | 
			
		||||
	hasher := hasherType.New()
 | 
			
		||||
	if _, err := hasher.Write(data); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to write to hasher: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	hash := hasher.Sum([]byte{})
 | 
			
		||||
 | 
			
		||||
	switch sig.SignatureAlgorithm {
 | 
			
		||||
	case RSA:
 | 
			
		||||
		rsaKey, ok := s.pubKey.(*rsa.PublicKey)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return fmt.Errorf("cannot verify RSA signature with %T key", s.pubKey)
 | 
			
		||||
		}
 | 
			
		||||
		if err := rsa.VerifyPKCS1v15(rsaKey, hasherType, hash, sig.Signature); err != nil {
 | 
			
		||||
			return fmt.Errorf("failed to verify rsa signature: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	case ECDSA:
 | 
			
		||||
		ecdsaKey, ok := s.pubKey.(*ecdsa.PublicKey)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return fmt.Errorf("cannot verify ECDSA signature with %T key", s.pubKey)
 | 
			
		||||
		}
 | 
			
		||||
		var ecdsaSig struct {
 | 
			
		||||
			R, S *big.Int
 | 
			
		||||
		}
 | 
			
		||||
		rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if len(rest) != 0 {
 | 
			
		||||
			log.Printf("Garbage following signature %v", rest)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !ecdsa.Verify(ecdsaKey, hash, ecdsaSig.R, ecdsaSig.S) {
 | 
			
		||||
			return errors.New("failed to verify ecdsa signature")
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("unsupported signature type %v", sig.SignatureAlgorithm)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VerifySCTSignature verifies that the SCT's signature is valid for the given LogEntry
 | 
			
		||||
func (s SignatureVerifier) VerifySCTSignature(sct SignedCertificateTimestamp, entry LogEntry) error {
 | 
			
		||||
	sctData, err := SerializeSCTSignatureInput(sct, entry)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.verifySignature(sctData, sct.Signature)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VerifySTHSignature verifies that the STH's signature is valid.
 | 
			
		||||
func (s SignatureVerifier) VerifySTHSignature(sth SignedTreeHead) error {
 | 
			
		||||
	sthData, err := SerializeSTHSignatureInput(sth)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.verifySignature(sthData, sth.TreeHeadSignature)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										323
									
								
								ct/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								ct/types.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,323 @@
 | 
			
		||||
package ct
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	issuerKeyHashLength = 32
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// The following structures represent those outlined in the RFC6962 document:
 | 
			
		||||
///////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// LogEntryType represents the LogEntryType enum from section 3.1 of the RFC:
 | 
			
		||||
//   enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType;
 | 
			
		||||
type LogEntryType uint16
 | 
			
		||||
 | 
			
		||||
func (e LogEntryType) String() string {
 | 
			
		||||
	switch e {
 | 
			
		||||
	case X509LogEntryType:
 | 
			
		||||
		return "X509LogEntryType"
 | 
			
		||||
	case PrecertLogEntryType:
 | 
			
		||||
		return "PrecertLogEntryType"
 | 
			
		||||
	}
 | 
			
		||||
	panic(fmt.Sprintf("No string defined for LogEntryType constant value %d", e))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LogEntryType constants, see section 3.1 of RFC6962.
 | 
			
		||||
const (
 | 
			
		||||
	X509LogEntryType    LogEntryType = 0
 | 
			
		||||
	PrecertLogEntryType LogEntryType = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MerkleLeafType represents the MerkleLeafType enum from section 3.4 of the
 | 
			
		||||
// RFC: enum { timestamped_entry(0), (255) } MerkleLeafType;
 | 
			
		||||
type MerkleLeafType uint8
 | 
			
		||||
 | 
			
		||||
func (m MerkleLeafType) String() string {
 | 
			
		||||
	switch m {
 | 
			
		||||
	case TimestampedEntryLeafType:
 | 
			
		||||
		return "TimestampedEntryLeafType"
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Sprintf("UnknownLeafType(%d)", m)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MerkleLeafType constants, see section 3.4 of the RFC.
 | 
			
		||||
const (
 | 
			
		||||
	TimestampedEntryLeafType MerkleLeafType = 0 // Entry type for an SCT
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version represents the Version enum from section 3.2 of the RFC:
 | 
			
		||||
// enum { v1(0), (255) } Version;
 | 
			
		||||
type Version uint8
 | 
			
		||||
 | 
			
		||||
func (v Version) String() string {
 | 
			
		||||
	switch v {
 | 
			
		||||
	case V1:
 | 
			
		||||
		return "V1"
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Sprintf("UnknownVersion(%d)", v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CT Version constants, see section 3.2 of the RFC.
 | 
			
		||||
const (
 | 
			
		||||
	V1 Version = 0
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SignatureType differentiates STH signatures from SCT signatures, see RFC
 | 
			
		||||
// section 3.2
 | 
			
		||||
type SignatureType uint8
 | 
			
		||||
 | 
			
		||||
func (st SignatureType) String() string {
 | 
			
		||||
	switch st {
 | 
			
		||||
	case CertificateTimestampSignatureType:
 | 
			
		||||
		return "CertificateTimestamp"
 | 
			
		||||
	case TreeHashSignatureType:
 | 
			
		||||
		return "TreeHash"
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Sprintf("UnknownSignatureType(%d)", st)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignatureType constants, see RFC section 3.2
 | 
			
		||||
const (
 | 
			
		||||
	CertificateTimestampSignatureType SignatureType = 0
 | 
			
		||||
	TreeHashSignatureType             SignatureType = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ASN1Cert type for holding the raw DER bytes of an ASN.1 Certificate
 | 
			
		||||
// (section 3.1)
 | 
			
		||||
type ASN1Cert []byte
 | 
			
		||||
 | 
			
		||||
// PreCert represents a Precertificate (section 3.2)
 | 
			
		||||
type PreCert struct {
 | 
			
		||||
	IssuerKeyHash  [issuerKeyHashLength]byte
 | 
			
		||||
	TBSCertificate []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CTExtensions is a representation of the raw bytes of any CtExtension
 | 
			
		||||
// structure (see section 3.2)
 | 
			
		||||
type CTExtensions []byte
 | 
			
		||||
 | 
			
		||||
// MerkleTreeNode represents an internal node in the CT tree
 | 
			
		||||
type MerkleTreeNode []byte
 | 
			
		||||
 | 
			
		||||
// ConsistencyProof represents a CT consistency proof (see sections 2.1.2 and
 | 
			
		||||
// 4.4)
 | 
			
		||||
type ConsistencyProof []MerkleTreeNode
 | 
			
		||||
 | 
			
		||||
// AuditPath represents a CT inclusion proof (see sections 2.1.1 and 4.5)
 | 
			
		||||
type AuditPath []MerkleTreeNode
 | 
			
		||||
 | 
			
		||||
// LeafInput represents a serialized MerkleTreeLeaf structure
 | 
			
		||||
type LeafInput []byte
 | 
			
		||||
 | 
			
		||||
// HashAlgorithm from the DigitallySigned struct
 | 
			
		||||
type HashAlgorithm byte
 | 
			
		||||
 | 
			
		||||
// HashAlgorithm constants
 | 
			
		||||
const (
 | 
			
		||||
	None   HashAlgorithm = 0
 | 
			
		||||
	MD5    HashAlgorithm = 1
 | 
			
		||||
	SHA1   HashAlgorithm = 2
 | 
			
		||||
	SHA224 HashAlgorithm = 3
 | 
			
		||||
	SHA256 HashAlgorithm = 4
 | 
			
		||||
	SHA384 HashAlgorithm = 5
 | 
			
		||||
	SHA512 HashAlgorithm = 6
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (h HashAlgorithm) String() string {
 | 
			
		||||
	switch h {
 | 
			
		||||
	case None:
 | 
			
		||||
		return "None"
 | 
			
		||||
	case MD5:
 | 
			
		||||
		return "MD5"
 | 
			
		||||
	case SHA1:
 | 
			
		||||
		return "SHA1"
 | 
			
		||||
	case SHA224:
 | 
			
		||||
		return "SHA224"
 | 
			
		||||
	case SHA256:
 | 
			
		||||
		return "SHA256"
 | 
			
		||||
	case SHA384:
 | 
			
		||||
		return "SHA384"
 | 
			
		||||
	case SHA512:
 | 
			
		||||
		return "SHA512"
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Sprintf("UNKNOWN(%d)", h)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignatureAlgorithm from the the DigitallySigned struct
 | 
			
		||||
type SignatureAlgorithm byte
 | 
			
		||||
 | 
			
		||||
// SignatureAlgorithm constants
 | 
			
		||||
const (
 | 
			
		||||
	Anonymous SignatureAlgorithm = 0
 | 
			
		||||
	RSA       SignatureAlgorithm = 1
 | 
			
		||||
	DSA       SignatureAlgorithm = 2
 | 
			
		||||
	ECDSA     SignatureAlgorithm = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s SignatureAlgorithm) String() string {
 | 
			
		||||
	switch s {
 | 
			
		||||
	case Anonymous:
 | 
			
		||||
		return "Anonymous"
 | 
			
		||||
	case RSA:
 | 
			
		||||
		return "RSA"
 | 
			
		||||
	case DSA:
 | 
			
		||||
		return "DSA"
 | 
			
		||||
	case ECDSA:
 | 
			
		||||
		return "ECDSA"
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Sprintf("UNKNOWN(%d)", s)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DigitallySigned represents an RFC5246 DigitallySigned structure
 | 
			
		||||
type DigitallySigned struct {
 | 
			
		||||
	HashAlgorithm      HashAlgorithm
 | 
			
		||||
	SignatureAlgorithm SignatureAlgorithm
 | 
			
		||||
	Signature          []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FromBase64String populates the DigitallySigned structure from the base64 data passed in.
 | 
			
		||||
// Returns an error if the base64 data is invalid.
 | 
			
		||||
func (d *DigitallySigned) FromBase64String(b64 string) error {
 | 
			
		||||
	raw, err := base64.StdEncoding.DecodeString(b64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	ds, err := UnmarshalDigitallySigned(bytes.NewReader(raw))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	*d = *ds
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base64String returns the base64 representation of the DigitallySigned struct.
 | 
			
		||||
func (d DigitallySigned) Base64String() (string, error) {
 | 
			
		||||
	b, err := MarshalDigitallySigned(d)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(b), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements the json.Marshaller interface.
 | 
			
		||||
func (d DigitallySigned) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	b64, err := d.Base64String()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []byte{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return []byte(`"` + b64 + `"`), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements the json.Unmarshaler interface.
 | 
			
		||||
func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	var content string
 | 
			
		||||
	if err := json.Unmarshal(b, &content); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return d.FromBase64String(content)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LogEntry represents the contents of an entry in a CT log, see section 3.1.
 | 
			
		||||
type LogEntry struct {
 | 
			
		||||
	Index    int64
 | 
			
		||||
	Leaf     MerkleTreeLeaf
 | 
			
		||||
	Chain    []ASN1Cert
 | 
			
		||||
	LeafBytes []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SHA256Hash represents the output from the SHA256 hash function.
 | 
			
		||||
type SHA256Hash [sha256.Size]byte
 | 
			
		||||
 | 
			
		||||
// FromBase64String populates the SHA256 struct with the contents of the base64 data passed in.
 | 
			
		||||
func (s *SHA256Hash) FromBase64String(b64 string) error {
 | 
			
		||||
	bs, err := base64.StdEncoding.DecodeString(b64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unbase64 LogID: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(bs) != sha256.Size {
 | 
			
		||||
		return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs))
 | 
			
		||||
	}
 | 
			
		||||
	copy(s[:], bs)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base64String returns the base64 representation of this SHA256Hash.
 | 
			
		||||
func (s SHA256Hash) Base64String() string {
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(s[:])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements the json.Marshaller interface for SHA256Hash.
 | 
			
		||||
func (s SHA256Hash) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	return []byte(`"` + s.Base64String() + `"`), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements the json.Unmarshaller interface.
 | 
			
		||||
func (s *SHA256Hash) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	var content string
 | 
			
		||||
	if err := json.Unmarshal(b, &content); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return s.FromBase64String(content)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignedTreeHead represents the structure returned by the get-sth CT method
 | 
			
		||||
// after base64 decoding. See sections 3.5 and 4.3 in the RFC)
 | 
			
		||||
type SignedTreeHead struct {
 | 
			
		||||
	Version           Version         `json:"sth_version"`         // The version of the protocol to which the STH conforms
 | 
			
		||||
	TreeSize          uint64          `json:"tree_size"`           // The number of entries in the new tree
 | 
			
		||||
	Timestamp         uint64          `json:"timestamp"`           // The time at which the STH was created
 | 
			
		||||
	SHA256RootHash    SHA256Hash      `json:"sha256_root_hash"`    // The root hash of the log's Merkle tree
 | 
			
		||||
	TreeHeadSignature DigitallySigned `json:"tree_head_signature"` // The Log's signature for this STH (see RFC section 3.5)
 | 
			
		||||
	LogID             SHA256Hash      `json:"log_id"`              // The SHA256 hash of the log's public key
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignedCertificateTimestamp represents the structure returned by the
 | 
			
		||||
// add-chain and add-pre-chain methods after base64 decoding. (see RFC sections
 | 
			
		||||
// 3.2 ,4.1 and 4.2)
 | 
			
		||||
type SignedCertificateTimestamp struct {
 | 
			
		||||
	SCTVersion Version    // The version of the protocol to which the SCT conforms
 | 
			
		||||
	LogID      SHA256Hash // the SHA-256 hash of the log's public key, calculated over
 | 
			
		||||
	// the DER encoding of the key represented as SubjectPublicKeyInfo.
 | 
			
		||||
	Timestamp  uint64          // Timestamp (in ms since unix epoc) at which the SCT was issued
 | 
			
		||||
	Extensions CTExtensions    // For future extensions to the protocol
 | 
			
		||||
	Signature  DigitallySigned // The Log's signature for this SCT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SignedCertificateTimestamp) String() string {
 | 
			
		||||
	return fmt.Sprintf("{Version:%d LogId:%s Timestamp:%d Extensions:'%s' Signature:%v}", s.SCTVersion,
 | 
			
		||||
		base64.StdEncoding.EncodeToString(s.LogID[:]),
 | 
			
		||||
		s.Timestamp,
 | 
			
		||||
		s.Extensions,
 | 
			
		||||
		s.Signature)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TimestampedEntry is part of the MerkleTreeLeaf structure.
 | 
			
		||||
// See RFC section 3.4
 | 
			
		||||
type TimestampedEntry struct {
 | 
			
		||||
	Timestamp    uint64
 | 
			
		||||
	EntryType    LogEntryType
 | 
			
		||||
	X509Entry    ASN1Cert
 | 
			
		||||
	PrecertEntry PreCert
 | 
			
		||||
	Extensions   CTExtensions
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MerkleTreeLeaf represents the deserialized sructure of the hash input for the
 | 
			
		||||
// leaves of a log's Merkle tree. See RFC section 3.4
 | 
			
		||||
type MerkleTreeLeaf struct {
 | 
			
		||||
	Version          Version          // the version of the protocol to which the MerkleTreeLeaf corresponds
 | 
			
		||||
	LeafType         MerkleLeafType   // The type of the leaf input, currently only TimestampedEntry can exist
 | 
			
		||||
	TimestampedEntry TimestampedEntry // The entry data itself
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								ct/x509/cert_pool.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								ct/x509/cert_pool.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CertPool is a set of certificates.
 | 
			
		||||
type CertPool struct {
 | 
			
		||||
	bySubjectKeyId map[string][]int
 | 
			
		||||
	byName         map[string][]int
 | 
			
		||||
	certs          []*Certificate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCertPool returns a new, empty CertPool.
 | 
			
		||||
func NewCertPool() *CertPool {
 | 
			
		||||
	return &CertPool{
 | 
			
		||||
		make(map[string][]int),
 | 
			
		||||
		make(map[string][]int),
 | 
			
		||||
		nil,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findVerifiedParents attempts to find certificates in s which have signed the
 | 
			
		||||
// given certificate. If any candidates were rejected then errCert will be set
 | 
			
		||||
// to one of them, arbitrarily, and err will contain the reason that it was
 | 
			
		||||
// rejected.
 | 
			
		||||
func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
 | 
			
		||||
	if s == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var candidates []int
 | 
			
		||||
 | 
			
		||||
	if len(cert.AuthorityKeyId) > 0 {
 | 
			
		||||
		candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
 | 
			
		||||
	}
 | 
			
		||||
	if len(candidates) == 0 {
 | 
			
		||||
		candidates = s.byName[string(cert.RawIssuer)]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range candidates {
 | 
			
		||||
		if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
 | 
			
		||||
			parents = append(parents, c)
 | 
			
		||||
		} else {
 | 
			
		||||
			errCert = s.certs[c]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddCert adds a certificate to a pool.
 | 
			
		||||
func (s *CertPool) AddCert(cert *Certificate) {
 | 
			
		||||
	if cert == nil {
 | 
			
		||||
		panic("adding nil Certificate to CertPool")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check that the certificate isn't being added twice.
 | 
			
		||||
	for _, c := range s.certs {
 | 
			
		||||
		if c.Equal(cert) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	n := len(s.certs)
 | 
			
		||||
	s.certs = append(s.certs, cert)
 | 
			
		||||
 | 
			
		||||
	if len(cert.SubjectKeyId) > 0 {
 | 
			
		||||
		keyId := string(cert.SubjectKeyId)
 | 
			
		||||
		s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
 | 
			
		||||
	}
 | 
			
		||||
	name := string(cert.RawSubject)
 | 
			
		||||
	s.byName[name] = append(s.byName[name], n)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
 | 
			
		||||
// It appends any certificates found to s and returns true if any certificates
 | 
			
		||||
// were successfully parsed.
 | 
			
		||||
//
 | 
			
		||||
// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
 | 
			
		||||
// of root CAs in a format suitable for this function.
 | 
			
		||||
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
 | 
			
		||||
	for len(pemCerts) > 0 {
 | 
			
		||||
		var block *pem.Block
 | 
			
		||||
		block, pemCerts = pem.Decode(pemCerts)
 | 
			
		||||
		if block == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cert, err := ParseCertificate(block.Bytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.AddCert(cert)
 | 
			
		||||
		ok = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Subjects returns a list of the DER-encoded subjects of
 | 
			
		||||
// all of the certificates in the pool.
 | 
			
		||||
func (s *CertPool) Subjects() (res [][]byte) {
 | 
			
		||||
	res = make([][]byte, len(s.certs))
 | 
			
		||||
	for i, c := range s.certs {
 | 
			
		||||
		res[i] = c.RawSubject
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										233
									
								
								ct/x509/pem_decrypt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								ct/x509/pem_decrypt.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,233 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
 | 
			
		||||
// generate a key from the password was derived by looking at the OpenSSL
 | 
			
		||||
// implementation.
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/des"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PEMCipher int
 | 
			
		||||
 | 
			
		||||
// Possible values for the EncryptPEMBlock encryption algorithm.
 | 
			
		||||
const (
 | 
			
		||||
	_ PEMCipher = iota
 | 
			
		||||
	PEMCipherDES
 | 
			
		||||
	PEMCipher3DES
 | 
			
		||||
	PEMCipherAES128
 | 
			
		||||
	PEMCipherAES192
 | 
			
		||||
	PEMCipherAES256
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// rfc1423Algo holds a method for enciphering a PEM block.
 | 
			
		||||
type rfc1423Algo struct {
 | 
			
		||||
	cipher     PEMCipher
 | 
			
		||||
	name       string
 | 
			
		||||
	cipherFunc func(key []byte) (cipher.Block, error)
 | 
			
		||||
	keySize    int
 | 
			
		||||
	blockSize  int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
 | 
			
		||||
// block.  The ivSize numbers were taken from the OpenSSL source.
 | 
			
		||||
var rfc1423Algos = []rfc1423Algo{{
 | 
			
		||||
	cipher:     PEMCipherDES,
 | 
			
		||||
	name:       "DES-CBC",
 | 
			
		||||
	cipherFunc: des.NewCipher,
 | 
			
		||||
	keySize:    8,
 | 
			
		||||
	blockSize:  des.BlockSize,
 | 
			
		||||
}, {
 | 
			
		||||
	cipher:     PEMCipher3DES,
 | 
			
		||||
	name:       "DES-EDE3-CBC",
 | 
			
		||||
	cipherFunc: des.NewTripleDESCipher,
 | 
			
		||||
	keySize:    24,
 | 
			
		||||
	blockSize:  des.BlockSize,
 | 
			
		||||
}, {
 | 
			
		||||
	cipher:     PEMCipherAES128,
 | 
			
		||||
	name:       "AES-128-CBC",
 | 
			
		||||
	cipherFunc: aes.NewCipher,
 | 
			
		||||
	keySize:    16,
 | 
			
		||||
	blockSize:  aes.BlockSize,
 | 
			
		||||
}, {
 | 
			
		||||
	cipher:     PEMCipherAES192,
 | 
			
		||||
	name:       "AES-192-CBC",
 | 
			
		||||
	cipherFunc: aes.NewCipher,
 | 
			
		||||
	keySize:    24,
 | 
			
		||||
	blockSize:  aes.BlockSize,
 | 
			
		||||
}, {
 | 
			
		||||
	cipher:     PEMCipherAES256,
 | 
			
		||||
	name:       "AES-256-CBC",
 | 
			
		||||
	cipherFunc: aes.NewCipher,
 | 
			
		||||
	keySize:    32,
 | 
			
		||||
	blockSize:  aes.BlockSize,
 | 
			
		||||
},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// deriveKey uses a key derivation function to stretch the password into a key
 | 
			
		||||
// with the number of bits our cipher requires. This algorithm was derived from
 | 
			
		||||
// the OpenSSL source.
 | 
			
		||||
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
 | 
			
		||||
	hash := md5.New()
 | 
			
		||||
	out := make([]byte, c.keySize)
 | 
			
		||||
	var digest []byte
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(out); i += len(digest) {
 | 
			
		||||
		hash.Reset()
 | 
			
		||||
		hash.Write(digest)
 | 
			
		||||
		hash.Write(password)
 | 
			
		||||
		hash.Write(salt)
 | 
			
		||||
		digest = hash.Sum(digest[:0])
 | 
			
		||||
		copy(out[i:], digest)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
 | 
			
		||||
func IsEncryptedPEMBlock(b *pem.Block) bool {
 | 
			
		||||
	_, ok := b.Headers["DEK-Info"]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncorrectPasswordError is returned when an incorrect password is detected.
 | 
			
		||||
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
 | 
			
		||||
 | 
			
		||||
// DecryptPEMBlock takes a password encrypted PEM block and the password used to
 | 
			
		||||
// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
 | 
			
		||||
// the DEK-Info header to determine the algorithm used for decryption. If no
 | 
			
		||||
// DEK-Info header is present, an error is returned. If an incorrect password
 | 
			
		||||
// is detected an IncorrectPasswordError is returned.
 | 
			
		||||
func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
 | 
			
		||||
	dek, ok := b.Headers["DEK-Info"]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("x509: no DEK-Info header in block")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	idx := strings.Index(dek, ",")
 | 
			
		||||
	if idx == -1 {
 | 
			
		||||
		return nil, errors.New("x509: malformed DEK-Info header")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mode, hexIV := dek[:idx], dek[idx+1:]
 | 
			
		||||
	ciph := cipherByName(mode)
 | 
			
		||||
	if ciph == nil {
 | 
			
		||||
		return nil, errors.New("x509: unknown encryption mode")
 | 
			
		||||
	}
 | 
			
		||||
	iv, err := hex.DecodeString(hexIV)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(iv) != ciph.blockSize {
 | 
			
		||||
		return nil, errors.New("x509: incorrect IV size")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Based on the OpenSSL implementation. The salt is the first 8 bytes
 | 
			
		||||
	// of the initialization vector.
 | 
			
		||||
	key := ciph.deriveKey(password, iv[:8])
 | 
			
		||||
	block, err := ciph.cipherFunc(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data := make([]byte, len(b.Bytes))
 | 
			
		||||
	dec := cipher.NewCBCDecrypter(block, iv)
 | 
			
		||||
	dec.CryptBlocks(data, b.Bytes)
 | 
			
		||||
 | 
			
		||||
	// Blocks are padded using a scheme where the last n bytes of padding are all
 | 
			
		||||
	// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
 | 
			
		||||
	// For example:
 | 
			
		||||
	//	[x y z 2 2]
 | 
			
		||||
	//	[x y 7 7 7 7 7 7 7]
 | 
			
		||||
	// If we detect a bad padding, we assume it is an invalid password.
 | 
			
		||||
	dlen := len(data)
 | 
			
		||||
	if dlen == 0 || dlen%ciph.blockSize != 0 {
 | 
			
		||||
		return nil, errors.New("x509: invalid padding")
 | 
			
		||||
	}
 | 
			
		||||
	last := int(data[dlen-1])
 | 
			
		||||
	if dlen < last {
 | 
			
		||||
		return nil, IncorrectPasswordError
 | 
			
		||||
	}
 | 
			
		||||
	if last == 0 || last > ciph.blockSize {
 | 
			
		||||
		return nil, IncorrectPasswordError
 | 
			
		||||
	}
 | 
			
		||||
	for _, val := range data[dlen-last:] {
 | 
			
		||||
		if int(val) != last {
 | 
			
		||||
			return nil, IncorrectPasswordError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return data[:dlen-last], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncryptPEMBlock returns a PEM block of the specified type holding the
 | 
			
		||||
// given DER-encoded data encrypted with the specified algorithm and
 | 
			
		||||
// password.
 | 
			
		||||
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
 | 
			
		||||
	ciph := cipherByKey(alg)
 | 
			
		||||
	if ciph == nil {
 | 
			
		||||
		return nil, errors.New("x509: unknown encryption mode")
 | 
			
		||||
	}
 | 
			
		||||
	iv := make([]byte, ciph.blockSize)
 | 
			
		||||
	if _, err := io.ReadFull(rand, iv); err != nil {
 | 
			
		||||
		return nil, errors.New("x509: cannot generate IV: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	// The salt is the first 8 bytes of the initialization vector,
 | 
			
		||||
	// matching the key derivation in DecryptPEMBlock.
 | 
			
		||||
	key := ciph.deriveKey(password, iv[:8])
 | 
			
		||||
	block, err := ciph.cipherFunc(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	enc := cipher.NewCBCEncrypter(block, iv)
 | 
			
		||||
	pad := ciph.blockSize - len(data)%ciph.blockSize
 | 
			
		||||
	encrypted := make([]byte, len(data), len(data)+pad)
 | 
			
		||||
	// We could save this copy by encrypting all the whole blocks in
 | 
			
		||||
	// the data separately, but it doesn't seem worth the additional
 | 
			
		||||
	// code.
 | 
			
		||||
	copy(encrypted, data)
 | 
			
		||||
	// See RFC 1423, section 1.1
 | 
			
		||||
	for i := 0; i < pad; i++ {
 | 
			
		||||
		encrypted = append(encrypted, byte(pad))
 | 
			
		||||
	}
 | 
			
		||||
	enc.CryptBlocks(encrypted, encrypted)
 | 
			
		||||
 | 
			
		||||
	return &pem.Block{
 | 
			
		||||
		Type: blockType,
 | 
			
		||||
		Headers: map[string]string{
 | 
			
		||||
			"Proc-Type": "4,ENCRYPTED",
 | 
			
		||||
			"DEK-Info":  ciph.name + "," + hex.EncodeToString(iv),
 | 
			
		||||
		},
 | 
			
		||||
		Bytes: encrypted,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cipherByName(name string) *rfc1423Algo {
 | 
			
		||||
	for i := range rfc1423Algos {
 | 
			
		||||
		alg := &rfc1423Algos[i]
 | 
			
		||||
		if alg.name == name {
 | 
			
		||||
			return alg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cipherByKey(key PEMCipher) *rfc1423Algo {
 | 
			
		||||
	for i := range rfc1423Algos {
 | 
			
		||||
		alg := &rfc1423Algos[i]
 | 
			
		||||
		if alg.cipher == key {
 | 
			
		||||
			return alg
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										223
									
								
								ct/x509/pem_decrypt_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								ct/x509/pem_decrypt_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,223 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestDecrypt(t *testing.T) {
 | 
			
		||||
	for i, data := range testData {
 | 
			
		||||
		t.Logf("test %d. %s", i, data.kind)
 | 
			
		||||
		block, rest := pem.Decode(data.pemData)
 | 
			
		||||
		if len(rest) > 0 {
 | 
			
		||||
			t.Error("extra data")
 | 
			
		||||
		}
 | 
			
		||||
		der, err := DecryptPEMBlock(block, data.password)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Error("decrypt failed: ", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := ParsePKCS1PrivateKey(der); err != nil {
 | 
			
		||||
			t.Error("invalid private key: ", err)
 | 
			
		||||
		}
 | 
			
		||||
		plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal("cannot decode test DER data: ", err)
 | 
			
		||||
		}
 | 
			
		||||
		if !bytes.Equal(der, plainDER) {
 | 
			
		||||
			t.Error("data mismatch")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEncrypt(t *testing.T) {
 | 
			
		||||
	for i, data := range testData {
 | 
			
		||||
		t.Logf("test %d. %s", i, data.kind)
 | 
			
		||||
		plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal("cannot decode test DER data: ", err)
 | 
			
		||||
		}
 | 
			
		||||
		password := []byte("kremvax1")
 | 
			
		||||
		block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Error("encrypt: ", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !IsEncryptedPEMBlock(block) {
 | 
			
		||||
			t.Error("PEM block does not appear to be encrypted")
 | 
			
		||||
		}
 | 
			
		||||
		if block.Type != "RSA PRIVATE KEY" {
 | 
			
		||||
			t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY")
 | 
			
		||||
		}
 | 
			
		||||
		if block.Headers["Proc-Type"] != "4,ENCRYPTED" {
 | 
			
		||||
			t.Errorf("block does not have correct Proc-Type header")
 | 
			
		||||
		}
 | 
			
		||||
		der, err := DecryptPEMBlock(block, password)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Error("decrypt: ", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if !bytes.Equal(der, plainDER) {
 | 
			
		||||
			t.Errorf("data mismatch")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var testData = []struct {
 | 
			
		||||
	kind     PEMCipher
 | 
			
		||||
	password []byte
 | 
			
		||||
	pemData  []byte
 | 
			
		||||
	plainDER string
 | 
			
		||||
}{
 | 
			
		||||
	{
 | 
			
		||||
		kind:     PEMCipherDES,
 | 
			
		||||
		password: []byte("asdf"),
 | 
			
		||||
		pemData: []byte(`
 | 
			
		||||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
Proc-Type: 4,ENCRYPTED
 | 
			
		||||
DEK-Info: DES-CBC,34F09A4FC8DE22B5
 | 
			
		||||
 | 
			
		||||
WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5
 | 
			
		||||
ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc
 | 
			
		||||
6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx
 | 
			
		||||
qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz
 | 
			
		||||
XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm
 | 
			
		||||
4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD
 | 
			
		||||
r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug==
 | 
			
		||||
-----END RSA PRIVATE KEY-----`),
 | 
			
		||||
		plainDER: `
 | 
			
		||||
MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo
 | 
			
		||||
KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf
 | 
			
		||||
c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ
 | 
			
		||||
QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv
 | 
			
		||||
7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w
 | 
			
		||||
glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA
 | 
			
		||||
9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		kind:     PEMCipher3DES,
 | 
			
		||||
		password: []byte("asdf"),
 | 
			
		||||
		pemData: []byte(`
 | 
			
		||||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
Proc-Type: 4,ENCRYPTED
 | 
			
		||||
DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7
 | 
			
		||||
 | 
			
		||||
0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ
 | 
			
		||||
YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x
 | 
			
		||||
8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk
 | 
			
		||||
Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB
 | 
			
		||||
ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w
 | 
			
		||||
3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq
 | 
			
		||||
gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk=
 | 
			
		||||
-----END RSA PRIVATE KEY-----`),
 | 
			
		||||
		plainDER: `
 | 
			
		||||
MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5
 | 
			
		||||
NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl
 | 
			
		||||
B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef
 | 
			
		||||
QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx
 | 
			
		||||
fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+
 | 
			
		||||
eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX
 | 
			
		||||
tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		kind:     PEMCipherAES128,
 | 
			
		||||
		password: []byte("asdf"),
 | 
			
		||||
		pemData: []byte(`
 | 
			
		||||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
Proc-Type: 4,ENCRYPTED
 | 
			
		||||
DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A
 | 
			
		||||
 | 
			
		||||
EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE
 | 
			
		||||
ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE
 | 
			
		||||
GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D
 | 
			
		||||
33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs
 | 
			
		||||
3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E
 | 
			
		||||
080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo
 | 
			
		||||
AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0=
 | 
			
		||||
-----END RSA PRIVATE KEY-----`),
 | 
			
		||||
		plainDER: `
 | 
			
		||||
MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT
 | 
			
		||||
cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3
 | 
			
		||||
k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE
 | 
			
		||||
cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB
 | 
			
		||||
kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78
 | 
			
		||||
XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR
 | 
			
		||||
B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		kind:     PEMCipherAES192,
 | 
			
		||||
		password: []byte("asdf"),
 | 
			
		||||
		pemData: []byte(`
 | 
			
		||||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
Proc-Type: 4,ENCRYPTED
 | 
			
		||||
DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369
 | 
			
		||||
 | 
			
		||||
cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5
 | 
			
		||||
FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM
 | 
			
		||||
Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs
 | 
			
		||||
Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG
 | 
			
		||||
ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6
 | 
			
		||||
xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t
 | 
			
		||||
Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw=
 | 
			
		||||
-----END RSA PRIVATE KEY-----`),
 | 
			
		||||
		plainDER: `
 | 
			
		||||
MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs
 | 
			
		||||
OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj
 | 
			
		||||
IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX
 | 
			
		||||
qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn
 | 
			
		||||
J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P
 | 
			
		||||
uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD
 | 
			
		||||
+qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		kind:     PEMCipherAES256,
 | 
			
		||||
		password: []byte("asdf"),
 | 
			
		||||
		pemData: []byte(`
 | 
			
		||||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
Proc-Type: 4,ENCRYPTED
 | 
			
		||||
DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD
 | 
			
		||||
 | 
			
		||||
4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf
 | 
			
		||||
JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr
 | 
			
		||||
DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7
 | 
			
		||||
Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/
 | 
			
		||||
2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N
 | 
			
		||||
sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk
 | 
			
		||||
clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0=
 | 
			
		||||
-----END RSA PRIVATE KEY-----`),
 | 
			
		||||
		plainDER: `
 | 
			
		||||
MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY
 | 
			
		||||
AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F
 | 
			
		||||
Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id
 | 
			
		||||
oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ
 | 
			
		||||
nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF
 | 
			
		||||
aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq
 | 
			
		||||
PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// generated with:
 | 
			
		||||
		// openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128
 | 
			
		||||
		kind:     PEMCipherAES128,
 | 
			
		||||
		password: []byte("asdf"),
 | 
			
		||||
		pemData: []byte(`
 | 
			
		||||
-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
Proc-Type: 4,ENCRYPTED
 | 
			
		||||
DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
 | 
			
		||||
 | 
			
		||||
6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3
 | 
			
		||||
eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD
 | 
			
		||||
hTP8O1mS/MHl92NE0nhv0w==
 | 
			
		||||
-----END RSA PRIVATE KEY-----`),
 | 
			
		||||
		plainDER: `
 | 
			
		||||
MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB
 | 
			
		||||
AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A
 | 
			
		||||
jryIst8=`,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										124
									
								
								ct/x509/pkcs1.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								ct/x509/pkcs1.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,124 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/asn1"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"errors"
 | 
			
		||||
	"math/big"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
 | 
			
		||||
type pkcs1PrivateKey struct {
 | 
			
		||||
	Version int
 | 
			
		||||
	N       *big.Int
 | 
			
		||||
	E       int
 | 
			
		||||
	D       *big.Int
 | 
			
		||||
	P       *big.Int
 | 
			
		||||
	Q       *big.Int
 | 
			
		||||
	// We ignore these values, if present, because rsa will calculate them.
 | 
			
		||||
	Dp   *big.Int `asn1:"optional"`
 | 
			
		||||
	Dq   *big.Int `asn1:"optional"`
 | 
			
		||||
	Qinv *big.Int `asn1:"optional"`
 | 
			
		||||
 | 
			
		||||
	AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type pkcs1AdditionalRSAPrime struct {
 | 
			
		||||
	Prime *big.Int
 | 
			
		||||
 | 
			
		||||
	// We ignore these values because rsa will calculate them.
 | 
			
		||||
	Exp   *big.Int
 | 
			
		||||
	Coeff *big.Int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
 | 
			
		||||
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
 | 
			
		||||
	var priv pkcs1PrivateKey
 | 
			
		||||
	rest, err := asn1.Unmarshal(der, &priv)
 | 
			
		||||
	if len(rest) > 0 {
 | 
			
		||||
		err = asn1.SyntaxError{Msg: "trailing data"}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if priv.Version > 1 {
 | 
			
		||||
		return nil, errors.New("x509: unsupported private key version")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
 | 
			
		||||
		return nil, errors.New("x509: private key contains zero or negative value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key = new(rsa.PrivateKey)
 | 
			
		||||
	key.PublicKey = rsa.PublicKey{
 | 
			
		||||
		E: priv.E,
 | 
			
		||||
		N: priv.N,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	key.D = priv.D
 | 
			
		||||
	key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
 | 
			
		||||
	key.Primes[0] = priv.P
 | 
			
		||||
	key.Primes[1] = priv.Q
 | 
			
		||||
	for i, a := range priv.AdditionalPrimes {
 | 
			
		||||
		if a.Prime.Sign() <= 0 {
 | 
			
		||||
			return nil, errors.New("x509: private key contains zero or negative prime")
 | 
			
		||||
		}
 | 
			
		||||
		key.Primes[i+2] = a.Prime
 | 
			
		||||
		// We ignore the other two values because rsa will calculate
 | 
			
		||||
		// them as needed.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = key.Validate()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	key.Precompute()
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
 | 
			
		||||
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
 | 
			
		||||
	key.Precompute()
 | 
			
		||||
 | 
			
		||||
	version := 0
 | 
			
		||||
	if len(key.Primes) > 2 {
 | 
			
		||||
		version = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv := pkcs1PrivateKey{
 | 
			
		||||
		Version: version,
 | 
			
		||||
		N:       key.N,
 | 
			
		||||
		E:       key.PublicKey.E,
 | 
			
		||||
		D:       key.D,
 | 
			
		||||
		P:       key.Primes[0],
 | 
			
		||||
		Q:       key.Primes[1],
 | 
			
		||||
		Dp:      key.Precomputed.Dp,
 | 
			
		||||
		Dq:      key.Precomputed.Dq,
 | 
			
		||||
		Qinv:    key.Precomputed.Qinv,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
 | 
			
		||||
	for i, values := range key.Precomputed.CRTValues {
 | 
			
		||||
		priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
 | 
			
		||||
		priv.AdditionalPrimes[i].Exp = values.Exp
 | 
			
		||||
		priv.AdditionalPrimes[i].Coeff = values.Coeff
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b, _ := asn1.Marshal(priv)
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key.
 | 
			
		||||
type rsaPublicKey struct {
 | 
			
		||||
	N *big.Int
 | 
			
		||||
	E int
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								ct/x509/pkcs8.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								ct/x509/pkcs8.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/asn1"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/x509/pkix"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
 | 
			
		||||
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
 | 
			
		||||
// and RFC5208.
 | 
			
		||||
type pkcs8 struct {
 | 
			
		||||
	Version    int
 | 
			
		||||
	Algo       pkix.AlgorithmIdentifier
 | 
			
		||||
	PrivateKey []byte
 | 
			
		||||
	// optional attributes omitted.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
 | 
			
		||||
// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
 | 
			
		||||
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
 | 
			
		||||
	var privKey pkcs8
 | 
			
		||||
	if _, err := asn1.Unmarshal(der, &privKey); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	switch {
 | 
			
		||||
	case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
 | 
			
		||||
		key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		return key, nil
 | 
			
		||||
 | 
			
		||||
	case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
 | 
			
		||||
		bytes := privKey.Algo.Parameters.FullBytes
 | 
			
		||||
		namedCurveOID := new(asn1.ObjectIdentifier)
 | 
			
		||||
		if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
 | 
			
		||||
			namedCurveOID = nil
 | 
			
		||||
		}
 | 
			
		||||
		key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		return key, nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								ct/x509/pkcs8_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								ct/x509/pkcs8_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
 | 
			
		||||
 | 
			
		||||
// Generated using:
 | 
			
		||||
//   openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
 | 
			
		||||
var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2`
 | 
			
		||||
 | 
			
		||||
func TestPKCS8(t *testing.T) {
 | 
			
		||||
	derBytes, _ := hex.DecodeString(pkcs8RSAPrivateKeyHex)
 | 
			
		||||
	if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
 | 
			
		||||
		t.Errorf("failed to decode PKCS8 with RSA private key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	derBytes, _ = hex.DecodeString(pkcs8ECPrivateKeyHex)
 | 
			
		||||
	if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
 | 
			
		||||
		t.Errorf("failed to decode PKCS8 with EC private key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										173
									
								
								ct/x509/pkix/pkix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								ct/x509/pkix/pkix.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,173 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
// Package pkix contains shared, low level structures used for ASN.1 parsing
 | 
			
		||||
// and serialization of X.509 certificates, CRL and OCSP.
 | 
			
		||||
package pkix
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/asn1"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
 | 
			
		||||
// 5280, section 4.1.1.2.
 | 
			
		||||
type AlgorithmIdentifier struct {
 | 
			
		||||
	Algorithm  asn1.ObjectIdentifier
 | 
			
		||||
	Parameters asn1.RawValue `asn1:"optional"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RDNSequence []RelativeDistinguishedNameSET
 | 
			
		||||
 | 
			
		||||
type RelativeDistinguishedNameSET []AttributeTypeAndValue
 | 
			
		||||
 | 
			
		||||
// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in
 | 
			
		||||
// http://tools.ietf.org/html/rfc5280#section-4.1.2.4
 | 
			
		||||
type AttributeTypeAndValue struct {
 | 
			
		||||
	Type  asn1.ObjectIdentifier
 | 
			
		||||
	Value interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Extension represents the ASN.1 structure of the same name. See RFC
 | 
			
		||||
// 5280, section 4.2.
 | 
			
		||||
type Extension struct {
 | 
			
		||||
	Id       asn1.ObjectIdentifier
 | 
			
		||||
	Critical bool `asn1:"optional"`
 | 
			
		||||
	Value    []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name represents an X.509 distinguished name. This only includes the common
 | 
			
		||||
// elements of a DN.  Additional elements in the name are ignored.
 | 
			
		||||
type Name struct {
 | 
			
		||||
	Country, Organization, OrganizationalUnit []string
 | 
			
		||||
	Locality, Province                        []string
 | 
			
		||||
	StreetAddress, PostalCode                 []string
 | 
			
		||||
	SerialNumber, CommonName                  string
 | 
			
		||||
 | 
			
		||||
	Names []AttributeTypeAndValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
 | 
			
		||||
	for _, rdn := range *rdns {
 | 
			
		||||
		if len(rdn) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		atv := rdn[0]
 | 
			
		||||
		n.Names = append(n.Names, atv)
 | 
			
		||||
		value, ok := atv.Value.(string)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		t := atv.Type
 | 
			
		||||
		if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
 | 
			
		||||
			switch t[3] {
 | 
			
		||||
			case 3:
 | 
			
		||||
				n.CommonName = value
 | 
			
		||||
			case 5:
 | 
			
		||||
				n.SerialNumber = value
 | 
			
		||||
			case 6:
 | 
			
		||||
				n.Country = append(n.Country, value)
 | 
			
		||||
			case 7:
 | 
			
		||||
				n.Locality = append(n.Locality, value)
 | 
			
		||||
			case 8:
 | 
			
		||||
				n.Province = append(n.Province, value)
 | 
			
		||||
			case 9:
 | 
			
		||||
				n.StreetAddress = append(n.StreetAddress, value)
 | 
			
		||||
			case 10:
 | 
			
		||||
				n.Organization = append(n.Organization, value)
 | 
			
		||||
			case 11:
 | 
			
		||||
				n.OrganizationalUnit = append(n.OrganizationalUnit, value)
 | 
			
		||||
			case 17:
 | 
			
		||||
				n.PostalCode = append(n.PostalCode, value)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	oidCountry            = []int{2, 5, 4, 6}
 | 
			
		||||
	oidOrganization       = []int{2, 5, 4, 10}
 | 
			
		||||
	oidOrganizationalUnit = []int{2, 5, 4, 11}
 | 
			
		||||
	oidCommonName         = []int{2, 5, 4, 3}
 | 
			
		||||
	oidSerialNumber       = []int{2, 5, 4, 5}
 | 
			
		||||
	oidLocality           = []int{2, 5, 4, 7}
 | 
			
		||||
	oidProvince           = []int{2, 5, 4, 8}
 | 
			
		||||
	oidStreetAddress      = []int{2, 5, 4, 9}
 | 
			
		||||
	oidPostalCode         = []int{2, 5, 4, 17}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence
 | 
			
		||||
// and returns the new value. The relativeDistinguishedNameSET contains an
 | 
			
		||||
// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
 | 
			
		||||
// search for AttributeTypeAndValue.
 | 
			
		||||
func appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence {
 | 
			
		||||
	if len(values) == 0 {
 | 
			
		||||
		return in
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := make([]AttributeTypeAndValue, len(values))
 | 
			
		||||
	for i, value := range values {
 | 
			
		||||
		s[i].Type = oid
 | 
			
		||||
		s[i].Value = value
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return append(in, s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (n Name) ToRDNSequence() (ret RDNSequence) {
 | 
			
		||||
	ret = appendRDNs(ret, n.Country, oidCountry)
 | 
			
		||||
	ret = appendRDNs(ret, n.Organization, oidOrganization)
 | 
			
		||||
	ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
 | 
			
		||||
	ret = appendRDNs(ret, n.Locality, oidLocality)
 | 
			
		||||
	ret = appendRDNs(ret, n.Province, oidProvince)
 | 
			
		||||
	ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress)
 | 
			
		||||
	ret = appendRDNs(ret, n.PostalCode, oidPostalCode)
 | 
			
		||||
	if len(n.CommonName) > 0 {
 | 
			
		||||
		ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName)
 | 
			
		||||
	}
 | 
			
		||||
	if len(n.SerialNumber) > 0 {
 | 
			
		||||
		ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CertificateList represents the ASN.1 structure of the same name. See RFC
 | 
			
		||||
// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the
 | 
			
		||||
// signature.
 | 
			
		||||
type CertificateList struct {
 | 
			
		||||
	TBSCertList        TBSCertificateList
 | 
			
		||||
	SignatureAlgorithm AlgorithmIdentifier
 | 
			
		||||
	SignatureValue     asn1.BitString
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasExpired reports whether now is past the expiry time of certList.
 | 
			
		||||
func (certList *CertificateList) HasExpired(now time.Time) bool {
 | 
			
		||||
	return now.After(certList.TBSCertList.NextUpdate)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
 | 
			
		||||
// 5280, section 5.1.
 | 
			
		||||
type TBSCertificateList struct {
 | 
			
		||||
	Raw                 asn1.RawContent
 | 
			
		||||
	Version             int `asn1:"optional,default:2"`
 | 
			
		||||
	Signature           AlgorithmIdentifier
 | 
			
		||||
	Issuer              RDNSequence
 | 
			
		||||
	ThisUpdate          time.Time
 | 
			
		||||
	NextUpdate          time.Time
 | 
			
		||||
	RevokedCertificates []RevokedCertificate `asn1:"optional"`
 | 
			
		||||
	Extensions          []Extension          `asn1:"tag:0,optional,explicit"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
 | 
			
		||||
// 5280, section 5.1.
 | 
			
		||||
type RevokedCertificate struct {
 | 
			
		||||
	SerialNumber   *big.Int
 | 
			
		||||
	RevocationTime time.Time
 | 
			
		||||
	Extensions     []Extension `asn1:"optional"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								ct/x509/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								ct/x509/root.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import "sync"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	once        sync.Once
 | 
			
		||||
	systemRoots *CertPool
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func systemRootsPool() *CertPool {
 | 
			
		||||
	once.Do(initSystemRoots)
 | 
			
		||||
	return systemRoots
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								ct/x509/root_darwin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								ct/x509/root_darwin.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
// Copyright 2011 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 darwin,cgo
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
 | 
			
		||||
#cgo LDFLAGS: -framework CoreFoundation -framework Security
 | 
			
		||||
 | 
			
		||||
#include <CoreFoundation/CoreFoundation.h>
 | 
			
		||||
#include <Security/Security.h>
 | 
			
		||||
 | 
			
		||||
// FetchPEMRootsCTX509 fetches the system's list of trusted X.509 root certificates.
 | 
			
		||||
//
 | 
			
		||||
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
 | 
			
		||||
// certificates of the system. On failure, the function returns -1.
 | 
			
		||||
//
 | 
			
		||||
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
 | 
			
		||||
// we've consumed its content.
 | 
			
		||||
int FetchPEMRootsCTX509(CFDataRef *pemRoots) {
 | 
			
		||||
	if (pemRoots == NULL) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CFArrayRef certs = NULL;
 | 
			
		||||
	OSStatus err = SecTrustCopyAnchorCertificates(&certs);
 | 
			
		||||
	if (err != noErr) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
 | 
			
		||||
	int i, ncerts = CFArrayGetCount(certs);
 | 
			
		||||
	for (i = 0; i < ncerts; i++) {
 | 
			
		||||
		CFDataRef data = NULL;
 | 
			
		||||
		SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
 | 
			
		||||
		if (cert == NULL) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
 | 
			
		||||
		// Once we support weak imports via cgo we should prefer that, and fall back to this
 | 
			
		||||
		// for older systems.
 | 
			
		||||
		err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
 | 
			
		||||
		if (err != noErr) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (data != NULL) {
 | 
			
		||||
			CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
 | 
			
		||||
			CFRelease(data);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CFRelease(certs);
 | 
			
		||||
 | 
			
		||||
	*pemRoots = combinedData;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
import "C"
 | 
			
		||||
import "unsafe"
 | 
			
		||||
 | 
			
		||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initSystemRoots() {
 | 
			
		||||
	roots := NewCertPool()
 | 
			
		||||
 | 
			
		||||
	var data C.CFDataRef = nil
 | 
			
		||||
	err := C.FetchPEMRootsCTX509(&data)
 | 
			
		||||
	if err == -1 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer C.CFRelease(C.CFTypeRef(data))
 | 
			
		||||
	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
 | 
			
		||||
	roots.AppendCertsFromPEM(buf)
 | 
			
		||||
	systemRoots = roots
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								ct/x509/root_plan9.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ct/x509/root_plan9.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// Copyright 2012 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 plan9
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import "io/ioutil"
 | 
			
		||||
 | 
			
		||||
// Possible certificate files; stop after finding one.
 | 
			
		||||
var certFiles = []string{
 | 
			
		||||
	"/sys/lib/tls/ca.pem",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initSystemRoots() {
 | 
			
		||||
	roots := NewCertPool()
 | 
			
		||||
	for _, file := range certFiles {
 | 
			
		||||
		data, err := ioutil.ReadFile(file)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			roots.AppendCertsFromPEM(data)
 | 
			
		||||
			systemRoots = roots
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// All of the files failed to load. systemRoots will be nil which will
 | 
			
		||||
	// trigger a specific error at verification time.
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								ct/x509/root_stub.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ct/x509/root_stub.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
// Copyright 2011 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 darwin,!cgo
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initSystemRoots() {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								ct/x509/root_unix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ct/x509/root_unix.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
// Copyright 2011 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 dragonfly freebsd linux openbsd netbsd
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import "io/ioutil"
 | 
			
		||||
 | 
			
		||||
// Possible certificate files; stop after finding one.
 | 
			
		||||
var certFiles = []string{
 | 
			
		||||
	"/etc/ssl/certs/ca-certificates.crt",     // Debian/Ubuntu/Gentoo etc.
 | 
			
		||||
	"/etc/pki/tls/certs/ca-bundle.crt",       // Fedora/RHEL
 | 
			
		||||
	"/etc/ssl/ca-bundle.pem",                 // OpenSUSE
 | 
			
		||||
	"/etc/ssl/cert.pem",                      // OpenBSD
 | 
			
		||||
	"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initSystemRoots() {
 | 
			
		||||
	roots := NewCertPool()
 | 
			
		||||
	for _, file := range certFiles {
 | 
			
		||||
		data, err := ioutil.ReadFile(file)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			roots.AppendCertsFromPEM(data)
 | 
			
		||||
			systemRoots = roots
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// All of the files failed to load. systemRoots will be nil which will
 | 
			
		||||
	// trigger a specific error at verification time.
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										229
									
								
								ct/x509/root_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								ct/x509/root_windows.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,229 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
 | 
			
		||||
// certificate store containing itself and all of the intermediate certificates specified
 | 
			
		||||
// in the opts.Intermediates CertPool.
 | 
			
		||||
//
 | 
			
		||||
// A pointer to the in-memory store is available in the returned CertContext's Store field.
 | 
			
		||||
// The store is automatically freed when the CertContext is freed using
 | 
			
		||||
// syscall.CertFreeCertificateContext.
 | 
			
		||||
func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
 | 
			
		||||
	var storeCtx *syscall.CertContext
 | 
			
		||||
 | 
			
		||||
	leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer syscall.CertFreeCertificateContext(leafCtx)
 | 
			
		||||
 | 
			
		||||
	handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer syscall.CertCloseStore(handle, 0)
 | 
			
		||||
 | 
			
		||||
	err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Intermediates != nil {
 | 
			
		||||
		for _, intermediate := range opts.Intermediates.certs {
 | 
			
		||||
			ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
 | 
			
		||||
			syscall.CertFreeCertificateContext(ctx)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return storeCtx, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
 | 
			
		||||
func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
 | 
			
		||||
	if simpleChain == nil || count == 0 {
 | 
			
		||||
		return nil, errors.New("x509: invalid simple chain")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
 | 
			
		||||
	lastChain := simpleChains[count-1]
 | 
			
		||||
	elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
 | 
			
		||||
	for i := 0; i < int(lastChain.NumElements); i++ {
 | 
			
		||||
		// Copy the buf, since ParseCertificate does not create its own copy.
 | 
			
		||||
		cert := elements[i].CertContext
 | 
			
		||||
		encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
 | 
			
		||||
		buf := make([]byte, cert.Length)
 | 
			
		||||
		copy(buf, encodedCert[:])
 | 
			
		||||
		parsedCert, err := ParseCertificate(buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		chain = append(chain, parsedCert)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return chain, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkChainTrustStatus checks the trust status of the certificate chain, translating
 | 
			
		||||
// any errors it finds into Go errors in the process.
 | 
			
		||||
func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
 | 
			
		||||
	if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
 | 
			
		||||
		status := chainCtx.TrustStatus.ErrorStatus
 | 
			
		||||
		switch status {
 | 
			
		||||
		case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
 | 
			
		||||
			return CertificateInvalidError{c, Expired}
 | 
			
		||||
		default:
 | 
			
		||||
			return UnknownAuthorityError{c, nil, nil}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
 | 
			
		||||
// use as a certificate chain for a SSL/TLS server.
 | 
			
		||||
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
 | 
			
		||||
	servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	sslPara := &syscall.SSLExtraCertChainPolicyPara{
 | 
			
		||||
		AuthType:   syscall.AUTHTYPE_SERVER,
 | 
			
		||||
		ServerName: servernamep,
 | 
			
		||||
	}
 | 
			
		||||
	sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
 | 
			
		||||
 | 
			
		||||
	para := &syscall.CertChainPolicyPara{
 | 
			
		||||
		ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
 | 
			
		||||
	}
 | 
			
		||||
	para.Size = uint32(unsafe.Sizeof(*para))
 | 
			
		||||
 | 
			
		||||
	status := syscall.CertChainPolicyStatus{}
 | 
			
		||||
	err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO(mkrautz): use the lChainIndex and lElementIndex fields
 | 
			
		||||
	// of the CertChainPolicyStatus to provide proper context, instead
 | 
			
		||||
	// using c.
 | 
			
		||||
	if status.Error != 0 {
 | 
			
		||||
		switch status.Error {
 | 
			
		||||
		case syscall.CERT_E_EXPIRED:
 | 
			
		||||
			return CertificateInvalidError{c, Expired}
 | 
			
		||||
		case syscall.CERT_E_CN_NO_MATCH:
 | 
			
		||||
			return HostnameError{c, opts.DNSName}
 | 
			
		||||
		case syscall.CERT_E_UNTRUSTEDROOT:
 | 
			
		||||
			return UnknownAuthorityError{c, nil, nil}
 | 
			
		||||
		default:
 | 
			
		||||
			return UnknownAuthorityError{c, nil, nil}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// systemVerify is like Verify, except that it uses CryptoAPI calls
 | 
			
		||||
// to build certificate chains and verify them.
 | 
			
		||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	hasDNSName := opts != nil && len(opts.DNSName) > 0
 | 
			
		||||
 | 
			
		||||
	storeCtx, err := createStoreContext(c, opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer syscall.CertFreeCertificateContext(storeCtx)
 | 
			
		||||
 | 
			
		||||
	para := new(syscall.CertChainPara)
 | 
			
		||||
	para.Size = uint32(unsafe.Sizeof(*para))
 | 
			
		||||
 | 
			
		||||
	// If there's a DNSName set in opts, assume we're verifying
 | 
			
		||||
	// a certificate from a TLS server.
 | 
			
		||||
	if hasDNSName {
 | 
			
		||||
		oids := []*byte{
 | 
			
		||||
			&syscall.OID_PKIX_KP_SERVER_AUTH[0],
 | 
			
		||||
			// Both IE and Chrome allow certificates with
 | 
			
		||||
			// Server Gated Crypto as well. Some certificates
 | 
			
		||||
			// in the wild require them.
 | 
			
		||||
			&syscall.OID_SERVER_GATED_CRYPTO[0],
 | 
			
		||||
			&syscall.OID_SGC_NETSCAPE[0],
 | 
			
		||||
		}
 | 
			
		||||
		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
 | 
			
		||||
		para.RequestedUsage.Usage.Length = uint32(len(oids))
 | 
			
		||||
		para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
 | 
			
		||||
	} else {
 | 
			
		||||
		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
 | 
			
		||||
		para.RequestedUsage.Usage.Length = 0
 | 
			
		||||
		para.RequestedUsage.Usage.UsageIdentifiers = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var verifyTime *syscall.Filetime
 | 
			
		||||
	if opts != nil && !opts.CurrentTime.IsZero() {
 | 
			
		||||
		ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
 | 
			
		||||
		verifyTime = &ft
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// CertGetCertificateChain will traverse Windows's root stores
 | 
			
		||||
	// in an attempt to build a verified certificate chain.  Once
 | 
			
		||||
	// it has found a verified chain, it stops. MSDN docs on
 | 
			
		||||
	// CERT_CHAIN_CONTEXT:
 | 
			
		||||
	//
 | 
			
		||||
	//   When a CERT_CHAIN_CONTEXT is built, the first simple chain
 | 
			
		||||
	//   begins with an end certificate and ends with a self-signed
 | 
			
		||||
	//   certificate. If that self-signed certificate is not a root
 | 
			
		||||
	//   or otherwise trusted certificate, an attempt is made to
 | 
			
		||||
	//   build a new chain. CTLs are used to create the new chain
 | 
			
		||||
	//   beginning with the self-signed certificate from the original
 | 
			
		||||
	//   chain as the end certificate of the new chain. This process
 | 
			
		||||
	//   continues building additional simple chains until the first
 | 
			
		||||
	//   self-signed certificate is a trusted certificate or until
 | 
			
		||||
	//   an additional simple chain cannot be built.
 | 
			
		||||
	//
 | 
			
		||||
	// The result is that we'll only get a single trusted chain to
 | 
			
		||||
	// return to our caller.
 | 
			
		||||
	var chainCtx *syscall.CertChainContext
 | 
			
		||||
	err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer syscall.CertFreeCertificateChain(chainCtx)
 | 
			
		||||
 | 
			
		||||
	err = checkChainTrustStatus(c, chainCtx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasDNSName {
 | 
			
		||||
		err = checkChainSSLServerPolicy(c, chainCtx, opts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chains = append(chains, chain)
 | 
			
		||||
 | 
			
		||||
	return chains, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initSystemRoots() {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								ct/x509/sec1.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								ct/x509/sec1.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/asn1"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math/big"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const ecPrivKeyVersion = 1
 | 
			
		||||
 | 
			
		||||
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
 | 
			
		||||
// References:
 | 
			
		||||
//   RFC5915
 | 
			
		||||
//   SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf
 | 
			
		||||
// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
 | 
			
		||||
// most cases it is not.
 | 
			
		||||
type ecPrivateKey struct {
 | 
			
		||||
	Version       int
 | 
			
		||||
	PrivateKey    []byte
 | 
			
		||||
	NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
 | 
			
		||||
	PublicKey     asn1.BitString        `asn1:"optional,explicit,tag:1"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
 | 
			
		||||
func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
 | 
			
		||||
	return parseECPrivateKey(nil, der)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalECPrivateKey marshals an EC private key into ASN.1, DER format.
 | 
			
		||||
func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
 | 
			
		||||
	oid, ok := oidFromNamedCurve(key.Curve)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("x509: unknown elliptic curve")
 | 
			
		||||
	}
 | 
			
		||||
	return asn1.Marshal(ecPrivateKey{
 | 
			
		||||
		Version:       1,
 | 
			
		||||
		PrivateKey:    key.D.Bytes(),
 | 
			
		||||
		NamedCurveOID: oid,
 | 
			
		||||
		PublicKey:     asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
 | 
			
		||||
// The OID for the named curve may be provided from another source (such as
 | 
			
		||||
// the PKCS8 container) - if it is provided then use this instead of the OID
 | 
			
		||||
// that may exist in the EC private key structure.
 | 
			
		||||
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
 | 
			
		||||
	var privKey ecPrivateKey
 | 
			
		||||
	if _, err := asn1.Unmarshal(der, &privKey); err != nil {
 | 
			
		||||
		return nil, errors.New("x509: failed to parse EC private key: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	if privKey.Version != ecPrivKeyVersion {
 | 
			
		||||
		return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var curve elliptic.Curve
 | 
			
		||||
	if namedCurveOID != nil {
 | 
			
		||||
		curve = namedCurveFromOID(*namedCurveOID)
 | 
			
		||||
	} else {
 | 
			
		||||
		curve = namedCurveFromOID(privKey.NamedCurveOID)
 | 
			
		||||
	}
 | 
			
		||||
	if curve == nil {
 | 
			
		||||
		return nil, errors.New("x509: unknown elliptic curve")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k := new(big.Int).SetBytes(privKey.PrivateKey)
 | 
			
		||||
	if k.Cmp(curve.Params().N) >= 0 {
 | 
			
		||||
		return nil, errors.New("x509: invalid elliptic curve private key value")
 | 
			
		||||
	}
 | 
			
		||||
	priv := new(ecdsa.PrivateKey)
 | 
			
		||||
	priv.Curve = curve
 | 
			
		||||
	priv.D = k
 | 
			
		||||
	priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey)
 | 
			
		||||
 | 
			
		||||
	return priv, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								ct/x509/sec1_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								ct/x509/sec1_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
// Copyright 2012 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Generated using:
 | 
			
		||||
//   openssl ecparam -genkey -name secp384r1 -outform PEM
 | 
			
		||||
var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50`
 | 
			
		||||
 | 
			
		||||
func TestParseECPrivateKey(t *testing.T) {
 | 
			
		||||
	derBytes, _ := hex.DecodeString(ecPrivateKeyHex)
 | 
			
		||||
	key, err := ParseECPrivateKey(derBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("failed to decode EC private key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	serialized, err := MarshalECPrivateKey(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("failed to encode EC private key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !bytes.Equal(serialized, derBytes) {
 | 
			
		||||
		t.Fatalf("serialized key differs: got %x, want %x", serialized, derBytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										476
									
								
								ct/x509/verify.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										476
									
								
								ct/x509/verify.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,476 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type InvalidReason int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// NotAuthorizedToSign results when a certificate is signed by another
 | 
			
		||||
	// which isn't marked as a CA certificate.
 | 
			
		||||
	NotAuthorizedToSign InvalidReason = iota
 | 
			
		||||
	// Expired results when a certificate has expired, based on the time
 | 
			
		||||
	// given in the VerifyOptions.
 | 
			
		||||
	Expired
 | 
			
		||||
	// CANotAuthorizedForThisName results when an intermediate or root
 | 
			
		||||
	// certificate has a name constraint which doesn't include the name
 | 
			
		||||
	// being checked.
 | 
			
		||||
	CANotAuthorizedForThisName
 | 
			
		||||
	// TooManyIntermediates results when a path length constraint is
 | 
			
		||||
	// violated.
 | 
			
		||||
	TooManyIntermediates
 | 
			
		||||
	// IncompatibleUsage results when the certificate's key usage indicates
 | 
			
		||||
	// that it may only be used for a different purpose.
 | 
			
		||||
	IncompatibleUsage
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CertificateInvalidError results when an odd error occurs. Users of this
 | 
			
		||||
// library probably want to handle all these errors uniformly.
 | 
			
		||||
type CertificateInvalidError struct {
 | 
			
		||||
	Cert   *Certificate
 | 
			
		||||
	Reason InvalidReason
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e CertificateInvalidError) Error() string {
 | 
			
		||||
	switch e.Reason {
 | 
			
		||||
	case NotAuthorizedToSign:
 | 
			
		||||
		return "x509: certificate is not authorized to sign other certificates"
 | 
			
		||||
	case Expired:
 | 
			
		||||
		return "x509: certificate has expired or is not yet valid"
 | 
			
		||||
	case CANotAuthorizedForThisName:
 | 
			
		||||
		return "x509: a root or intermediate certificate is not authorized to sign in this domain"
 | 
			
		||||
	case TooManyIntermediates:
 | 
			
		||||
		return "x509: too many intermediates for path length constraint"
 | 
			
		||||
	case IncompatibleUsage:
 | 
			
		||||
		return "x509: certificate specifies an incompatible key usage"
 | 
			
		||||
	}
 | 
			
		||||
	return "x509: unknown error"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HostnameError results when the set of authorized names doesn't match the
 | 
			
		||||
// requested name.
 | 
			
		||||
type HostnameError struct {
 | 
			
		||||
	Certificate *Certificate
 | 
			
		||||
	Host        string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h HostnameError) Error() string {
 | 
			
		||||
	c := h.Certificate
 | 
			
		||||
 | 
			
		||||
	var valid string
 | 
			
		||||
	if ip := net.ParseIP(h.Host); ip != nil {
 | 
			
		||||
		// Trying to validate an IP
 | 
			
		||||
		if len(c.IPAddresses) == 0 {
 | 
			
		||||
			return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs"
 | 
			
		||||
		}
 | 
			
		||||
		for _, san := range c.IPAddresses {
 | 
			
		||||
			if len(valid) > 0 {
 | 
			
		||||
				valid += ", "
 | 
			
		||||
			}
 | 
			
		||||
			valid += san.String()
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if len(c.DNSNames) > 0 {
 | 
			
		||||
			valid = strings.Join(c.DNSNames, ", ")
 | 
			
		||||
		} else {
 | 
			
		||||
			valid = c.Subject.CommonName
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "x509: certificate is valid for " + valid + ", not " + h.Host
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnknownAuthorityError results when the certificate issuer is unknown
 | 
			
		||||
type UnknownAuthorityError struct {
 | 
			
		||||
	cert *Certificate
 | 
			
		||||
	// hintErr contains an error that may be helpful in determining why an
 | 
			
		||||
	// authority wasn't found.
 | 
			
		||||
	hintErr error
 | 
			
		||||
	// hintCert contains a possible authority certificate that was rejected
 | 
			
		||||
	// because of the error in hintErr.
 | 
			
		||||
	hintCert *Certificate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e UnknownAuthorityError) Error() string {
 | 
			
		||||
	s := "x509: certificate signed by unknown authority"
 | 
			
		||||
	if e.hintErr != nil {
 | 
			
		||||
		certName := e.hintCert.Subject.CommonName
 | 
			
		||||
		if len(certName) == 0 {
 | 
			
		||||
			if len(e.hintCert.Subject.Organization) > 0 {
 | 
			
		||||
				certName = e.hintCert.Subject.Organization[0]
 | 
			
		||||
			}
 | 
			
		||||
			certName = "serial:" + e.hintCert.SerialNumber.String()
 | 
			
		||||
		}
 | 
			
		||||
		s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SystemRootsError results when we fail to load the system root certificates.
 | 
			
		||||
type SystemRootsError struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e SystemRootsError) Error() string {
 | 
			
		||||
	return "x509: failed to load system roots and no roots provided"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 behaviour,
 | 
			
		||||
	// but not the spec. To accept any key usage, include ExtKeyUsageAny.
 | 
			
		||||
	KeyUsages []ExtKeyUsage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	leafCertificate = iota
 | 
			
		||||
	intermediateCertificate
 | 
			
		||||
	rootCertificate
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// isValid performs validity checks on the c.
 | 
			
		||||
func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
 | 
			
		||||
	if !opts.DisableTimeChecks {
 | 
			
		||||
		now := opts.CurrentTime
 | 
			
		||||
		if now.IsZero() {
 | 
			
		||||
			now = time.Now()
 | 
			
		||||
		}
 | 
			
		||||
		if now.Before(c.NotBefore) || now.After(c.NotAfter) {
 | 
			
		||||
			return CertificateInvalidError{c, Expired}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.PermittedDNSDomains) > 0 {
 | 
			
		||||
		ok := false
 | 
			
		||||
		for _, domain := range c.PermittedDNSDomains {
 | 
			
		||||
			if opts.DNSName == domain ||
 | 
			
		||||
				(strings.HasSuffix(opts.DNSName, domain) &&
 | 
			
		||||
					len(opts.DNSName) >= 1+len(domain) &&
 | 
			
		||||
					opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') {
 | 
			
		||||
				ok = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return CertificateInvalidError{c, CANotAuthorizedForThisName}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// KeyUsage status flags are ignored. From Engineering Security, Peter
 | 
			
		||||
	// Gutmann: A European government CA marked its signing certificates as
 | 
			
		||||
	// being valid for encryption only, but no-one noticed. Another
 | 
			
		||||
	// European CA marked its signature keys as not being valid for
 | 
			
		||||
	// signatures. A different CA marked its own trusted root certificate
 | 
			
		||||
	// as being invalid for certificate signing.  Another national CA
 | 
			
		||||
	// distributed a certificate to be used to encrypt data for the
 | 
			
		||||
	// country’s tax authority that was marked as only being usable for
 | 
			
		||||
	// digital signatures but not for encryption. Yet another CA reversed
 | 
			
		||||
	// the order of the bit flags in the keyUsage due to confusion over
 | 
			
		||||
	// encoding endianness, essentially setting a random keyUsage in
 | 
			
		||||
	// certificates that it issued. Another CA created a self-invalidating
 | 
			
		||||
	// certificate by adding a certificate policy statement stipulating
 | 
			
		||||
	// that the certificate had to be used strictly as specified in the
 | 
			
		||||
	// keyUsage, and a keyUsage containing a flag indicating that the RSA
 | 
			
		||||
	// encryption key could only be used for Diffie-Hellman key agreement.
 | 
			
		||||
 | 
			
		||||
	if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) {
 | 
			
		||||
		return CertificateInvalidError{c, NotAuthorizedToSign}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.BasicConstraintsValid && c.MaxPathLen >= 0 {
 | 
			
		||||
		numIntermediates := len(currentChain) - 1
 | 
			
		||||
		if numIntermediates > c.MaxPathLen {
 | 
			
		||||
			return CertificateInvalidError{c, TooManyIntermediates}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// element of the chain is c and the last element is from opts.Roots.
 | 
			
		||||
//
 | 
			
		||||
// WARNING: this doesn't do any revocation checking.
 | 
			
		||||
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	// Use Windows's own verification and chain building.
 | 
			
		||||
	if opts.Roots == nil && runtime.GOOS == "windows" {
 | 
			
		||||
		return c.systemVerify(&opts)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Roots == nil {
 | 
			
		||||
		opts.Roots = systemRootsPool()
 | 
			
		||||
		if opts.Roots == nil {
 | 
			
		||||
			return nil, SystemRootsError{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = c.isValid(leafCertificate, nil, &opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(opts.DNSName) > 0 {
 | 
			
		||||
		err = c.VerifyHostname(opts.DNSName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	keyUsages := opts.KeyUsages
 | 
			
		||||
	if len(keyUsages) == 0 {
 | 
			
		||||
		keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If any key usage is acceptable then we're done.
 | 
			
		||||
	for _, usage := range keyUsages {
 | 
			
		||||
		if usage == ExtKeyUsageAny {
 | 
			
		||||
			chains = candidateChains
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, candidate := range candidateChains {
 | 
			
		||||
		if checkChainForKeyUsage(candidate, keyUsages) {
 | 
			
		||||
			chains = append(chains, candidate)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(chains) == 0 {
 | 
			
		||||
		err = CertificateInvalidError{c, IncompatibleUsage}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
 | 
			
		||||
	n := make([]*Certificate, len(chain)+1)
 | 
			
		||||
	copy(n, chain)
 | 
			
		||||
	n[len(chain)] = cert
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
 | 
			
		||||
	possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
 | 
			
		||||
	for _, rootNum := range possibleRoots {
 | 
			
		||||
		root := opts.Roots.certs[rootNum]
 | 
			
		||||
		err = root.isValid(rootCertificate, currentChain, opts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		chains = append(chains, appendToFreshChain(currentChain, root))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c)
 | 
			
		||||
nextIntermediate:
 | 
			
		||||
	for _, intermediateNum := range possibleIntermediates {
 | 
			
		||||
		intermediate := opts.Intermediates.certs[intermediateNum]
 | 
			
		||||
		for _, cert := range currentChain {
 | 
			
		||||
			if cert == intermediate {
 | 
			
		||||
				continue nextIntermediate
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		err = intermediate.isValid(intermediateCertificate, currentChain, opts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var childChains [][]*Certificate
 | 
			
		||||
		childChains, ok := cache[intermediateNum]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts)
 | 
			
		||||
			cache[intermediateNum] = childChains
 | 
			
		||||
		}
 | 
			
		||||
		chains = append(chains, childChains...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(chains) > 0 {
 | 
			
		||||
		err = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(chains) == 0 && err == nil {
 | 
			
		||||
		hintErr := rootErr
 | 
			
		||||
		hintCert := failedRoot
 | 
			
		||||
		if hintErr == nil {
 | 
			
		||||
			hintErr = intermediateErr
 | 
			
		||||
			hintCert = failedIntermediate
 | 
			
		||||
		}
 | 
			
		||||
		err = UnknownAuthorityError{c, hintErr, hintCert}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func matchHostnames(pattern, host string) bool {
 | 
			
		||||
	if len(pattern) == 0 || len(host) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	patternParts := strings.Split(pattern, ".")
 | 
			
		||||
	hostParts := strings.Split(host, ".")
 | 
			
		||||
 | 
			
		||||
	if len(patternParts) != len(hostParts) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, patternPart := range patternParts {
 | 
			
		||||
		if patternPart == "*" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if patternPart != hostParts[i] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
 | 
			
		||||
// an explicitly ASCII function to avoid any sharp corners resulting from
 | 
			
		||||
// performing Unicode operations on DNS labels.
 | 
			
		||||
func toLowerCaseASCII(in string) string {
 | 
			
		||||
	// If the string is already lower-case then there's nothing to do.
 | 
			
		||||
	isAlreadyLowerCase := true
 | 
			
		||||
	for _, c := range in {
 | 
			
		||||
		if c == utf8.RuneError {
 | 
			
		||||
			// If we get a UTF-8 error then there might be
 | 
			
		||||
			// upper-case ASCII bytes in the invalid sequence.
 | 
			
		||||
			isAlreadyLowerCase = false
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if 'A' <= c && c <= 'Z' {
 | 
			
		||||
			isAlreadyLowerCase = false
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isAlreadyLowerCase {
 | 
			
		||||
		return in
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out := []byte(in)
 | 
			
		||||
	for i, c := range out {
 | 
			
		||||
		if 'A' <= c && c <= 'Z' {
 | 
			
		||||
			out[i] += 'a' - 'A'
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return string(out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VerifyHostname returns nil if c is a valid certificate for the named host.
 | 
			
		||||
// Otherwise it returns an error describing the mismatch.
 | 
			
		||||
func (c *Certificate) VerifyHostname(h string) error {
 | 
			
		||||
	// IP addresses may be written in [ ].
 | 
			
		||||
	candidateIP := h
 | 
			
		||||
	if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
 | 
			
		||||
		candidateIP = h[1 : len(h)-1]
 | 
			
		||||
	}
 | 
			
		||||
	if ip := net.ParseIP(candidateIP); ip != nil {
 | 
			
		||||
		// We only match IP addresses against IP SANs.
 | 
			
		||||
		// https://tools.ietf.org/html/rfc6125#appendix-B.2
 | 
			
		||||
		for _, candidate := range c.IPAddresses {
 | 
			
		||||
			if ip.Equal(candidate) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return HostnameError{c, candidateIP}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lowered := toLowerCaseASCII(h)
 | 
			
		||||
 | 
			
		||||
	if len(c.DNSNames) > 0 {
 | 
			
		||||
		for _, match := range c.DNSNames {
 | 
			
		||||
			if matchHostnames(toLowerCaseASCII(match), lowered) {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// If Subject Alt Name is given, we ignore the common name.
 | 
			
		||||
	} else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return HostnameError{c, h}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool {
 | 
			
		||||
	usages := make([]ExtKeyUsage, len(keyUsages))
 | 
			
		||||
	copy(usages, keyUsages)
 | 
			
		||||
 | 
			
		||||
	if len(chain) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	usagesRemaining := len(usages)
 | 
			
		||||
 | 
			
		||||
	// We walk down the list and cross out any usages that aren't supported
 | 
			
		||||
	// by each certificate. If we cross out all the usages, then the chain
 | 
			
		||||
	// is unacceptable.
 | 
			
		||||
 | 
			
		||||
	for i := len(chain) - 1; i >= 0; i-- {
 | 
			
		||||
		cert := chain[i]
 | 
			
		||||
		if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
 | 
			
		||||
			// The certificate doesn't have any extended key usage specified.
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, usage := range cert.ExtKeyUsage {
 | 
			
		||||
			if usage == ExtKeyUsageAny {
 | 
			
		||||
				// The certificate is explicitly good for any usage.
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const invalidUsage ExtKeyUsage = -1
 | 
			
		||||
 | 
			
		||||
	NextRequestedUsage:
 | 
			
		||||
		for i, requestedUsage := range usages {
 | 
			
		||||
			if requestedUsage == invalidUsage {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, usage := range cert.ExtKeyUsage {
 | 
			
		||||
				if requestedUsage == usage {
 | 
			
		||||
					continue NextRequestedUsage
 | 
			
		||||
				} else if requestedUsage == ExtKeyUsageServerAuth &&
 | 
			
		||||
					(usage == ExtKeyUsageNetscapeServerGatedCrypto ||
 | 
			
		||||
						usage == ExtKeyUsageMicrosoftServerGatedCrypto) {
 | 
			
		||||
					// In order to support COMODO
 | 
			
		||||
					// certificate chains, we have to
 | 
			
		||||
					// accept Netscape or Microsoft SGC
 | 
			
		||||
					// usages as equal to ServerAuth.
 | 
			
		||||
					continue NextRequestedUsage
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			usages[i] = invalidUsage
 | 
			
		||||
			usagesRemaining--
 | 
			
		||||
			if usagesRemaining == 0 {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										975
									
								
								ct/x509/verify_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										975
									
								
								ct/x509/verify_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,975 @@
 | 
			
		||||
// Copyright 2011 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.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/x509/pkix"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type verifyTest struct {
 | 
			
		||||
	leaf                 string
 | 
			
		||||
	intermediates        []string
 | 
			
		||||
	roots                []string
 | 
			
		||||
	currentTime          int64
 | 
			
		||||
	dnsName              string
 | 
			
		||||
	systemSkip           bool
 | 
			
		||||
	keyUsages            []ExtKeyUsage
 | 
			
		||||
	testSystemRootsError bool
 | 
			
		||||
	disableTimeChecks    bool
 | 
			
		||||
 | 
			
		||||
	errorCallback  func(*testing.T, int, error) bool
 | 
			
		||||
	expectedChains [][]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var verifyTests = []verifyTest{
 | 
			
		||||
	{
 | 
			
		||||
		leaf:                 googleLeaf,
 | 
			
		||||
		intermediates:        []string{thawteIntermediate},
 | 
			
		||||
		currentTime:          1302726541,
 | 
			
		||||
		dnsName:              "www.google.com",
 | 
			
		||||
		testSystemRootsError: true,
 | 
			
		||||
 | 
			
		||||
		// Without any roots specified we should get a system roots
 | 
			
		||||
		// error.
 | 
			
		||||
		errorCallback: expectSystemRootsError,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeaf,
 | 
			
		||||
		intermediates: []string{thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
		dnsName:       "www.google.com",
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"Google", "Thawte", "VeriSign"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeaf,
 | 
			
		||||
		intermediates: []string{thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
		dnsName:       "WwW.GooGLE.coM",
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"Google", "Thawte", "VeriSign"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeaf,
 | 
			
		||||
		intermediates: []string{thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
		dnsName:       "www.example.com",
 | 
			
		||||
 | 
			
		||||
		errorCallback: expectHostnameError,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeaf,
 | 
			
		||||
		intermediates: []string{thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   1,
 | 
			
		||||
		dnsName:       "www.google.com",
 | 
			
		||||
 | 
			
		||||
		errorCallback: expectExpired,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:              googleLeaf,
 | 
			
		||||
		intermediates:     []string{thawteIntermediate},
 | 
			
		||||
		roots:             []string{verisignRoot},
 | 
			
		||||
		currentTime:       1,
 | 
			
		||||
		dnsName:           "www.google.com",
 | 
			
		||||
		disableTimeChecks: true,
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"Google", "Thawte", "VeriSign"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeaf,
 | 
			
		||||
		intermediates: []string{thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   10000000000,
 | 
			
		||||
		dnsName:       "www.google.com",
 | 
			
		||||
 | 
			
		||||
		errorCallback: expectExpired,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:              googleLeaf,
 | 
			
		||||
		intermediates:     []string{thawteIntermediate},
 | 
			
		||||
		roots:             []string{verisignRoot},
 | 
			
		||||
		currentTime:       10000000000,
 | 
			
		||||
		dnsName:           "www.google.com",
 | 
			
		||||
		disableTimeChecks: true,
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"Google", "Thawte", "VeriSign"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:        googleLeaf,
 | 
			
		||||
		roots:       []string{verisignRoot},
 | 
			
		||||
		currentTime: 1302726541,
 | 
			
		||||
		dnsName:     "www.google.com",
 | 
			
		||||
 | 
			
		||||
		// Skip when using systemVerify, since Windows
 | 
			
		||||
		// *will* find the missing intermediate cert.
 | 
			
		||||
		systemSkip:    true,
 | 
			
		||||
		errorCallback: expectAuthorityUnknown,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeaf,
 | 
			
		||||
		intermediates: []string{verisignRoot, thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
		dnsName:       "www.google.com",
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"Google", "Thawte", "VeriSign"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          dnssecExpLeaf,
 | 
			
		||||
		intermediates: []string{startComIntermediate},
 | 
			
		||||
		roots:         []string{startComRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          dnssecExpLeaf,
 | 
			
		||||
		intermediates: []string{startComIntermediate, startComRoot},
 | 
			
		||||
		roots:         []string{startComRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
 | 
			
		||||
		// Skip when using systemVerify, since Windows
 | 
			
		||||
		// can only return a single chain to us (for now).
 | 
			
		||||
		systemSkip: true,
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
 | 
			
		||||
			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          googleLeafWithInvalidHash,
 | 
			
		||||
		intermediates: []string{thawteIntermediate},
 | 
			
		||||
		roots:         []string{verisignRoot},
 | 
			
		||||
		currentTime:   1302726541,
 | 
			
		||||
		dnsName:       "www.google.com",
 | 
			
		||||
 | 
			
		||||
		// The specific error message may not occur when using system
 | 
			
		||||
		// verification.
 | 
			
		||||
		systemSkip:    true,
 | 
			
		||||
		errorCallback: expectHashError,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// The default configuration should reject an S/MIME chain.
 | 
			
		||||
		leaf:        smimeLeaf,
 | 
			
		||||
		roots:       []string{smimeIntermediate},
 | 
			
		||||
		currentTime: 1339436154,
 | 
			
		||||
 | 
			
		||||
		// Key usage not implemented for Windows yet.
 | 
			
		||||
		systemSkip:    true,
 | 
			
		||||
		errorCallback: expectUsageError,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:        smimeLeaf,
 | 
			
		||||
		roots:       []string{smimeIntermediate},
 | 
			
		||||
		currentTime: 1339436154,
 | 
			
		||||
		keyUsages:   []ExtKeyUsage{ExtKeyUsageServerAuth},
 | 
			
		||||
 | 
			
		||||
		// Key usage not implemented for Windows yet.
 | 
			
		||||
		systemSkip:    true,
 | 
			
		||||
		errorCallback: expectUsageError,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:        smimeLeaf,
 | 
			
		||||
		roots:       []string{smimeIntermediate},
 | 
			
		||||
		currentTime: 1339436154,
 | 
			
		||||
		keyUsages:   []ExtKeyUsage{ExtKeyUsageEmailProtection},
 | 
			
		||||
 | 
			
		||||
		// Key usage not implemented for Windows yet.
 | 
			
		||||
		systemSkip: true,
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		leaf:          megaLeaf,
 | 
			
		||||
		intermediates: []string{comodoIntermediate1},
 | 
			
		||||
		roots:         []string{comodoRoot},
 | 
			
		||||
		currentTime:   1360431182,
 | 
			
		||||
 | 
			
		||||
		// CryptoAPI can find alternative validation paths so we don't
 | 
			
		||||
		// perform this test with system validation.
 | 
			
		||||
		systemSkip: true,
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		// Check that a name constrained intermediate works even when
 | 
			
		||||
		// it lists multiple constraints.
 | 
			
		||||
		leaf:          nameConstraintsLeaf,
 | 
			
		||||
		intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2},
 | 
			
		||||
		roots:         []string{globalSignRoot},
 | 
			
		||||
		currentTime:   1382387896,
 | 
			
		||||
		dnsName:       "secure.iddl.vt.edu",
 | 
			
		||||
 | 
			
		||||
		expectedChains: [][]string{
 | 
			
		||||
			{
 | 
			
		||||
				"Technology-enhanced Learning and Online Strategies",
 | 
			
		||||
				"Virginia Tech Global Qualified Server CA",
 | 
			
		||||
				"Trusted Root CA G2",
 | 
			
		||||
				"GlobalSign Root CA",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
 | 
			
		||||
	if _, ok := err.(HostnameError); !ok {
 | 
			
		||||
		t.Errorf("#%d: error was not a HostnameError: %s", i, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expectExpired(t *testing.T, i int, err error) (ok bool) {
 | 
			
		||||
	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired {
 | 
			
		||||
		t.Errorf("#%d: error was not Expired: %s", i, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expectUsageError(t *testing.T, i int, err error) (ok bool) {
 | 
			
		||||
	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage {
 | 
			
		||||
		t.Errorf("#%d: error was not IncompatibleUsage: %s", i, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) {
 | 
			
		||||
	if _, ok := err.(UnknownAuthorityError); !ok {
 | 
			
		||||
		t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expectSystemRootsError(t *testing.T, i int, err error) bool {
 | 
			
		||||
	if _, ok := err.(SystemRootsError); !ok {
 | 
			
		||||
		t.Errorf("#%d: error was not SystemRootsError: %s", i, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expectHashError(t *testing.T, i int, err error) bool {
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("#%d: no error resulted from invalid hash", i)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) {
 | 
			
		||||
		t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %s", i, expected, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func certificateFromPEM(pemBytes string) (*Certificate, error) {
 | 
			
		||||
	block, _ := pem.Decode([]byte(pemBytes))
 | 
			
		||||
	if block == nil {
 | 
			
		||||
		return nil, errors.New("failed to decode PEM")
 | 
			
		||||
	}
 | 
			
		||||
	return ParseCertificate(block.Bytes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testVerify(t *testing.T, useSystemRoots bool) {
 | 
			
		||||
	for i, test := range verifyTests {
 | 
			
		||||
		if useSystemRoots && test.systemSkip {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if runtime.GOOS == "windows" && test.testSystemRootsError {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		opts := VerifyOptions{
 | 
			
		||||
			Intermediates:     NewCertPool(),
 | 
			
		||||
			DNSName:           test.dnsName,
 | 
			
		||||
			CurrentTime:       time.Unix(test.currentTime, 0),
 | 
			
		||||
			KeyUsages:         test.keyUsages,
 | 
			
		||||
			DisableTimeChecks: test.disableTimeChecks,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !useSystemRoots {
 | 
			
		||||
			opts.Roots = NewCertPool()
 | 
			
		||||
			for j, root := range test.roots {
 | 
			
		||||
				ok := opts.Roots.AppendCertsFromPEM([]byte(root))
 | 
			
		||||
				if !ok {
 | 
			
		||||
					t.Errorf("#%d: failed to parse root #%d", i, j)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for j, intermediate := range test.intermediates {
 | 
			
		||||
			ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate))
 | 
			
		||||
			if !ok {
 | 
			
		||||
				t.Errorf("#%d: failed to parse intermediate #%d", i, j)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		leaf, err := certificateFromPEM(test.leaf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("#%d: failed to parse leaf: %s", i, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var oldSystemRoots *CertPool
 | 
			
		||||
		if test.testSystemRootsError {
 | 
			
		||||
			oldSystemRoots = systemRootsPool()
 | 
			
		||||
			systemRoots = nil
 | 
			
		||||
			opts.Roots = nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		chains, err := leaf.Verify(opts)
 | 
			
		||||
 | 
			
		||||
		if test.testSystemRootsError {
 | 
			
		||||
			systemRoots = oldSystemRoots
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if test.errorCallback == nil && err != nil {
 | 
			
		||||
			t.Errorf("#%d: unexpected error: %s", i, err)
 | 
			
		||||
		}
 | 
			
		||||
		if test.errorCallback != nil {
 | 
			
		||||
			if !test.errorCallback(t, i, err) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(chains) != len(test.expectedChains) {
 | 
			
		||||
			t.Errorf("#%d: wanted %d chains, got %d", i, len(test.expectedChains), len(chains))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// We check that each returned chain matches a chain from
 | 
			
		||||
		// expectedChains but an entry in expectedChains can't match
 | 
			
		||||
		// two chains.
 | 
			
		||||
		seenChains := make([]bool, len(chains))
 | 
			
		||||
	NextOutputChain:
 | 
			
		||||
		for _, chain := range chains {
 | 
			
		||||
		TryNextExpected:
 | 
			
		||||
			for j, expectedChain := range test.expectedChains {
 | 
			
		||||
				if seenChains[j] {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if len(chain) != len(expectedChain) {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				for k, cert := range chain {
 | 
			
		||||
					if strings.Index(nameToKey(&cert.Subject), expectedChain[k]) == -1 {
 | 
			
		||||
						continue TryNextExpected
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				// we matched
 | 
			
		||||
				seenChains[j] = true
 | 
			
		||||
				continue NextOutputChain
 | 
			
		||||
			}
 | 
			
		||||
			t.Errorf("#%d: No expected chain matched %s", i, chainToDebugString(chain))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGoVerify(t *testing.T) {
 | 
			
		||||
	testVerify(t, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSystemVerify(t *testing.T) {
 | 
			
		||||
	if runtime.GOOS != "windows" {
 | 
			
		||||
		t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	testVerify(t, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func chainToDebugString(chain []*Certificate) string {
 | 
			
		||||
	var chainStr string
 | 
			
		||||
	for _, cert := range chain {
 | 
			
		||||
		if len(chainStr) > 0 {
 | 
			
		||||
			chainStr += " -> "
 | 
			
		||||
		}
 | 
			
		||||
		chainStr += nameToKey(&cert.Subject)
 | 
			
		||||
	}
 | 
			
		||||
	return chainStr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nameToKey(name *pkix.Name) string {
 | 
			
		||||
	return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const verisignRoot = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
 | 
			
		||||
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
 | 
			
		||||
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
 | 
			
		||||
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
 | 
			
		||||
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
 | 
			
		||||
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
 | 
			
		||||
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
 | 
			
		||||
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
 | 
			
		||||
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
 | 
			
		||||
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
 | 
			
		||||
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
 | 
			
		||||
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const thawteIntermediate = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJV
 | 
			
		||||
UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVi
 | 
			
		||||
bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAw
 | 
			
		||||
MDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh
 | 
			
		||||
d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBD
 | 
			
		||||
QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGhPwtx
 | 
			
		||||
PKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g
 | 
			
		||||
5/OIty0y3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo
 | 
			
		||||
3nWhLHpo39XKHIdYYBkCAwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsG
 | 
			
		||||
A1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAX
 | 
			
		||||
BgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDov
 | 
			
		||||
L2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAkMCIGCCsG
 | 
			
		||||
AQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUF
 | 
			
		||||
BwMBBggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEB
 | 
			
		||||
BQUAA4GBAFWsY+reod3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTc
 | 
			
		||||
q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR
 | 
			
		||||
bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const googleLeaf = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM
 | 
			
		||||
MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
 | 
			
		||||
THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
 | 
			
		||||
MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
 | 
			
		||||
MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
 | 
			
		||||
FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
 | 
			
		||||
gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
 | 
			
		||||
gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
 | 
			
		||||
05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
 | 
			
		||||
BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
 | 
			
		||||
LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
 | 
			
		||||
BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
 | 
			
		||||
Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
 | 
			
		||||
ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF
 | 
			
		||||
AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
 | 
			
		||||
u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
 | 
			
		||||
z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
// googleLeafWithInvalidHash is the same as googleLeaf, but the signature
 | 
			
		||||
// algorithm in the certificate contains a nonsense OID.
 | 
			
		||||
const googleLeafWithInvalidHash = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BATIFADBM
 | 
			
		||||
MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
 | 
			
		||||
THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
 | 
			
		||||
MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
 | 
			
		||||
MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
 | 
			
		||||
FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
 | 
			
		||||
gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
 | 
			
		||||
gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
 | 
			
		||||
05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
 | 
			
		||||
BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
 | 
			
		||||
LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
 | 
			
		||||
BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
 | 
			
		||||
Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
 | 
			
		||||
ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAVAF
 | 
			
		||||
AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
 | 
			
		||||
u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
 | 
			
		||||
z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
const dnssecExpLeaf = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
 | 
			
		||||
TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
 | 
			
		||||
YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg
 | 
			
		||||
MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTAwNzA0MTQ1MjQ1
 | 
			
		||||
WhcNMTEwNzA1MTA1NzA0WjCBwTEgMB4GA1UEDRMXMjIxMTM3LWxpOWE5dHhJRzZM
 | 
			
		||||
NnNyVFMxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVQZXJzb25hIE5vdCBWYWxpZGF0
 | 
			
		||||
ZWQxKTAnBgNVBAsTIFN0YXJ0Q29tIEZyZWUgQ2VydGlmaWNhdGUgTWVtYmVyMRsw
 | 
			
		||||
GQYDVQQDExJ3d3cuZG5zc2VjLWV4cC5vcmcxKDAmBgkqhkiG9w0BCQEWGWhvc3Rt
 | 
			
		||||
YXN0ZXJAZG5zc2VjLWV4cC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
 | 
			
		||||
AoIBAQDEdF/22vaxrPbqpgVYMWi+alfpzBctpbfLBdPGuqOazJdCT0NbWcK8/+B4
 | 
			
		||||
X6OlSOURNIlwLzhkmwVsWdVv6dVSaN7d4yI/fJkvgfDB9+au+iBJb6Pcz8ULBfe6
 | 
			
		||||
D8HVvqKdORp6INzHz71z0sghxrQ0EAEkoWAZLh+kcn2ZHdcmZaBNUfjmGbyU6PRt
 | 
			
		||||
RjdqoP+owIaC1aktBN7zl4uO7cRjlYFdusINrh2kPP02KAx2W84xjxX1uyj6oS6e
 | 
			
		||||
7eBfvcwe8czW/N1rbE0CoR7h9+HnIrjnVG9RhBiZEiw3mUmF++Up26+4KTdRKbu3
 | 
			
		||||
+BL4yMpfd66z0+zzqu+HkvyLpFn5AgMBAAGjggL/MIIC+zAJBgNVHRMEAjAAMAsG
 | 
			
		||||
A1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUy04I5guM
 | 
			
		||||
drzfh2JQaXhgV86+4jUwHwYDVR0jBBgwFoAU60I00Jiwq5/0G2sI98xkLu8OLEUw
 | 
			
		||||
LQYDVR0RBCYwJIISd3d3LmRuc3NlYy1leHAub3Jngg5kbnNzZWMtZXhwLm9yZzCC
 | 
			
		||||
AUIGA1UdIASCATkwggE1MIIBMQYLKwYBBAGBtTcBAgIwggEgMC4GCCsGAQUFBwIB
 | 
			
		||||
FiJodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMDQGCCsGAQUFBwIB
 | 
			
		||||
FihodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9pbnRlcm1lZGlhdGUucGRmMIG3Bggr
 | 
			
		||||
BgEFBQcCAjCBqjAUFg1TdGFydENvbSBMdGQuMAMCAQEagZFMaW1pdGVkIExpYWJp
 | 
			
		||||
bGl0eSwgc2VlIHNlY3Rpb24gKkxlZ2FsIExpbWl0YXRpb25zKiBvZiB0aGUgU3Rh
 | 
			
		||||
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUG9saWN5IGF2YWlsYWJsZSBh
 | 
			
		||||
dCBodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMGEGA1UdHwRaMFgw
 | 
			
		||||
KqAooCaGJGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2NydDEtY3JsLmNybDAqoCig
 | 
			
		||||
JoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggrBgEF
 | 
			
		||||
BQcBAQSBgTB/MDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5zdGFydHNzbC5jb20v
 | 
			
		||||
c3ViL2NsYXNzMS9zZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly93d3cuc3Rh
 | 
			
		||||
cnRzc2wuY29tL2NlcnRzL3N1Yi5jbGFzczEuc2VydmVyLmNhLmNydDAjBgNVHRIE
 | 
			
		||||
HDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8wDQYJKoZIhvcNAQEFBQADggEB
 | 
			
		||||
ACXj6SB59KRJPenn6gUdGEqcta97U769SATyiQ87i9er64qLwvIGLMa3o2Rcgl2Y
 | 
			
		||||
kghUeyLdN/EXyFBYA8L8uvZREPoc7EZukpT/ZDLXy9i2S0jkOxvF2fD/XLbcjGjM
 | 
			
		||||
iEYG1/6ASw0ri9C0k4oDDoJLCoeH9++yqF7SFCCMcDkJqiAGXNb4euDpa8vCCtEQ
 | 
			
		||||
CSS+ObZbfkreRt3cNCf5LfCXe9OsTnCfc8Cuq81c0oLaG+SmaLUQNBuToq8e9/Zm
 | 
			
		||||
+b+/a3RVjxmkV5OCcGVBxsXNDn54Q6wsdw0TBMcjwoEndzpLS7yWgFbbkq5ZiGpw
 | 
			
		||||
Qibb2+CfKuQ+WFV1GkVQmVA=
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
const startComIntermediate = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
 | 
			
		||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
 | 
			
		||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
 | 
			
		||||
dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
 | 
			
		||||
jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
 | 
			
		||||
IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
 | 
			
		||||
YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
 | 
			
		||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
 | 
			
		||||
gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
 | 
			
		||||
pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
 | 
			
		||||
kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
 | 
			
		||||
ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
 | 
			
		||||
xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
 | 
			
		||||
AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
 | 
			
		||||
VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
 | 
			
		||||
F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
 | 
			
		||||
L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
 | 
			
		||||
YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
 | 
			
		||||
dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
 | 
			
		||||
c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
 | 
			
		||||
BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
 | 
			
		||||
BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
 | 
			
		||||
LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
 | 
			
		||||
tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
 | 
			
		||||
xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
 | 
			
		||||
xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
 | 
			
		||||
t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
 | 
			
		||||
RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
 | 
			
		||||
YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
 | 
			
		||||
WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
 | 
			
		||||
SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
 | 
			
		||||
wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
 | 
			
		||||
p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
 | 
			
		||||
0q6Dp6jOW6c=
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
const startComRoot = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
 | 
			
		||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
 | 
			
		||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
 | 
			
		||||
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
 | 
			
		||||
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
 | 
			
		||||
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
 | 
			
		||||
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
 | 
			
		||||
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
 | 
			
		||||
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
 | 
			
		||||
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
 | 
			
		||||
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
 | 
			
		||||
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
 | 
			
		||||
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
 | 
			
		||||
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
 | 
			
		||||
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
 | 
			
		||||
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
 | 
			
		||||
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
 | 
			
		||||
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
 | 
			
		||||
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
 | 
			
		||||
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
 | 
			
		||||
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
 | 
			
		||||
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
 | 
			
		||||
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
 | 
			
		||||
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
 | 
			
		||||
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
 | 
			
		||||
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
 | 
			
		||||
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
 | 
			
		||||
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
 | 
			
		||||
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
 | 
			
		||||
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
 | 
			
		||||
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
 | 
			
		||||
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
 | 
			
		||||
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
 | 
			
		||||
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
 | 
			
		||||
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
 | 
			
		||||
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
 | 
			
		||||
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
 | 
			
		||||
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
 | 
			
		||||
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
 | 
			
		||||
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
 | 
			
		||||
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
 | 
			
		||||
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
const startComRootSHA256 = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
 | 
			
		||||
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
 | 
			
		||||
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
 | 
			
		||||
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
 | 
			
		||||
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
 | 
			
		||||
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
 | 
			
		||||
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
 | 
			
		||||
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
 | 
			
		||||
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
 | 
			
		||||
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
 | 
			
		||||
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
 | 
			
		||||
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
 | 
			
		||||
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
 | 
			
		||||
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
 | 
			
		||||
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
 | 
			
		||||
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
 | 
			
		||||
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
 | 
			
		||||
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
 | 
			
		||||
AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
 | 
			
		||||
VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
 | 
			
		||||
F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
 | 
			
		||||
ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
 | 
			
		||||
ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
 | 
			
		||||
aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
 | 
			
		||||
YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
 | 
			
		||||
c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
 | 
			
		||||
aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
 | 
			
		||||
d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
 | 
			
		||||
CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
 | 
			
		||||
dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
 | 
			
		||||
wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
 | 
			
		||||
Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
 | 
			
		||||
0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
 | 
			
		||||
pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
 | 
			
		||||
CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
 | 
			
		||||
P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
 | 
			
		||||
1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
 | 
			
		||||
KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
 | 
			
		||||
JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
 | 
			
		||||
8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
 | 
			
		||||
fyWl8kgAwKQB2j8=
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
const smimeLeaf = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA
 | 
			
		||||
MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD
 | 
			
		||||
VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz
 | 
			
		||||
MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT
 | 
			
		||||
DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds
 | 
			
		||||
b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B
 | 
			
		||||
CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA
 | 
			
		||||
A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j
 | 
			
		||||
NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR
 | 
			
		||||
BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV
 | 
			
		||||
+5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf
 | 
			
		||||
9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7
 | 
			
		||||
NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV
 | 
			
		||||
HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH
 | 
			
		||||
AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud
 | 
			
		||||
EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV
 | 
			
		||||
HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0
 | 
			
		||||
cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww
 | 
			
		||||
VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh
 | 
			
		||||
bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE
 | 
			
		||||
FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU
 | 
			
		||||
bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd
 | 
			
		||||
BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy
 | 
			
		||||
q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N
 | 
			
		||||
C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl
 | 
			
		||||
a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub
 | 
			
		||||
d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
const smimeIntermediate = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG
 | 
			
		||||
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
 | 
			
		||||
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
 | 
			
		||||
MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
 | 
			
		||||
YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy
 | 
			
		||||
IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4
 | 
			
		||||
En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH
 | 
			
		||||
3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww
 | 
			
		||||
gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q
 | 
			
		||||
yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI
 | 
			
		||||
YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY
 | 
			
		||||
T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
 | 
			
		||||
MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA
 | 
			
		||||
MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu
 | 
			
		||||
LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds
 | 
			
		||||
b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN
 | 
			
		||||
NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8
 | 
			
		||||
eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
 | 
			
		||||
eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
 | 
			
		||||
YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0
 | 
			
		||||
7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2
 | 
			
		||||
2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var megaLeaf = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIFOjCCBCKgAwIBAgIQWYE8Dup170kZ+k11Lg51OjANBgkqhkiG9w0BAQUFADBy
 | 
			
		||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
 | 
			
		||||
VQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEYMBYGA1UE
 | 
			
		||||
AxMPRXNzZW50aWFsU1NMIENBMB4XDTEyMTIxNDAwMDAwMFoXDTE0MTIxNDIzNTk1
 | 
			
		||||
OVowfzEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMS4wLAYDVQQL
 | 
			
		||||
EyVIb3N0ZWQgYnkgSW5zdHJhIENvcnBvcmF0aW9uIFB0eS4gTFREMRUwEwYDVQQL
 | 
			
		||||
EwxFc3NlbnRpYWxTU0wxEzARBgNVBAMTCm1lZ2EuY28ubnowggEiMA0GCSqGSIb3
 | 
			
		||||
DQEBAQUAA4IBDwAwggEKAoIBAQDcxMCClae8BQIaJHBUIVttlLvhbK4XhXPk3RQ3
 | 
			
		||||
G5XA6tLZMBQ33l3F9knYJ0YErXtr8IdfYoulRQFmKFMJl9GtWyg4cGQi2Rcr5VN5
 | 
			
		||||
S5dA1vu4oyJBxE9fPELcK6Yz1vqaf+n6za+mYTiQYKggVdS8/s8hmNuXP9Zk1pIn
 | 
			
		||||
+q0pGsf8NAcSHMJgLqPQrTDw+zae4V03DvcYfNKjuno88d2226ld7MAmQZ7uRNsI
 | 
			
		||||
/CnkdelVs+akZsXf0szefSqMJlf08SY32t2jj4Ra7RApVYxOftD9nij/aLfuqOU6
 | 
			
		||||
ow6IgIcIG2ZvXLZwK87c5fxL7UAsTTV+M1sVv8jA33V2oKLhAgMBAAGjggG9MIIB
 | 
			
		||||
uTAfBgNVHSMEGDAWgBTay+qtWwhdzP/8JlTOSeVVxjj0+DAdBgNVHQ4EFgQUmP9l
 | 
			
		||||
6zhyrZ06Qj4zogt+6LKFk4AwDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAw
 | 
			
		||||
NAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDBglghkgB
 | 
			
		||||
hvhCBAEwTwYDVR0gBEgwRjA6BgsrBgEEAbIxAQICBzArMCkGCCsGAQUFBwIBFh1o
 | 
			
		||||
dHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzAIBgZngQwBAgEwOwYDVR0fBDQw
 | 
			
		||||
MjAwoC6gLIYqaHR0cDovL2NybC5jb21vZG9jYS5jb20vRXNzZW50aWFsU1NMQ0Eu
 | 
			
		||||
Y3JsMG4GCCsGAQUFBwEBBGIwYDA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21v
 | 
			
		||||
ZG9jYS5jb20vRXNzZW50aWFsU1NMQ0FfMi5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6
 | 
			
		||||
Ly9vY3NwLmNvbW9kb2NhLmNvbTAlBgNVHREEHjAcggptZWdhLmNvLm56gg53d3cu
 | 
			
		||||
bWVnYS5jby5uejANBgkqhkiG9w0BAQUFAAOCAQEAcYhrsPSvDuwihMOh0ZmRpbOE
 | 
			
		||||
Gw6LqKgLNTmaYUPQhzi2cyIjhUhNvugXQQlP5f0lp5j8cixmArafg1dTn4kQGgD3
 | 
			
		||||
ivtuhBTgKO1VYB/VRoAt6Lmswg3YqyiS7JiLDZxjoV7KoS5xdiaINfHDUaBBY4ZH
 | 
			
		||||
j2BUlPniNBjCqXe/HndUTVUewlxbVps9FyCmH+C4o9DWzdGBzDpCkcmo5nM+cp7q
 | 
			
		||||
ZhTIFTvZfo3zGuBoyu8BzuopCJcFRm3cRiXkpI7iOMUIixO1szkJS6WpL1sKdT73
 | 
			
		||||
UXp08U0LBqoqG130FbzEJBBV3ixbvY6BWMHoCWuaoF12KJnC5kHt2RoWAAgMXA==
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var comodoIntermediate1 = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIFAzCCA+ugAwIBAgIQGLLLuqME8aAPwfLzJkYqSjANBgkqhkiG9w0BAQUFADCB
 | 
			
		||||
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
 | 
			
		||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
 | 
			
		||||
BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
 | 
			
		||||
MDBaFw0xOTEyMzEyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh
 | 
			
		||||
dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
 | 
			
		||||
TyBDQSBMaW1pdGVkMRgwFgYDVQQDEw9Fc3NlbnRpYWxTU0wgQ0EwggEiMA0GCSqG
 | 
			
		||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt8AiwcsargxIxF3CJhakgEtSYau2A1NHf
 | 
			
		||||
5I5ZLdOWIY120j8YC0YZYwvHIPPlC92AGvFaoL0dds23Izp0XmEbdaqb1IX04XiR
 | 
			
		||||
0y3hr/yYLgbSeT1awB8hLRyuIVPGOqchfr7tZ291HRqfalsGs2rjsQuqag7nbWzD
 | 
			
		||||
ypWMN84hHzWQfdvaGlyoiBSyD8gSIF/F03/o4Tjg27z5H6Gq1huQByH6RSRQXScq
 | 
			
		||||
oChBRVt9vKCiL6qbfltTxfEFFld+Edc7tNkBdtzffRDPUanlOPJ7FAB1WfnwWdsX
 | 
			
		||||
Pvev5gItpHnBXaIcw5rIp6gLSApqLn8tl2X2xQScRMiZln5+pN0vAgMBAAGjggGD
 | 
			
		||||
MIIBfzAfBgNVHSMEGDAWgBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQU
 | 
			
		||||
2svqrVsIXcz//CZUzknlVcY49PgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
 | 
			
		||||
MAYBAf8CAQAwIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMD4GA1Ud
 | 
			
		||||
IAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21v
 | 
			
		||||
ZG8uY29tL0NQUzBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2Nh
 | 
			
		||||
LmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBsBggrBgEFBQcB
 | 
			
		||||
AQRgMF4wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NvbW9k
 | 
			
		||||
b1VUTlNHQ0NBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2Eu
 | 
			
		||||
Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAtlzR6QDLqcJcvgTtLeRJ3rvuq1xqo2l/z
 | 
			
		||||
odueTZbLN3qo6u6bldudu+Ennv1F7Q5Slqz0J790qpL0pcRDAB8OtXj5isWMcL2a
 | 
			
		||||
ejGjKdBZa0wztSz4iw+SY1dWrCRnilsvKcKxudokxeRiDn55w/65g+onO7wdQ7Vu
 | 
			
		||||
F6r7yJiIatnyfKH2cboZT7g440LX8NqxwCPf3dfxp+0Jj1agq8MLy6SSgIGSH6lv
 | 
			
		||||
+Wwz3D5XxqfyH8wqfOQsTEZf6/Nh9yvENZ+NWPU6g0QO2JOsTGvMd/QDzczc4BxL
 | 
			
		||||
XSXaPV7Od4rhPsbXlM1wSTz/Dr0ISKvlUhQVnQ6cGodWaK2cCQBk
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var comodoRoot = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
 | 
			
		||||
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
 | 
			
		||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
 | 
			
		||||
BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
 | 
			
		||||
MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
 | 
			
		||||
YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
 | 
			
		||||
RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
 | 
			
		||||
aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
 | 
			
		||||
UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
 | 
			
		||||
2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
 | 
			
		||||
Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
 | 
			
		||||
+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
 | 
			
		||||
DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
 | 
			
		||||
nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
 | 
			
		||||
/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
 | 
			
		||||
PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
 | 
			
		||||
QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
 | 
			
		||||
SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
 | 
			
		||||
IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
 | 
			
		||||
RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
 | 
			
		||||
zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
 | 
			
		||||
BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
 | 
			
		||||
ZQ==
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var nameConstraintsLeaf = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIHMTCCBRmgAwIBAgIIIZaV/3ezOJkwDQYJKoZIhvcNAQEFBQAwgcsxCzAJBgNV
 | 
			
		||||
BAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEj
 | 
			
		||||
MCEGA1UECxMaR2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1Zp
 | 
			
		||||
cmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0
 | 
			
		||||
eTExMC8GA1UEAxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZl
 | 
			
		||||
ciBDQTAeFw0xMzA5MTkxNDM2NTVaFw0xNTA5MTkxNDM2NTVaMIHNMQswCQYDVQQG
 | 
			
		||||
EwJVUzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkJsYWNrc2J1cmcxPDA6
 | 
			
		||||
BgNVBAoMM1ZpcmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUg
 | 
			
		||||
VW5pdmVyc2l0eTE7MDkGA1UECwwyVGVjaG5vbG9neS1lbmhhbmNlZCBMZWFybmlu
 | 
			
		||||
ZyBhbmQgT25saW5lIFN0cmF0ZWdpZXMxGzAZBgNVBAMMEnNlY3VyZS5pZGRsLnZ0
 | 
			
		||||
LmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkOyPpsOK/6IuPG
 | 
			
		||||
WnIBlVwlHzeYf+cUlggqkLq0b0+vZbiTXgio9/VCuNQ8opSoss7J7o3ygV9to+9Y
 | 
			
		||||
YwJKVC5WDT/y5JWpQey0CWILymViJnpNSwnxBc8A+Q8w5NUGDd/UhtPx/U8/hqbd
 | 
			
		||||
WPDYj2hbOqyq8UlRhfS5pwtnv6BbCTaY11I6FhCLK7zttISyTuWCf9p9o/ggiipP
 | 
			
		||||
ii/5oh4dkl+r5SfuSp5GPNHlYO8lWqys5NAPoDD4fc/kuflcK7Exx7XJ+Oqu0W0/
 | 
			
		||||
psjEY/tES1ZgDWU/ParcxxFpFmKHbD5DXsfPOObzkVWXIY6tGMutSlE1Froy/Nn0
 | 
			
		||||
OZsAOrcCAwEAAaOCAhMwggIPMIG4BggrBgEFBQcBAQSBqzCBqDBYBggrBgEFBQcw
 | 
			
		||||
AoZMaHR0cDovL3d3dy5wa2kudnQuZWR1L2dsb2JhbHF1YWxpZmllZHNlcnZlci9j
 | 
			
		||||
YWNlcnQvZ2xvYmFscXVhbGlmaWVkc2VydmVyLmNydDBMBggrBgEFBQcwAYZAaHR0
 | 
			
		||||
cDovL3Z0Y2EtcC5lcHJvdi5zZXRpLnZ0LmVkdTo4MDgwL2VqYmNhL3B1YmxpY3dl
 | 
			
		||||
Yi9zdGF0dXMvb2NzcDAdBgNVHQ4EFgQUp7xbO6iHkvtZbPE4jmndmnAbSEcwDAYD
 | 
			
		||||
VR0TAQH/BAIwADAfBgNVHSMEGDAWgBS8YmAn1eM1SBfpS6tFatDIqHdxjDBqBgNV
 | 
			
		||||
HSAEYzBhMA4GDCsGAQQBtGgFAgICATAOBgwrBgEEAbRoBQICAQEwPwYMKwYBBAG0
 | 
			
		||||
aAUCAgMBMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucGtpLnZ0LmVkdS9nbG9i
 | 
			
		||||
YWwvY3BzLzBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vd3d3LnBraS52dC5lZHUv
 | 
			
		||||
Z2xvYmFscXVhbGlmaWVkc2VydmVyL2NybC9jYWNybC5jcmwwDgYDVR0PAQH/BAQD
 | 
			
		||||
AgTwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHREEFjAUghJz
 | 
			
		||||
ZWN1cmUuaWRkbC52dC5lZHUwDQYJKoZIhvcNAQEFBQADggIBAEgoYo4aUtatY3gI
 | 
			
		||||
OyyKp7QlIOaLbTJZywESHqy+L5EGDdJW2DJV+mcE0LDGvqa2/1Lo+AR1ntsZwfOi
 | 
			
		||||
Y718JwgVVaX/RCd5+QKP25c5/x72xI8hb/L1bgS0ED9b0YAhd7Qm1K1ot82+6mqX
 | 
			
		||||
DW6WiGeDr8Z07MQ3143qQe2rBlq+QI69DYzm2GOqAIAnUIWv7tCyLUm31b4DwmrJ
 | 
			
		||||
TeudVreTKUbBNB1TWRFHEPkWhjjXKZnNGRO11wHXcyBu6YekIvVZ+vmx8ePee4jJ
 | 
			
		||||
3GFOi7lMuWOeq57jTVL7KOKaKLVXBb6gqo5aq+Wwt8RUD5MakrCAEeQZj7DKaFmZ
 | 
			
		||||
oQCO0Pxrsl3InCGvxnGzT+bFVO9nJ/BAMj7hknFdm9Jr6Bg5q33Z+gnf909AD9QF
 | 
			
		||||
ESqUSykaHu2LVdJx2MaCH1CyKnRgMw5tEwE15EXpUjCm24m8FMOYC+rNtf18pgrz
 | 
			
		||||
5D8Jhh+oxK9PjcBYqXNtnioIxiMCYcV0q5d4w4BYFEh71tk7/bYB0R55CsBUVPmp
 | 
			
		||||
timWNOdRd57Tfpk3USaVsumWZAf9MP3wPiC7gb4d5tYEEAG5BuDT8ruFw838wU8G
 | 
			
		||||
1VvAVutSiYBg7k3NYO7AUqZ+Ax4klQX3aM9lgonmJ78Qt94UPtbptrfZ4/lSqEf8
 | 
			
		||||
GBUwDrQNTb+gsXsDkjd5lcYxNx6l
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var nameConstraintsIntermediate1 = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIINLjCCDBagAwIBAgIRIqpyf/YoGgvHc8HiDAxAI8owDQYJKoZIhvcNAQEFBQAw
 | 
			
		||||
XDELMAkGA1UEBhMCQkUxFTATBgNVBAsTDFRydXN0ZWQgUm9vdDEZMBcGA1UEChMQ
 | 
			
		||||
R2xvYmFsU2lnbiBudi1zYTEbMBkGA1UEAxMSVHJ1c3RlZCBSb290IENBIEcyMB4X
 | 
			
		||||
DTEyMTIxMzAwMDAwMFoXDTE3MTIxMzAwMDAwMFowgcsxCzAJBgNVBAYTAlVTMREw
 | 
			
		||||
DwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEjMCEGA1UECxMa
 | 
			
		||||
R2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1ZpcmdpbmlhIFBv
 | 
			
		||||
bHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0eTExMC8GA1UE
 | 
			
		||||
AxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZlciBDQTCCAiIw
 | 
			
		||||
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALgIZhEaptBWADBqdJ45ueFGzMXa
 | 
			
		||||
GHnzNxoxR1fQIaaRQNdCg4cw3A4dWKMeEgYLtsp65ai3Xfw62Qaus0+KJ3RhgV+r
 | 
			
		||||
ihqK81NUzkls78fJlADVDI4fCTlothsrE1CTOMiy97jKHai5mVTiWxmcxpmjv7fm
 | 
			
		||||
5Nhc+uHgh2hIz6npryq495mD51ZrUTIaqAQN6Pw/VHfAmR524vgriTOjtp1t4lA9
 | 
			
		||||
pXGWjF/vkhAKFFheOQSQ00rngo2wHgCqMla64UTN0oz70AsCYNZ3jDLx0kOP0YmM
 | 
			
		||||
R3Ih91VA63kLqPXA0R6yxmmhhxLZ5bcyAy1SLjr1N302MIxLM/pSy6aquEnbELhz
 | 
			
		||||
qyp9yGgRyGJay96QH7c4RJY6gtcoPDbldDcHI9nXngdAL4DrZkJ9OkDkJLyqG66W
 | 
			
		||||
ZTF5q4EIs6yMdrywz0x7QP+OXPJrjYpbeFs6tGZCFnWPFfmHCRJF8/unofYrheq+
 | 
			
		||||
9J7Jx3U55S/k57NXbAM1RAJOuMTlfn9Etf9Dpoac9poI4Liav6rBoUQk3N3JWqnV
 | 
			
		||||
HNx/NdCyJ1/6UbKMJUZsStAVglsi6lVPo289HHOE4f7iwl3SyekizVOp01wUin3y
 | 
			
		||||
cnbZB/rXmZbwapSxTTSBf0EIOr9i4EGfnnhCAVA9U5uLrI5OEB69IY8PNX0071s3
 | 
			
		||||
Z2a2fio5c8m3JkdrAgMBAAGjggh5MIIIdTAOBgNVHQ8BAf8EBAMCAQYwTAYDVR0g
 | 
			
		||||
BEUwQzBBBgkrBgEEAaAyATwwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
 | 
			
		||||
YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wEgYDVR0TAQH/BAgwBgEB/wIBADCCBtAG
 | 
			
		||||
A1UdHgSCBscwggbDoIIGvzASghAzZGJsYWNrc2J1cmcub3JnMBiCFmFjY2VsZXJh
 | 
			
		||||
dGV2aXJnaW5pYS5jb20wGIIWYWNjZWxlcmF0ZXZpcmdpbmlhLm9yZzALgglhY3Zj
 | 
			
		||||
cC5vcmcwCYIHYmV2Lm5ldDAJggdiZXYub3JnMAuCCWNsaWdzLm9yZzAMggpjbWl3
 | 
			
		||||
ZWIub3JnMBeCFWVhc3Rlcm5icm9va3Ryb3V0Lm5ldDAXghVlYXN0ZXJuYnJvb2t0
 | 
			
		||||
cm91dC5vcmcwEYIPZWNvcnJpZG9ycy5pbmZvMBOCEWVkZ2FycmVzZWFyY2gub3Jn
 | 
			
		||||
MBKCEGdldC1lZHVjYXRlZC5jb20wE4IRZ2V0LWVkdWNhdGVkLmluZm8wEYIPZ2V0
 | 
			
		||||
ZWR1Y2F0ZWQubmV0MBKCEGdldC1lZHVjYXRlZC5uZXQwEYIPZ2V0ZWR1Y2F0ZWQu
 | 
			
		||||
b3JnMBKCEGdldC1lZHVjYXRlZC5vcmcwD4INaG9raWVjbHViLmNvbTAQgg5ob2tp
 | 
			
		||||
ZXBob3RvLmNvbTAPgg1ob2tpZXNob3AuY29tMBGCD2hva2llc3BvcnRzLmNvbTAS
 | 
			
		||||
ghBob2tpZXRpY2tldHMuY29tMBKCEGhvdGVscm9hbm9rZS5jb20wE4IRaHVtYW53
 | 
			
		||||
aWxkbGlmZS5vcmcwF4IVaW5uYXR2aXJnaW5pYXRlY2guY29tMA+CDWlzY2hwMjAx
 | 
			
		||||
MS5vcmcwD4INbGFuZHJlaGFiLm9yZzAggh5uYXRpb25hbHRpcmVyZXNlYXJjaGNl
 | 
			
		||||
bnRlci5jb20wFYITbmV0d29ya3ZpcmdpbmlhLm5ldDAMggpwZHJjdnQuY29tMBiC
 | 
			
		||||
FnBldGVkeWVyaXZlcmNvdXJzZS5jb20wDYILcmFkaW9pcS5vcmcwFYITcml2ZXJj
 | 
			
		||||
b3Vyc2Vnb2xmLmNvbTALgglzZGltaS5vcmcwEIIOc292YW1vdGlvbi5jb20wHoIc
 | 
			
		||||
c3VzdGFpbmFibGUtYmlvbWF0ZXJpYWxzLmNvbTAeghxzdXN0YWluYWJsZS1iaW9t
 | 
			
		||||
YXRlcmlhbHMub3JnMBWCE3RoaXNpc3RoZWZ1dHVyZS5jb20wGIIWdGhpcy1pcy10
 | 
			
		||||
aGUtZnV0dXJlLmNvbTAVghN0aGlzaXN0aGVmdXR1cmUubmV0MBiCFnRoaXMtaXMt
 | 
			
		||||
dGhlLWZ1dHVyZS5uZXQwCoIIdmFkcy5vcmcwDIIKdmFsZWFmLm9yZzANggt2YXRl
 | 
			
		||||
Y2guaW5mbzANggt2YXRlY2gubW9iaTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5n
 | 
			
		||||
LmNvbTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5nLm5ldDAcghp2YXRlY2hsaWZl
 | 
			
		||||
bG9uZ2xlYXJuaW5nLm9yZzAKggh2Y29tLmVkdTASghB2aXJnaW5pYXZpZXcubmV0
 | 
			
		||||
MDSCMnZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVuaXZlcnNp
 | 
			
		||||
dHkuY29tMDWCM3ZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVu
 | 
			
		||||
aXZlcnNpdHkuaW5mbzA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0dXRlYW5k
 | 
			
		||||
c3RhdGV1bml2ZXJzaXR5Lm5ldDA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0
 | 
			
		||||
dXRlYW5kc3RhdGV1bml2ZXJzaXR5Lm9yZzAZghd2aXJnaW5pYXB1YmxpY3JhZGlv
 | 
			
		||||
Lm9yZzASghB2aXJnaW5pYXRlY2guZWR1MBOCEXZpcmdpbmlhdGVjaC5tb2JpMByC
 | 
			
		||||
GnZpcmdpbmlhdGVjaGZvdW5kYXRpb24ub3JnMAiCBnZ0LmVkdTALggl2dGFyYy5v
 | 
			
		||||
cmcwDIIKdnQtYXJjLm9yZzALggl2dGNyYy5jb20wCoIIdnRpcC5vcmcwDIIKdnRs
 | 
			
		||||
ZWFuLm9yZzAWghR2dGtub3dsZWRnZXdvcmtzLmNvbTAYghZ2dGxpZmVsb25nbGVh
 | 
			
		||||
cm5pbmcuY29tMBiCFnZ0bGlmZWxvbmdsZWFybmluZy5uZXQwGIIWdnRsaWZlbG9u
 | 
			
		||||
Z2xlYXJuaW5nLm9yZzATghF2dHNwb3J0c21lZGlhLmNvbTALggl2dHdlaS5jb20w
 | 
			
		||||
D4INd2l3YXR3ZXJjLmNvbTAKggh3dnRmLm9yZzAIgQZ2dC5lZHUwd6R1MHMxCzAJ
 | 
			
		||||
BgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVy
 | 
			
		||||
ZzE8MDoGA1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFuZCBT
 | 
			
		||||
dGF0ZSBVbml2ZXJzaXR5MCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYI
 | 
			
		||||
KwYBBQUHAwkwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2NybC5nbG9iYWxzaWdu
 | 
			
		||||
LmNvbS9ncy90cnVzdHJvb3RnMi5jcmwwgYQGCCsGAQUFBwEBBHgwdjAzBggrBgEF
 | 
			
		||||
BQcwAYYnaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL3RydXN0cm9vdGcyMD8G
 | 
			
		||||
CCsGAQUFBzAChjNodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC90
 | 
			
		||||
cnVzdHJvb3RnMi5jcnQwHQYDVR0OBBYEFLxiYCfV4zVIF+lLq0Vq0Miod3GMMB8G
 | 
			
		||||
A1UdIwQYMBaAFBT25YsxtkWASkxt/MKHico2w5BiMA0GCSqGSIb3DQEBBQUAA4IB
 | 
			
		||||
AQAyJm/lOB2Er4tHXhc/+fSufSzgjohJgYfMkvG4LknkvnZ1BjliefR8tTXX49d2
 | 
			
		||||
SCDFWfGjqyJZwavavkl/4p3oXPG/nAMDMvxh4YAT+CfEK9HH+6ICV087kD4BLegi
 | 
			
		||||
+aFJMj8MMdReWCzn5sLnSR1rdse2mo2arX3Uod14SW+PGrbUmTuWNyvRbz3fVmxp
 | 
			
		||||
UdbGmj3laknO9YPsBGgHfv73pVVsTJkW4ZfY/7KdD/yaVv6ophpOB3coXfjl2+kd
 | 
			
		||||
Z4ypn2zK+cx9IL/LSewqd/7W9cD55PCUy4X9OTbEmAccwiz3LB66mQoUGfdHdkoB
 | 
			
		||||
jUY+v9vLQXmaVwI0AYL7g9LN
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var nameConstraintsIntermediate2 = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIEXTCCA0WgAwIBAgILBAAAAAABNuk6OrMwDQYJKoZIhvcNAQEFBQAwVzELMAkG
 | 
			
		||||
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
 | 
			
		||||
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMjA0MjUxMTAw
 | 
			
		||||
MDBaFw0yNzA0MjUxMTAwMDBaMFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVz
 | 
			
		||||
dGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRy
 | 
			
		||||
dXN0ZWQgUm9vdCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
 | 
			
		||||
AKyuvqrtcMr7g7EuNbu4sKwxM127UsCmx1RxbxxgcArGS7rjiefpBH/w4LYrymjf
 | 
			
		||||
vcw1ueyMNoqLo9nJMz/ORXupb35NNfE667prQYHa+tTjl1IiKpB7QUwt3wXPuTMF
 | 
			
		||||
Ja1tXtjKzkqJyuJlNuPKT76HcjgNqgV1s9qG44MD5I2JvI12du8zI1bgdQ+l/KsX
 | 
			
		||||
kTfbGjUvhOLOlVNWVQDpL+YMIrGqgBYxy5TUNgrAcRtwpNdS2KkF5otSmMweVb5k
 | 
			
		||||
hoUVv3u8UxQH/WWbNhHq1RrIlg/0rBUfi/ziShYFSB7U+aLx5DxPphTFBiDquQGp
 | 
			
		||||
tB+FC4JvnukDStFihZCZ1R8CAwEAAaOCASMwggEfMA4GA1UdDwEB/wQEAwIBBjAP
 | 
			
		||||
BgNVHRMBAf8EBTADAQH/MEcGA1UdIARAMD4wPAYEVR0gADA0MDIGCCsGAQUFBwIB
 | 
			
		||||
FiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAdBgNVHQ4E
 | 
			
		||||
FgQUFPblizG2RYBKTG38woeJyjbDkGIwMwYDVR0fBCwwKjAooCagJIYiaHR0cDov
 | 
			
		||||
L2NybC5nbG9iYWxzaWduLm5ldC9yb290LmNybDA+BggrBgEFBQcBAQQyMDAwLgYI
 | 
			
		||||
KwYBBQUHMAGGImh0dHA6Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYD
 | 
			
		||||
VR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEB
 | 
			
		||||
AL7IG0l+k4LkcpI+a/kvZsSRwSM4uA6zGX34e78A2oytr8RG8bJwVb8+AHMUD+Xe
 | 
			
		||||
2kYdh/Uj/waQXfqR0OgxQXL9Ct4ZM+JlR1avsNKXWL5AwYXAXCOB3J5PW2XOck7H
 | 
			
		||||
Zw0vRbGQhjWjQx+B4KOUFg1b3ov/z6Xkr3yaCfRQhXh7KC0Bc0RXPPG5Nv5lCW+z
 | 
			
		||||
tbbg0zMm3kyfQITRusMSg6IBsDJqOnjaiaKQRcXiD0Sk43ZXb2bUKMxC7+Td3QL4
 | 
			
		||||
RyHcWJbQ7YylLTS/x+jxWIcOQ0oO5/54t5PTQ14neYhOz9x4gUk2AYAW6d1vePwb
 | 
			
		||||
hcC8roQwkHT7HvfYBoc74FM=
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
var globalSignRoot = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
 | 
			
		||||
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
 | 
			
		||||
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
 | 
			
		||||
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
 | 
			
		||||
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
 | 
			
		||||
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
 | 
			
		||||
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
 | 
			
		||||
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
 | 
			
		||||
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
 | 
			
		||||
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
 | 
			
		||||
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
 | 
			
		||||
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
 | 
			
		||||
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
 | 
			
		||||
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
 | 
			
		||||
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
 | 
			
		||||
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
 | 
			
		||||
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
 | 
			
		||||
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
 | 
			
		||||
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
							
								
								
									
										1644
									
								
								ct/x509/x509.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1644
									
								
								ct/x509/x509.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										751
									
								
								ct/x509/x509_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										751
									
								
								ct/x509/x509_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,751 @@
 | 
			
		||||
// 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 LICENSE file.
 | 
			
		||||
 | 
			
		||||
package x509
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/dsa"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	_ "crypto/sha256"
 | 
			
		||||
	_ "crypto/sha512"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/asn1"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/x509/pkix"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"errors"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"net"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	// START CT CHANGES
 | 
			
		||||
	"strings"
 | 
			
		||||
	// END CT CHANGES
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParsePKCS1PrivateKey(t *testing.T) {
 | 
			
		||||
	block, _ := pem.Decode([]byte(pemPrivateKey))
 | 
			
		||||
	priv, err := ParsePKCS1PrivateKey(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Failed to parse private key: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if priv.PublicKey.N.Cmp(rsaPrivateKey.PublicKey.N) != 0 ||
 | 
			
		||||
		priv.PublicKey.E != rsaPrivateKey.PublicKey.E ||
 | 
			
		||||
		priv.D.Cmp(rsaPrivateKey.D) != 0 ||
 | 
			
		||||
		priv.Primes[0].Cmp(rsaPrivateKey.Primes[0]) != 0 ||
 | 
			
		||||
		priv.Primes[1].Cmp(rsaPrivateKey.Primes[1]) != 0 {
 | 
			
		||||
		t.Errorf("got:%+v want:%+v", priv, rsaPrivateKey)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePKIXPublicKey(t *testing.T) {
 | 
			
		||||
	block, _ := pem.Decode([]byte(pemPublicKey))
 | 
			
		||||
	pub, err := ParsePKIXPublicKey(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Failed to parse RSA public key: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	rsaPub, ok := pub.(*rsa.PublicKey)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pubBytes2, err := MarshalPKIXPublicKey(rsaPub)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Failed to marshal RSA public key for the second time: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !bytes.Equal(pubBytes2, block.Bytes) {
 | 
			
		||||
		t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var pemPublicKey = `-----BEGIN PUBLIC KEY-----
 | 
			
		||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+
 | 
			
		||||
wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k
 | 
			
		||||
enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ
 | 
			
		||||
FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg
 | 
			
		||||
fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR
 | 
			
		||||
FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ
 | 
			
		||||
+QIDAQAB
 | 
			
		||||
-----END PUBLIC KEY-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
 | 
			
		||||
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
 | 
			
		||||
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
 | 
			
		||||
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
 | 
			
		||||
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
 | 
			
		||||
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
 | 
			
		||||
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
 | 
			
		||||
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
 | 
			
		||||
-----END RSA PRIVATE KEY-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func bigFromString(s string) *big.Int {
 | 
			
		||||
	ret := new(big.Int)
 | 
			
		||||
	ret.SetString(s, 10)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fromBase10(base10 string) *big.Int {
 | 
			
		||||
	i := new(big.Int)
 | 
			
		||||
	i.SetString(base10, 10)
 | 
			
		||||
	return i
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func bigFromHexString(s string) *big.Int {
 | 
			
		||||
	ret := new(big.Int)
 | 
			
		||||
	ret.SetString(s, 16)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var rsaPrivateKey = &rsa.PrivateKey{
 | 
			
		||||
	PublicKey: rsa.PublicKey{
 | 
			
		||||
		N: bigFromString("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077"),
 | 
			
		||||
		E: 65537,
 | 
			
		||||
	},
 | 
			
		||||
	D: bigFromString("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861"),
 | 
			
		||||
	Primes: []*big.Int{
 | 
			
		||||
		bigFromString("98920366548084643601728869055592650835572950932266967461790948584315647051443"),
 | 
			
		||||
		bigFromString("94560208308847015747498523884063394671606671904944666360068158221458669711639"),
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMarshalRSAPrivateKey(t *testing.T) {
 | 
			
		||||
	priv := &rsa.PrivateKey{
 | 
			
		||||
		PublicKey: rsa.PublicKey{
 | 
			
		||||
			N: fromBase10("16346378922382193400538269749936049106320265317511766357599732575277382844051791096569333808598921852351577762718529818072849191122419410612033592401403764925096136759934497687765453905884149505175426053037420486697072448609022753683683718057795566811401938833367954642951433473337066311978821180526439641496973296037000052546108507805269279414789035461158073156772151892452251106173507240488993608650881929629163465099476849643165682709047462010581308719577053905787496296934240246311806555924593059995202856826239801816771116902778517096212527979497399966526283516447337775509777558018145573127308919204297111496233"),
 | 
			
		||||
			E: 3,
 | 
			
		||||
		},
 | 
			
		||||
		D: fromBase10("10897585948254795600358846499957366070880176878341177571733155050184921896034527397712889205732614568234385175145686545381899460748279607074689061600935843283397424506622998458510302603922766336783617368686090042765718290914099334449154829375179958369993407724946186243249568928237086215759259909861748642124071874879861299389874230489928271621259294894142840428407196932444474088857746123104978617098858619445675532587787023228852383149557470077802718705420275739737958953794088728369933811184572620857678792001136676902250566845618813972833750098806496641114644760255910789397593428910198080271317419213080834885003"),
 | 
			
		||||
		Primes: []*big.Int{
 | 
			
		||||
			fromBase10("1025363189502892836833747188838978207017355117492483312747347695538428729137306368764177201532277413433182799108299960196606011786562992097313508180436744488171474690412562218914213688661311117337381958560443"),
 | 
			
		||||
			fromBase10("3467903426626310123395340254094941045497208049900750380025518552334536945536837294961497712862519984786362199788654739924501424784631315081391467293694361474867825728031147665777546570788493758372218019373"),
 | 
			
		||||
			fromBase10("4597024781409332673052708605078359346966325141767460991205742124888960305710298765592730135879076084498363772408626791576005136245060321874472727132746643162385746062759369754202494417496879741537284589047"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	derBytes := MarshalPKCS1PrivateKey(priv)
 | 
			
		||||
 | 
			
		||||
	priv2, err := ParsePKCS1PrivateKey(derBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error parsing serialized key: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if priv.PublicKey.N.Cmp(priv2.PublicKey.N) != 0 ||
 | 
			
		||||
		priv.PublicKey.E != priv2.PublicKey.E ||
 | 
			
		||||
		priv.D.Cmp(priv2.D) != 0 ||
 | 
			
		||||
		len(priv2.Primes) != 3 ||
 | 
			
		||||
		priv.Primes[0].Cmp(priv2.Primes[0]) != 0 ||
 | 
			
		||||
		priv.Primes[1].Cmp(priv2.Primes[1]) != 0 ||
 | 
			
		||||
		priv.Primes[2].Cmp(priv2.Primes[2]) != 0 {
 | 
			
		||||
		t.Errorf("got:%+v want:%+v", priv, priv2)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type matchHostnamesTest struct {
 | 
			
		||||
	pattern, host string
 | 
			
		||||
	ok            bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var matchHostnamesTests = []matchHostnamesTest{
 | 
			
		||||
	{"a.b.c", "a.b.c", true},
 | 
			
		||||
	{"a.b.c", "b.b.c", false},
 | 
			
		||||
	{"", "b.b.c", false},
 | 
			
		||||
	{"a.b.c", "", false},
 | 
			
		||||
	{"example.com", "example.com", true},
 | 
			
		||||
	{"example.com", "www.example.com", false},
 | 
			
		||||
	{"*.example.com", "www.example.com", true},
 | 
			
		||||
	{"*.example.com", "xyz.www.example.com", false},
 | 
			
		||||
	{"*.*.example.com", "xyz.www.example.com", true},
 | 
			
		||||
	{"*.www.*.com", "xyz.www.example.com", true},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMatchHostnames(t *testing.T) {
 | 
			
		||||
	for i, test := range matchHostnamesTests {
 | 
			
		||||
		r := matchHostnames(test.pattern, test.host)
 | 
			
		||||
		if r != test.ok {
 | 
			
		||||
			t.Errorf("#%d mismatch got: %t want: %t", i, r, test.ok)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMatchIP(t *testing.T) {
 | 
			
		||||
	// Check that pattern matching is working.
 | 
			
		||||
	c := &Certificate{
 | 
			
		||||
		DNSNames: []string{"*.foo.bar.baz"},
 | 
			
		||||
		Subject: pkix.Name{
 | 
			
		||||
			CommonName: "*.foo.bar.baz",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err := c.VerifyHostname("quux.foo.bar.baz")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("VerifyHostname(quux.foo.bar.baz): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// But check that if we change it to be matching against an IP address,
 | 
			
		||||
	// it is rejected.
 | 
			
		||||
	c = &Certificate{
 | 
			
		||||
		DNSNames: []string{"*.2.3.4"},
 | 
			
		||||
		Subject: pkix.Name{
 | 
			
		||||
			CommonName: "*.2.3.4",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	err = c.VerifyHostname("1.2.3.4")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatalf("VerifyHostname(1.2.3.4) should have failed, did not")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c = &Certificate{
 | 
			
		||||
		IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
 | 
			
		||||
	}
 | 
			
		||||
	err = c.VerifyHostname("127.0.0.1")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("VerifyHostname(127.0.0.1): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = c.VerifyHostname("::1")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("VerifyHostname(::1): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = c.VerifyHostname("[::1]")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("VerifyHostname([::1]): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCertificateParse(t *testing.T) {
 | 
			
		||||
	s, _ := hex.DecodeString(certBytes)
 | 
			
		||||
	certs, err := ParseCertificates(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(certs) != 2 {
 | 
			
		||||
		t.Errorf("Wrong number of certs: got %d want 2", len(certs))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = certs[0].CheckSignatureFrom(certs[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := certs[0].VerifyHostname("mail.google.com"); err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const expectedExtensions = 4
 | 
			
		||||
	if n := len(certs[0].Extensions); n != expectedExtensions {
 | 
			
		||||
		t.Errorf("want %d extensions, got %d", expectedExtensions, n)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be1300d06092a864886" +
 | 
			
		||||
	"f70d0101050500304c310b3009060355040613025a4131253023060355040a131c546861777465" +
 | 
			
		||||
	"20436f6e73756c74696e67202850747929204c74642e311630140603550403130d546861777465" +
 | 
			
		||||
	"20534743204341301e170d3039303332353136343932395a170d3130303332353136343932395a" +
 | 
			
		||||
	"3069310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630" +
 | 
			
		||||
	"140603550407130d4d6f756e7461696e205669657731133011060355040a130a476f6f676c6520" +
 | 
			
		||||
	"496e63311830160603550403130f6d61696c2e676f6f676c652e636f6d30819f300d06092a8648" +
 | 
			
		||||
	"86f70d010101050003818d0030818902818100c5d6f892fccaf5614b064149e80a2c9581a218ef" +
 | 
			
		||||
	"41ec35bd7a58125ae76f9ea54ddc893abbeb029f6b73616bf0ffd868791fba7af9c4aebf3706ba" +
 | 
			
		||||
	"3eeaeed27435b4ddcfb157c05f351d66aa87fee0de072d66d773affbd36ab78bef090e0cc861a9" +
 | 
			
		||||
	"03ac90dd98b51c9c41566c017f0beec3bff391051ffba0f5cc6850ad2a590203010001a381e730" +
 | 
			
		||||
	"81e430280603551d250421301f06082b0601050507030106082b06010505070302060960864801" +
 | 
			
		||||
	"86f842040130360603551d1f042f302d302ba029a0278625687474703a2f2f63726c2e74686177" +
 | 
			
		||||
	"74652e636f6d2f54686177746553474343412e63726c307206082b060105050701010466306430" +
 | 
			
		||||
	"2206082b060105050730018616687474703a2f2f6f6373702e7468617774652e636f6d303e0608" +
 | 
			
		||||
	"2b060105050730028632687474703a2f2f7777772e7468617774652e636f6d2f7265706f736974" +
 | 
			
		||||
	"6f72792f5468617774655f5347435f43412e637274300c0603551d130101ff04023000300d0609" +
 | 
			
		||||
	"2a864886f70d01010505000381810062f1f3050ebc105e497c7aedf87e24d2f4a986bb3b837bd1" +
 | 
			
		||||
	"9b91ebcad98b065992f6bd2b49b7d6d3cb2e427a99d606c7b1d46352527fac39e6a8b6726de5bf" +
 | 
			
		||||
	"70212a52cba07634a5e332011bd1868e78eb5e3c93cf03072276786f207494feaa0ed9d53b2110" +
 | 
			
		||||
	"a76571f90209cdae884385c882587030ee15f33d761e2e45a6bc308203233082028ca003020102" +
 | 
			
		||||
	"020430000002300d06092a864886f70d0101050500305f310b3009060355040613025553311730" +
 | 
			
		||||
	"15060355040a130e566572695369676e2c20496e632e31373035060355040b132e436c61737320" +
 | 
			
		||||
	"33205075626c6963205072696d6172792043657274696669636174696f6e20417574686f726974" +
 | 
			
		||||
	"79301e170d3034303531333030303030305a170d3134303531323233353935395a304c310b3009" +
 | 
			
		||||
	"060355040613025a4131253023060355040a131c54686177746520436f6e73756c74696e672028" +
 | 
			
		||||
	"50747929204c74642e311630140603550403130d5468617774652053474320434130819f300d06" +
 | 
			
		||||
	"092a864886f70d010101050003818d0030818902818100d4d367d08d157faecd31fe7d1d91a13f" +
 | 
			
		||||
	"0b713cacccc864fb63fc324b0794bd6f80ba2fe10493c033fc093323e90b742b71c403c6d2cde2" +
 | 
			
		||||
	"2ff50963cdff48a500bfe0e7f388b72d32de9836e60aad007bc4644a3b847503f270927d0e62f5" +
 | 
			
		||||
	"21ab693684317590f8bfc76c881b06957cc9e5a8de75a12c7a68dfd5ca1c875860190203010001" +
 | 
			
		||||
	"a381fe3081fb30120603551d130101ff040830060101ff020100300b0603551d0f040403020106" +
 | 
			
		||||
	"301106096086480186f842010104040302010630280603551d110421301fa41d301b3119301706" +
 | 
			
		||||
	"035504031310507269766174654c6162656c332d313530310603551d1f042a30283026a024a022" +
 | 
			
		||||
	"8620687474703a2f2f63726c2e766572697369676e2e636f6d2f706361332e63726c303206082b" +
 | 
			
		||||
	"0601050507010104263024302206082b060105050730018616687474703a2f2f6f6373702e7468" +
 | 
			
		||||
	"617774652e636f6d30340603551d25042d302b06082b0601050507030106082b06010505070302" +
 | 
			
		||||
	"06096086480186f8420401060a6086480186f845010801300d06092a864886f70d010105050003" +
 | 
			
		||||
	"81810055ac63eadea1ddd2905f9f0bce76be13518f93d9052bc81b774bad6950a1eededcfddb07" +
 | 
			
		||||
	"e9e83994dcab72792f06bfab8170c4a8edea5334edef1e53d906c7562bd15cf4d18a8eb42bb137" +
 | 
			
		||||
	"9048084225c53e8acb7feb6f04d16dc574a2f7a27c7b603c77cd0ece48027f012fb69b37e02a2a" +
 | 
			
		||||
	"36dcd585d6ace53f546f961e05af"
 | 
			
		||||
 | 
			
		||||
func TestCreateSelfSignedCertificate(t *testing.T) {
 | 
			
		||||
	random := rand.Reader
 | 
			
		||||
 | 
			
		||||
	block, _ := pem.Decode([]byte(pemPrivateKey))
 | 
			
		||||
	rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed to parse private key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed to generate ECDSA key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name      string
 | 
			
		||||
		pub, priv interface{}
 | 
			
		||||
		checkSig  bool
 | 
			
		||||
	}{
 | 
			
		||||
		{"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true},
 | 
			
		||||
		{"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false},
 | 
			
		||||
		{"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false},
 | 
			
		||||
		{"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
 | 
			
		||||
	testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}}
 | 
			
		||||
	extraExtensionData := []byte("extra extension")
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		commonName := "test.example.com"
 | 
			
		||||
		template := Certificate{
 | 
			
		||||
			SerialNumber: big.NewInt(1),
 | 
			
		||||
			Subject: pkix.Name{
 | 
			
		||||
				CommonName:   commonName,
 | 
			
		||||
				Organization: []string{"Σ Acme Co"},
 | 
			
		||||
			},
 | 
			
		||||
			NotBefore: time.Unix(1000, 0),
 | 
			
		||||
			NotAfter:  time.Unix(100000, 0),
 | 
			
		||||
 | 
			
		||||
			SubjectKeyId: []byte{1, 2, 3, 4},
 | 
			
		||||
			KeyUsage:     KeyUsageCertSign,
 | 
			
		||||
 | 
			
		||||
			ExtKeyUsage:        testExtKeyUsage,
 | 
			
		||||
			UnknownExtKeyUsage: testUnknownExtKeyUsage,
 | 
			
		||||
 | 
			
		||||
			BasicConstraintsValid: true,
 | 
			
		||||
			IsCA: true,
 | 
			
		||||
 | 
			
		||||
			OCSPServer:            []string{"http://ocsp.example.com"},
 | 
			
		||||
			IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"},
 | 
			
		||||
 | 
			
		||||
			DNSNames:       []string{"test.example.com"},
 | 
			
		||||
			EmailAddresses: []string{"gopher@golang.org"},
 | 
			
		||||
			IPAddresses:    []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")},
 | 
			
		||||
 | 
			
		||||
			PolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},
 | 
			
		||||
			PermittedDNSDomains: []string{".example.com", "example.com"},
 | 
			
		||||
 | 
			
		||||
			CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
 | 
			
		||||
 | 
			
		||||
			ExtraExtensions: []pkix.Extension{
 | 
			
		||||
				{
 | 
			
		||||
					Id:    []int{1, 2, 3, 4},
 | 
			
		||||
					Value: extraExtensionData,
 | 
			
		||||
				},
 | 
			
		||||
				// This extension should override the SubjectKeyId, above.
 | 
			
		||||
				{
 | 
			
		||||
					Id:       oidExtensionSubjectKeyId,
 | 
			
		||||
					Critical: false,
 | 
			
		||||
					Value:    []byte{0x04, 0x04, 4, 3, 2, 1},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("%s: failed to create certificate: %s", test.name, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cert, err := ParseCertificate(derBytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("%s: failed to parse certificate: %s", test.name, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
 | 
			
		||||
			t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
 | 
			
		||||
			t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if cert.Subject.CommonName != commonName {
 | 
			
		||||
			t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if cert.Issuer.CommonName != commonName {
 | 
			
		||||
			t.Errorf("%s: issuer wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Issuer.CommonName, commonName)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.ExtKeyUsage, testExtKeyUsage) {
 | 
			
		||||
			t.Errorf("%s: extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.ExtKeyUsage, testExtKeyUsage)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.UnknownExtKeyUsage, testUnknownExtKeyUsage) {
 | 
			
		||||
			t.Errorf("%s: unknown extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.UnknownExtKeyUsage, testUnknownExtKeyUsage)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.OCSPServer, template.OCSPServer) {
 | 
			
		||||
			t.Errorf("%s: OCSP servers differ from template. Got %v, want %v", test.name, cert.OCSPServer, template.OCSPServer)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.IssuingCertificateURL, template.IssuingCertificateURL) {
 | 
			
		||||
			t.Errorf("%s: Issuing certificate URLs differ from template. Got %v, want %v", test.name, cert.IssuingCertificateURL, template.IssuingCertificateURL)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.DNSNames, template.DNSNames) {
 | 
			
		||||
			t.Errorf("%s: SAN DNS names differ from template. Got %v, want %v", test.name, cert.DNSNames, template.DNSNames)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.EmailAddresses, template.EmailAddresses) {
 | 
			
		||||
			t.Errorf("%s: SAN emails differ from template. Got %v, want %v", test.name, cert.EmailAddresses, template.EmailAddresses)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.IPAddresses, template.IPAddresses) {
 | 
			
		||||
			t.Errorf("%s: SAN IPs differ from template. Got %v, want %v", test.name, cert.IPAddresses, template.IPAddresses)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(cert.CRLDistributionPoints, template.CRLDistributionPoints) {
 | 
			
		||||
			t.Errorf("%s: CRL distribution points differ from template. Got %v, want %v", test.name, cert.CRLDistributionPoints, template.CRLDistributionPoints)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !bytes.Equal(cert.SubjectKeyId, []byte{4, 3, 2, 1}) {
 | 
			
		||||
			t.Errorf("%s: ExtraExtensions didn't override SubjectKeyId", test.name)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bytes.Index(derBytes, extraExtensionData) == -1 {
 | 
			
		||||
			t.Errorf("%s: didn't find extra extension in DER output", test.name)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if test.checkSig {
 | 
			
		||||
			err = cert.CheckSignatureFrom(cert)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Errorf("%s: signature verification failed: %s", test.name, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Self-signed certificate using ECDSA with SHA1 & secp256r1
 | 
			
		||||
var ecdsaSHA1CertPem = `
 | 
			
		||||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICDjCCAbUCCQDF6SfN0nsnrjAJBgcqhkjOPQQBMIGPMQswCQYDVQQGEwJVUzET
 | 
			
		||||
MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMG
 | 
			
		||||
A1UECgwMR29vZ2xlLCBJbmMuMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
 | 
			
		||||
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIwMjAyMDUw
 | 
			
		||||
WhcNMjIwNTE4MjAyMDUwWjCBjzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
 | 
			
		||||
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTATBgNVBAoMDEdvb2dsZSwg
 | 
			
		||||
SW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAhBgkqhkiG9w0BCQEWFGdv
 | 
			
		||||
bGFuZy1kZXZAZ21haWwuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/Wgn
 | 
			
		||||
WQDo5+bz71T0327ERgd5SDDXFbXLpzIZDXTkjpe8QTEbsF+ezsQfrekrpDPC4Cd3
 | 
			
		||||
P9LY0tG+aI8IyVKdUjAJBgcqhkjOPQQBA0gAMEUCIGlsqMcRqWVIWTD6wXwe6Jk2
 | 
			
		||||
DKxL46r/FLgJYnzBEH99AiEA3fBouObsvV1R3oVkb4BQYnD4/4LeId6lAT43YvyV
 | 
			
		||||
a/A=
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// Self-signed certificate using ECDSA with SHA256 & secp256r1
 | 
			
		||||
var ecdsaSHA256p256CertPem = `
 | 
			
		||||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICDzCCAbYCCQDlsuMWvgQzhTAKBggqhkjOPQQDAjCBjzELMAkGA1UEBhMCVVMx
 | 
			
		||||
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
 | 
			
		||||
BgNVBAoMDEdvb2dsZSwgSW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAh
 | 
			
		||||
BgkqhkiG9w0BCQEWFGdvbGFuZy1kZXZAZ21haWwuY29tMB4XDTEyMDUyMTAwMTkx
 | 
			
		||||
NloXDTIyMDUxOTAwMTkxNlowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
 | 
			
		||||
Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYDVQQKDAxHb29nbGUs
 | 
			
		||||
IEluYy4xFzAVBgNVBAMMDnd3dy5nb29nbGUuY29tMSMwIQYJKoZIhvcNAQkBFhRn
 | 
			
		||||
b2xhbmctZGV2QGdtYWlsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMt
 | 
			
		||||
2ErhxAty5EJRu9yM+MTy+hUXm3pdW1ensAv382KoGExSXAFWP7pjJnNtHO+XSwVm
 | 
			
		||||
YNtqjcAGFKpweoN//kQwCgYIKoZIzj0EAwIDRwAwRAIgIYSaUA/IB81gjbIw/hUV
 | 
			
		||||
70twxJr5EcgOo0hLp3Jm+EYCIFDO3NNcgmURbJ1kfoS3N/0O+irUtoPw38YoNkqJ
 | 
			
		||||
h5wi
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// Self-signed certificate using ECDSA with SHA256 & secp384r1
 | 
			
		||||
var ecdsaSHA256p384CertPem = `
 | 
			
		||||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
 | 
			
		||||
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
 | 
			
		||||
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
 | 
			
		||||
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
 | 
			
		||||
WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
 | 
			
		||||
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
 | 
			
		||||
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
 | 
			
		||||
YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
 | 
			
		||||
jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
 | 
			
		||||
qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
 | 
			
		||||
zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
 | 
			
		||||
PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
 | 
			
		||||
3yILeYQzllt/g0rKVRk=
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
// Self-signed certificate using ECDSA with SHA384 & secp521r1
 | 
			
		||||
var ecdsaSHA384p521CertPem = `
 | 
			
		||||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIICljCCAfcCCQDhp1AFD/ahKjAKBggqhkjOPQQDAzCBjjELMAkGA1UEBhMCVVMx
 | 
			
		||||
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
 | 
			
		||||
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
 | 
			
		||||
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMTUwNDI5
 | 
			
		||||
WhcNMjIwNTE5MTUwNDI5WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
 | 
			
		||||
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
 | 
			
		||||
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
 | 
			
		||||
YW5nLWRldkBnbWFpbC5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACqx9Rv
 | 
			
		||||
IssRs1LWYcNN+WffwlHw4Tv3y8/LIAA9MF1ZScIonU9nRMxt4a2uGJVCPDw6JHpz
 | 
			
		||||
PaYc0E9puLoE9AfKpwFr59Jkot7dBg55SKPEFkddoip/rvmN7NPAWjMBirOwjOkm
 | 
			
		||||
8FPthvPhGPqsu9AvgVuHu3PosWiHGNrhh379pva8MzAKBggqhkjOPQQDAwOBjAAw
 | 
			
		||||
gYgCQgEHNmswkUdPpHqrVxp9PvLVl+xxPuHBkT+75z9JizyxtqykHQo9Uh6SWCYH
 | 
			
		||||
BF9KLolo01wMt8DjoYP5Fb3j5MH7xwJCAbWZzTOp4l4DPkIvAh4LeC4VWbwPPyqh
 | 
			
		||||
kBg71w/iEcSY3wUKgHGcJJrObZw7wys91I5kENljqw/Samdr3ka+jBJa
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
var ecdsaTests = []struct {
 | 
			
		||||
	sigAlgo SignatureAlgorithm
 | 
			
		||||
	pemCert string
 | 
			
		||||
}{
 | 
			
		||||
	{ECDSAWithSHA1, ecdsaSHA1CertPem},
 | 
			
		||||
	{ECDSAWithSHA256, ecdsaSHA256p256CertPem},
 | 
			
		||||
	{ECDSAWithSHA256, ecdsaSHA256p384CertPem},
 | 
			
		||||
	{ECDSAWithSHA384, ecdsaSHA384p521CertPem},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestECDSA(t *testing.T) {
 | 
			
		||||
	for i, test := range ecdsaTests {
 | 
			
		||||
		pemBlock, _ := pem.Decode([]byte(test.pemCert))
 | 
			
		||||
		cert, err := ParseCertificate(pemBlock.Bytes)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("%d: failed to parse certificate: %s", i, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if sa := cert.SignatureAlgorithm; sa != test.sigAlgo {
 | 
			
		||||
			t.Errorf("%d: signature algorithm is %v, want %v", i, sa, test.sigAlgo)
 | 
			
		||||
		}
 | 
			
		||||
		if parsedKey, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {
 | 
			
		||||
			t.Errorf("%d: wanted an ECDSA public key but found: %#v", i, parsedKey)
 | 
			
		||||
		}
 | 
			
		||||
		if pka := cert.PublicKeyAlgorithm; pka != ECDSA {
 | 
			
		||||
			t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka)
 | 
			
		||||
		}
 | 
			
		||||
		if err = cert.CheckSignatureFrom(cert); err != nil {
 | 
			
		||||
			t.Errorf("%d: certificate verification failed: %s", i, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Self-signed certificate using DSA with SHA1
 | 
			
		||||
var dsaCertPem = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIEDTCCA82gAwIBAgIJALHPghaoxeDhMAkGByqGSM44BAMweTELMAkGA1UEBhMC
 | 
			
		||||
VVMxCzAJBgNVBAgTAk5DMQ8wDQYDVQQHEwZOZXd0b24xFDASBgNVBAoTC0dvb2ds
 | 
			
		||||
ZSwgSW5jMRIwEAYDVQQDEwlKb24gQWxsaWUxIjAgBgkqhkiG9w0BCQEWE2pvbmFs
 | 
			
		||||
bGllQGdvb2dsZS5jb20wHhcNMTEwNTE0MDMwMTQ1WhcNMTEwNjEzMDMwMTQ1WjB5
 | 
			
		||||
MQswCQYDVQQGEwJVUzELMAkGA1UECBMCTkMxDzANBgNVBAcTBk5ld3RvbjEUMBIG
 | 
			
		||||
A1UEChMLR29vZ2xlLCBJbmMxEjAQBgNVBAMTCUpvbiBBbGxpZTEiMCAGCSqGSIb3
 | 
			
		||||
DQEJARYTam9uYWxsaWVAZ29vZ2xlLmNvbTCCAbcwggEsBgcqhkjOOAQBMIIBHwKB
 | 
			
		||||
gQC8hLUnQ7FpFYu4WXTj6DKvXvz8QrJkNJCVMTpKAT7uBpobk32S5RrPKXocd4gN
 | 
			
		||||
8lyGB9ggS03EVlEwXvSmO0DH2MQtke2jl9j1HLydClMf4sbx5V6TV9IFw505U1iW
 | 
			
		||||
jL7awRMgxge+FsudtJK254FjMFo03ZnOQ8ZJJ9E6AEDrlwIVAJpnBn9moyP11Ox5
 | 
			
		||||
Asc/5dnjb6dPAoGBAJFHd4KVv1iTVCvEG6gGiYop5DJh28hUQcN9kul+2A0yPUSC
 | 
			
		||||
X93oN00P8Vh3eYgSaCWZsha7zDG53MrVJ0Zf6v/X/CoZNhLldeNOepivTRAzn+Rz
 | 
			
		||||
kKUYy5l1sxYLHQKF0UGNCXfFKZT0PCmgU+PWhYNBBMn6/cIh44vp85ideo5CA4GE
 | 
			
		||||
AAKBgFmifCafzeRaohYKXJgMGSEaggCVCRq5xdyDCat+wbOkjC4mfG01/um3G8u5
 | 
			
		||||
LxasjlWRKTR/tcAL7t0QuokVyQaYdVypZXNaMtx1db7YBuHjj3aP+8JOQRI9xz8c
 | 
			
		||||
bp5NDJ5pISiFOv4p3GZfqZPcqckDt78AtkQrmnal2txhhjF6o4HeMIHbMB0GA1Ud
 | 
			
		||||
DgQWBBQVyyr7hO11ZFFpWX50298Sa3V+rzCBqwYDVR0jBIGjMIGggBQVyyr7hO11
 | 
			
		||||
ZFFpWX50298Sa3V+r6F9pHsweTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5DMQ8w
 | 
			
		||||
DQYDVQQHEwZOZXd0b24xFDASBgNVBAoTC0dvb2dsZSwgSW5jMRIwEAYDVQQDEwlK
 | 
			
		||||
b24gQWxsaWUxIjAgBgkqhkiG9w0BCQEWE2pvbmFsbGllQGdvb2dsZS5jb22CCQCx
 | 
			
		||||
z4IWqMXg4TAMBgNVHRMEBTADAQH/MAkGByqGSM44BAMDLwAwLAIUPtn/5j8Q1jJI
 | 
			
		||||
7ggOIsgrhgUdjGQCFCsmDq1H11q9+9Wp9IMeGrTSKHIM
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func TestParseCertificateWithDsaPublicKey(t *testing.T) {
 | 
			
		||||
	expectedKey := &dsa.PublicKey{
 | 
			
		||||
		Parameters: dsa.Parameters{
 | 
			
		||||
			P: bigFromHexString("00BC84B52743B169158BB85974E3E832AF5EFCFC42B264349095313A4A013EEE069A1B937D92E51ACF297A1C77880DF25C8607D8204B4DC45651305EF4A63B40C7D8C42D91EDA397D8F51CBC9D0A531FE2C6F1E55E9357D205C39D395358968CBEDAC11320C607BE16CB9DB492B6E78163305A34DD99CE43C64927D13A0040EB97"),
 | 
			
		||||
			Q: bigFromHexString("009A67067F66A323F5D4EC7902C73FE5D9E36FA74F"),
 | 
			
		||||
			G: bigFromHexString("009147778295BF5893542BC41BA806898A29E43261DBC85441C37D92E97ED80D323D44825FDDE8374D0FF15877798812682599B216BBCC31B9DCCAD527465FEAFFD7FC2A193612E575E34E7A98AF4D10339FE47390A518CB9975B3160B1D0285D1418D0977C52994F43C29A053E3D685834104C9FAFDC221E38BE9F3989D7A8E42"),
 | 
			
		||||
		},
 | 
			
		||||
		Y: bigFromHexString("59A27C269FCDE45AA2160A5C980C19211A820095091AB9C5DC8309AB7EC1B3A48C2E267C6D35FEE9B71BCBB92F16AC8E559129347FB5C00BEEDD10BA8915C90698755CA965735A32DC7575BED806E1E38F768FFBC24E41123DC73F1C6E9E4D0C9E692128853AFE29DC665FA993DCA9C903B7BF00B6442B9A76A5DADC6186317A"),
 | 
			
		||||
	}
 | 
			
		||||
	pemBlock, _ := pem.Decode([]byte(dsaCertPem))
 | 
			
		||||
	cert, err := ParseCertificate(pemBlock.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed to parse certificate: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if cert.PublicKeyAlgorithm != DSA {
 | 
			
		||||
		t.Errorf("Parsed key algorithm was not DSA")
 | 
			
		||||
	}
 | 
			
		||||
	parsedKey, ok := cert.PublicKey.(*dsa.PublicKey)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		t.Fatalf("Parsed key was not a DSA key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if expectedKey.Y.Cmp(parsedKey.Y) != 0 ||
 | 
			
		||||
		expectedKey.P.Cmp(parsedKey.P) != 0 ||
 | 
			
		||||
		expectedKey.Q.Cmp(parsedKey.Q) != 0 ||
 | 
			
		||||
		expectedKey.G.Cmp(parsedKey.G) != 0 {
 | 
			
		||||
		t.Fatal("Parsed key differs from expected key")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseCertificateWithDSASignatureAlgorithm(t *testing.T) {
 | 
			
		||||
	pemBlock, _ := pem.Decode([]byte(dsaCertPem))
 | 
			
		||||
	cert, err := ParseCertificate(pemBlock.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed to parse certificate: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if cert.SignatureAlgorithm != DSAWithSHA1 {
 | 
			
		||||
		t.Errorf("Parsed signature algorithm was not DSAWithSHA1")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVerifyCertificateWithDSASignature(t *testing.T) {
 | 
			
		||||
	pemBlock, _ := pem.Decode([]byte(dsaCertPem))
 | 
			
		||||
	cert, err := ParseCertificate(pemBlock.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Failed to parse certificate: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	// test cert is self-signed
 | 
			
		||||
	if err = cert.CheckSignatureFrom(cert); err != nil {
 | 
			
		||||
		t.Fatalf("DSA Certificate verification failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const pemCertificate = `-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIB5DCCAZCgAwIBAgIBATALBgkqhkiG9w0BAQUwLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UE
 | 
			
		||||
AxMQdGVzdC5leGFtcGxlLmNvbTAeFw03MDAxMDEwMDE2NDBaFw03MDAxMDIwMzQ2NDBaMC0xEDAO
 | 
			
		||||
BgNVBAoTB0FjbWUgQ28xGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wWjALBgkqhkiG9w0BAQED
 | 
			
		||||
SwAwSAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0fd7Ai2KW5ToIwzFo
 | 
			
		||||
fvJcS/STa6HA5gQenRUCAwEAAaOBnjCBmzAOBgNVHQ8BAf8EBAMCAAQwDwYDVR0TAQH/BAUwAwEB
 | 
			
		||||
/zANBgNVHQ4EBgQEAQIDBDAPBgNVHSMECDAGgAQBAgMEMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBs
 | 
			
		||||
ZS5jb20wDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4
 | 
			
		||||
YW1wbGUuY29tMAsGCSqGSIb3DQEBBQNBAHKZKoS1wEQOGhgklx4+/yFYQlnqwKXvar/ZecQvJwui
 | 
			
		||||
0seMQnwBhwdBkHfVIU2Fu5VUMRyxlf0ZNaDXcpU581k=
 | 
			
		||||
-----END CERTIFICATE-----`
 | 
			
		||||
 | 
			
		||||
func TestCRLCreation(t *testing.T) {
 | 
			
		||||
	block, _ := pem.Decode([]byte(pemPrivateKey))
 | 
			
		||||
	priv, _ := ParsePKCS1PrivateKey(block.Bytes)
 | 
			
		||||
	block, _ = pem.Decode([]byte(pemCertificate))
 | 
			
		||||
	cert, _ := ParseCertificate(block.Bytes)
 | 
			
		||||
 | 
			
		||||
	now := time.Unix(1000, 0)
 | 
			
		||||
	expiry := time.Unix(10000, 0)
 | 
			
		||||
 | 
			
		||||
	revokedCerts := []pkix.RevokedCertificate{
 | 
			
		||||
		{
 | 
			
		||||
			SerialNumber:   big.NewInt(1),
 | 
			
		||||
			RevocationTime: now,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			SerialNumber:   big.NewInt(42),
 | 
			
		||||
			RevocationTime: now,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	crlBytes, err := cert.CreateCRL(rand.Reader, priv, revokedCerts, now, expiry)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error creating CRL: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = ParseDERCRL(crlBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error reparsing CRL: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fromBase64(in string) []byte {
 | 
			
		||||
	out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
 | 
			
		||||
	_, err := base64.StdEncoding.Decode(out, []byte(in))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic("failed to base64 decode")
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseDERCRL(t *testing.T) {
 | 
			
		||||
	derBytes := fromBase64(derCRLBase64)
 | 
			
		||||
	certList, err := ParseDERCRL(derBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error parsing: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	numCerts := len(certList.TBSCertList.RevokedCertificates)
 | 
			
		||||
	expected := 88
 | 
			
		||||
	if numCerts != expected {
 | 
			
		||||
		t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if certList.HasExpired(time.Unix(1302517272, 0)) {
 | 
			
		||||
		t.Errorf("CRL has expired (but shouldn't have)")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Can't check the signature here without a package cycle.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePEMCRL(t *testing.T) {
 | 
			
		||||
	pemBytes := fromBase64(pemCRLBase64)
 | 
			
		||||
	certList, err := ParseCRL(pemBytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("error parsing: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	numCerts := len(certList.TBSCertList.RevokedCertificates)
 | 
			
		||||
	expected := 2
 | 
			
		||||
	if numCerts != expected {
 | 
			
		||||
		t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if certList.HasExpired(time.Unix(1302517272, 0)) {
 | 
			
		||||
		t.Errorf("CRL has expired (but shouldn't have)")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Can't check the signature here without a package cycle.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// START CT CHANGES
 | 
			
		||||
 | 
			
		||||
func TestNonFatalErrors(t *testing.T) {
 | 
			
		||||
	nfe := NonFatalErrors{}
 | 
			
		||||
 | 
			
		||||
	nfe.AddError(errors.New("one"))
 | 
			
		||||
	nfe.AddError(errors.New("two"))
 | 
			
		||||
	nfe.AddError(errors.New("three"))
 | 
			
		||||
 | 
			
		||||
	if !nfe.HasError() {
 | 
			
		||||
		t.Fatal("NonFatalError claimed not to have an error")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !strings.Contains(nfe.Error(), "one; two; three") {
 | 
			
		||||
		t.Fatalf("Didn't see expected string from Error(), got '%s'", nfe.Error())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// END CT CHANGES
 | 
			
		||||
 | 
			
		||||
const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQU5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7jbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LDQzvWNRlD6v9BydCZBcNMTAwMzAxMDk0NjIyWjAAMCECDkmNfeclaFhIaaUHJ0JlFw0xMDAzMDEwOTQ2MDVaMAAwIQIOT/qWWfpH/m8NTwcnQpQXDTEwMDUxMTA5MTgyMVowADAhAg5m/ksYxvCEgJSvBydClRcNMTAwNTExMDkxODAxWjAAMCECDgvf3Ohq6JOPU9AHJ0KWFw0xMDA1MTEwOTIxMjNaMAAwIQIOKSPas10z4jNVIQcnQpcXDTEwMDUxMTA5MjEwMlowADAhAg4mCWmhoZ3lyKCDBydCohcNMTEwNDI4MTEwMjI1WjAAMCECDkeiyRsBMK0Gvr4HJ0KjFw0xMTA0MjgxMTAyMDdaMAAwIQIOa09b/nH2+55SSwcnQq4XDTExMDQwMTA4Mjk0NlowADAhAg5O7M7iq7gGplr1BydCrxcNMTEwNDAxMDgzMDE3WjAAMCECDjlT6mJxUjTvyogHJ0K1Fw0xMTAxMjcxNTQ4NTJaMAAwIQIODS/l4UUFLe21NAcnQrYXDTExMDEyNzE1NDgyOFowADAhAg5lPRA0XdOUF6lSBydDHhcNMTEwMTI4MTQzNTA1WjAAMCECDixKX4fFGGpENwgHJ0MfFw0xMTAxMjgxNDM1MzBaMAAwIQIORNBkqsPnpKTtbAcnQ08XDTEwMDkwOTA4NDg0MlowADAhAg5QL+EMM3lohedEBydDUBcNMTAwOTA5MDg0ODE5WjAAMCECDlhDnHK+HiTRAXcHJ0NUFw0xMDEwMTkxNjIxNDBaMAAwIQIOdBFqAzq/INz53gcnQ1UXDTEwMTAxOTE2MjA0NFowADAhAg4OjR7s8MgKles1BydDWhcNMTEwMTI3MTY1MzM2WjAAMCECDmfR/elHee+d0SoHJ0NbFw0xMTAxMjcxNjUzNTZaMAAwIQIOBTKv2ui+KFMI+wcnQ5YXDTEwMDkxNTEwMjE1N1owADAhAg49F3c/GSah+oRUBydDmxcNMTEwMTI3MTczMjMzWjAAMCECDggv4I61WwpKFMMHJ0OcFw0xMTAxMjcxNzMyNTVaMAAwIQIOXx/Y8sEvwS10LAcnQ6UXDTExMDEyODExMjkzN1owADAhAg5LSLbnVrSKaw/9BydDphcNMTEwMTI4MTEyOTIwWjAAMCECDmFFoCuhKUeACQQHJ0PfFw0xMTAxMTExMDE3MzdaMAAwIQIOQTDdFh2fSPF6AAcnQ+AXDTExMDExMTEwMTcxMFowADAhAg5B8AOXX61FpvbbBydD5RcNMTAxMDA2MTAxNDM2WjAAMCECDh41P2Gmi7PkwI4HJ0PmFw0xMDEwMDYxMDE2MjVaMAAwIQIOWUHGLQCd+Ale9gcnQ/0XDTExMDUwMjA3NTYxMFowADAhAg5Z2c9AYkikmgWOBydD/hcNMTEwNTAyMDc1NjM0WjAAMCECDmf/UD+/h8nf+74HJ0QVFw0xMTA0MTUwNzI4MzNaMAAwIQIOICvj4epy3MrqfwcnRBYXDTExMDQxNTA3Mjg1NlowADAhAg4bouRMfOYqgv4xBydEHxcNMTEwMzA4MTYyNDI1WjAAMCECDhebWHGoKiTp7pEHJ0QgFw0xMTAzMDgxNjI0NDhaMAAwIQIOX+qnxxAqJ8LtawcnRDcXDTExMDEzMTE1MTIyOFowADAhAg4j0fICqZ+wkOdqBydEOBcNMTEwMTMxMTUxMTQxWjAAMCECDhmXjsV4SUpWtAMHJ0RLFw0xMTAxMjgxMTI0MTJaMAAwIQIODno/w+zG43kkTwcnREwXDTExMDEyODExMjM1MlowADAhAg4b1gc88767Fr+LBydETxcNMTEwMTI4MTEwMjA4WjAAMCECDn+M3Pa1w2nyFeUHJ0RQFw0xMTAxMjgxMDU4NDVaMAAwIQIOaduoyIH61tqybAcnRJUXDTEwMTIxNTA5NDMyMlowADAhAg4nLqQPkyi3ESAKBydElhcNMTAxMjE1MDk0MzM2WjAAMCECDi504NIMH8578gQHJ0SbFw0xMTAyMTQxNDA1NDFaMAAwIQIOGuaM8PDaC5u1egcnRJwXDTExMDIxNDE0MDYwNFowADAhAg4ehYq/BXGnB5PWBydEnxcNMTEwMjA0MDgwOTUxWjAAMCECDkSD4eS4FxW5H20HJ0SgFw0xMTAyMDQwODA5MjVaMAAwIQIOOCcb6ilYObt1egcnRKEXDTExMDEyNjEwNDEyOVowADAhAg58tISWCCwFnKGnBydEohcNMTEwMjA0MDgxMzQyWjAAMCECDn5rjtabY/L/WL0HJ0TJFw0xMTAyMDQxMTAzNDFaMAAwDQYJKoZIhvcNAQEFBQADggEBAGnF2Gs0+LNiYCW1Ipm83OXQYP/bd5tFFRzyz3iepFqNfYs4D68/QihjFoRHQoXEB0OEe1tvaVnnPGnEOpi6krwekquMxo4H88B5SlyiFIqemCOIss0SxlCFs69LmfRYvPPvPEhoXtQ3ZThe0UvKG83GOklhvGl6OaiRf4Mt+m8zOT4Wox/j6aOBK6cw6qKCdmD+Yj1rrNqFGg1CnSWMoD6S6mwNgkzwdBUJZ22BwrzAAo4RHa2Uy3ef1FjwD0XtU5N3uDSxGGBEDvOe5z82rps3E22FpAA8eYl8kaXtmWqyvYU0epp4brGuTxCuBMCAsxt/OjIjeNNQbBGkwxgfYA0="
 | 
			
		||||
 | 
			
		||||
const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K"
 | 
			
		||||
@ -8,8 +8,6 @@ import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/asn1"
 | 
			
		||||
	"crypto/x509/pkix"
 | 
			
		||||
	//"github.com/google/certificate-transparency/go/asn1"
 | 
			
		||||
	//"github.com/google/certificate-transparency/go/x509/pkix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								helpers.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								helpers.go
									
									
									
									
									
								
							@ -17,9 +17,9 @@ import (
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/certificate-transparency/go"
 | 
			
		||||
	"github.com/google/certificate-transparency/go/x509"
 | 
			
		||||
	"github.com/google/certificate-transparency/go/x509/pkix"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/x509"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/x509/pkix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ReadSTHFile (path string) (*ct.SignedTreeHead, error) {
 | 
			
		||||
@ -59,13 +59,7 @@ func EntryDNSNames (entry *ct.LogEntry) ([]string, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParseEntryCertificate (entry *ct.LogEntry) (*x509.Certificate, error) {
 | 
			
		||||
	if entry.Precert != nil {
 | 
			
		||||
		// already parsed
 | 
			
		||||
		return &entry.Precert.TBSCertificate, nil
 | 
			
		||||
	} else if entry.X509Cert != nil {
 | 
			
		||||
		// already parsed
 | 
			
		||||
		return entry.X509Cert, nil
 | 
			
		||||
	} else if entry.Leaf.TimestampedEntry.EntryType == ct.PrecertLogEntryType {
 | 
			
		||||
	if entry.Leaf.TimestampedEntry.EntryType == ct.PrecertLogEntryType {
 | 
			
		||||
		return x509.ParseTBSCertificate(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate)
 | 
			
		||||
	} else if entry.Leaf.TimestampedEntry.EntryType == ct.X509LogEntryType {
 | 
			
		||||
		return x509.ParseCertificate(entry.Leaf.TimestampedEntry.X509Entry)
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,8 @@ import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/certificate-transparency/go"
 | 
			
		||||
	"github.com/google/certificate-transparency/go/client"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct"
 | 
			
		||||
	"src.agwa.name/ctwatch/ct/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProcessCallback func(*Scanner, *ct.LogEntry)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user