From a071e9490aa918367c7bf01570157de363d1409a Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Wed, 16 Mar 2016 16:58:00 -0700 Subject: [PATCH] Replace embedded X509 parser with my own lightweight parser --- asn1.go | 65 ++ cmd/common.go | 2 +- cmd/ctwatch/main.go | 18 +- ct/asn1/asn1.go | 956 -------------------- ct/asn1/asn1_test.go | 790 ----------------- ct/asn1/common.go | 163 ---- ct/asn1/marshal.go | 581 ------------- ct/asn1/marshal_test.go | 148 ---- ct/x509/cert_pool.go | 116 --- ct/x509/pem_decrypt.go | 233 ----- ct/x509/pem_decrypt_test.go | 223 ----- ct/x509/pkcs1.go | 124 --- ct/x509/pkcs8.go | 56 -- ct/x509/pkcs8_test.go | 28 - ct/x509/pkix/pkix.go | 173 ---- ct/x509/root.go | 17 - ct/x509/root_darwin.go | 83 -- ct/x509/root_plan9.go | 33 - ct/x509/root_stub.go | 14 - ct/x509/root_unix.go | 37 - ct/x509/root_windows.go | 229 ----- ct/x509/sec1.go | 85 -- ct/x509/sec1_test.go | 30 - ct/x509/verify.go | 476 ---------- ct/x509/verify_test.go | 975 --------------------- ct/x509/x509.go | 1644 ----------------------------------- ct/x509/x509_test.go | 751 ---------------- dnsnames.go | 198 ----- helpers.go | 336 +++---- x509.go | 300 +++++++ 30 files changed, 548 insertions(+), 8336 deletions(-) create mode 100644 asn1.go delete mode 100644 ct/asn1/asn1.go delete mode 100644 ct/asn1/asn1_test.go delete mode 100644 ct/asn1/common.go delete mode 100644 ct/asn1/marshal.go delete mode 100644 ct/asn1/marshal_test.go delete mode 100644 ct/x509/cert_pool.go delete mode 100644 ct/x509/pem_decrypt.go delete mode 100644 ct/x509/pem_decrypt_test.go delete mode 100644 ct/x509/pkcs1.go delete mode 100644 ct/x509/pkcs8.go delete mode 100644 ct/x509/pkcs8_test.go delete mode 100644 ct/x509/pkix/pkix.go delete mode 100644 ct/x509/root.go delete mode 100644 ct/x509/root_darwin.go delete mode 100644 ct/x509/root_plan9.go delete mode 100644 ct/x509/root_stub.go delete mode 100644 ct/x509/root_unix.go delete mode 100644 ct/x509/root_windows.go delete mode 100644 ct/x509/sec1.go delete mode 100644 ct/x509/sec1_test.go delete mode 100644 ct/x509/verify.go delete mode 100644 ct/x509/verify_test.go delete mode 100644 ct/x509/x509.go delete mode 100644 ct/x509/x509_test.go delete mode 100644 dnsnames.go create mode 100644 x509.go diff --git a/asn1.go b/asn1.go new file mode 100644 index 0000000..69fac2b --- /dev/null +++ b/asn1.go @@ -0,0 +1,65 @@ +package ctwatch + +import ( + "errors" + "bytes" + "encoding/binary" + "encoding/asn1" +) + +func stringFromByteSlice (chars []byte) string { + runes := make([]rune, len(chars)) + for i, ch := range chars { + runes[i] = rune(ch) + } + return string(runes) +} + +func stringFromUint16Slice (chars []uint16) string { + runes := make([]rune, len(chars)) + for i, ch := range chars { + runes[i] = rune(ch) + } + return string(runes) +} + +func stringFromUint32Slice (chars []uint32) string { + runes := make([]rune, len(chars)) + for i, ch := range chars { + runes[i] = rune(ch) + } + return string(runes) +} + +func decodeASN1String (value *asn1.RawValue) (string, error) { + if !value.IsCompound && value.Class == 0 { + if value.Tag == 12 { + // UTF8String + return string(value.Bytes), nil + } else if value.Tag == 19 || value.Tag == 22 || value.Tag == 20 { + // * PrintableString - subset of ASCII + // * IA5String - ASCII + // * TeletexString - 8 bit charset; not quite ISO-8859-1, but often treated as such + + // Don't enforce character set rules. Allow any 8 bit character, since + // CAs routinely mess this up + return stringFromByteSlice(value.Bytes), nil + } else if value.Tag == 30 { + // BMPString - Unicode, encoded in big-endian format using two octets + runes := make([]uint16, len(value.Bytes) / 2) + if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil { + return "", errors.New("Malformed BMPString: " + err.Error()) + } + return stringFromUint16Slice(runes), nil + } else if value.Tag == 28 { + // UniversalString - Unicode, encoded in big-endian format using four octets + runes := make([]uint32, len(value.Bytes) / 4) + if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil { + return "", errors.New("Malformed UniversalString: " + err.Error()) + } + return stringFromUint32Slice(runes), nil + } + } + return "", errors.New("Not a string") +} + diff --git a/cmd/common.go b/cmd/common.go index ed5b178..a8729d9 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -59,7 +59,7 @@ func LogEntry (info *ctwatch.EntryInfo) { if !*noSave { var alreadyPresent bool var err error - alreadyPresent, info.Filename, err = ctwatch.WriteCertRepository(filepath.Join(stateDir, "certs"), info.Entry) + alreadyPresent, info.Filename, err = ctwatch.WriteCertRepository(filepath.Join(stateDir, "certs"), info.IsPrecert, info.FullChain) if err != nil { log.Print(err) } diff --git a/cmd/ctwatch/main.go b/cmd/ctwatch/main.go index eca2e4f..d2cce30 100644 --- a/cmd/ctwatch/main.go +++ b/cmd/ctwatch/main.go @@ -65,25 +65,17 @@ func processEntry (scanner *ctwatch.Scanner, entry *ct.LogEntry) { info := ctwatch.EntryInfo{ LogUri: scanner.LogUri, Entry: entry, + IsPrecert: ctwatch.IsPrecert(entry), + FullChain: ctwatch.GetFullChain(entry), } - // Extract DNS names - var dnsNames []string - dnsNames, info.ParseError = ctwatch.EntryDNSNames(entry) + info.CertInfo, info.ParseError = ctwatch.MakeCertInfo(entry) - if info.ParseError == nil { + if info.ParseError == nil && info.CertInfo.DNSNamesParseError == nil { // Match DNS names - if !anyDnsNameMatches(dnsNames) { + if !anyDnsNameMatches(info.CertInfo.DNSNames) { return } - - // Parse the certificate - info.ParsedCert, info.ParseError = ctwatch.ParseEntryCertificate(entry) - if info.ParsedCert != nil { - info.CertInfo = ctwatch.MakeCertInfo(info.ParsedCert) - } else { - info.CertInfo.DnsNames = dnsNames - } } cmd.LogEntry(&info) diff --git a/ct/asn1/asn1.go b/ct/asn1/asn1.go deleted file mode 100644 index e987477..0000000 --- a/ct/asn1/asn1.go +++ /dev/null @@ -1,956 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the 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< 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 -} diff --git a/ct/asn1/asn1_test.go b/ct/asn1/asn1_test.go deleted file mode 100644 index 4401f35..0000000 --- a/ct/asn1/asn1_test.go +++ /dev/null @@ -1,790 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the 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, -} diff --git a/ct/asn1/common.go b/ct/asn1/common.go deleted file mode 100644 index 33a117e..0000000 --- a/ct/asn1/common.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the 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 -} diff --git a/ct/asn1/marshal.go b/ct/asn1/marshal.go deleted file mode 100644 index ed17e41..0000000 --- a/ct/asn1/marshal.go +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the 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 -} diff --git a/ct/asn1/marshal_test.go b/ct/asn1/marshal_test.go deleted file mode 100644 index 763c86d..0000000 --- a/ct/asn1/marshal_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the 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") - } -} diff --git a/ct/x509/cert_pool.go b/ct/x509/cert_pool.go deleted file mode 100644 index babe94d..0000000 --- a/ct/x509/cert_pool.go +++ /dev/null @@ -1,116 +0,0 @@ -// 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 -} diff --git a/ct/x509/pem_decrypt.go b/ct/x509/pem_decrypt.go deleted file mode 100644 index 194c81b..0000000 --- a/ct/x509/pem_decrypt.go +++ /dev/null @@ -1,233 +0,0 @@ -// 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 -} diff --git a/ct/x509/pem_decrypt_test.go b/ct/x509/pem_decrypt_test.go deleted file mode 100644 index 59ba6f9..0000000 --- a/ct/x509/pem_decrypt_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// 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=`, - }, -} diff --git a/ct/x509/pkcs1.go b/ct/x509/pkcs1.go deleted file mode 100644 index 16d3c91..0000000 --- a/ct/x509/pkcs1.go +++ /dev/null @@ -1,124 +0,0 @@ -// 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 -} diff --git a/ct/x509/pkcs8.go b/ct/x509/pkcs8.go deleted file mode 100644 index 4466d9e..0000000 --- a/ct/x509/pkcs8.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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) - } -} diff --git a/ct/x509/pkcs8_test.go b/ct/x509/pkcs8_test.go deleted file mode 100644 index 4114efd..0000000 --- a/ct/x509/pkcs8_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// 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) - } -} diff --git a/ct/x509/pkix/pkix.go b/ct/x509/pkix/pkix.go deleted file mode 100644 index e0be1b6..0000000 --- a/ct/x509/pkix/pkix.go +++ /dev/null @@ -1,173 +0,0 @@ -// 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"` -} diff --git a/ct/x509/root.go b/ct/x509/root.go deleted file mode 100644 index 8aae14e..0000000 --- a/ct/x509/root.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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 -} diff --git a/ct/x509/root_darwin.go b/ct/x509/root_darwin.go deleted file mode 100644 index 3d3fa4e..0000000 --- a/ct/x509/root_darwin.go +++ /dev/null @@ -1,83 +0,0 @@ -// 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 -#include - -// 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 -} diff --git a/ct/x509/root_plan9.go b/ct/x509/root_plan9.go deleted file mode 100644 index 9965caa..0000000 --- a/ct/x509/root_plan9.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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. -} diff --git a/ct/x509/root_stub.go b/ct/x509/root_stub.go deleted file mode 100644 index 4c742cc..0000000 --- a/ct/x509/root_stub.go +++ /dev/null @@ -1,14 +0,0 @@ -// 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() { -} diff --git a/ct/x509/root_unix.go b/ct/x509/root_unix.go deleted file mode 100644 index 324f855..0000000 --- a/ct/x509/root_unix.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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. -} diff --git a/ct/x509/root_windows.go b/ct/x509/root_windows.go deleted file mode 100644 index 81018b7..0000000 --- a/ct/x509/root_windows.go +++ /dev/null @@ -1,229 +0,0 @@ -// 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() { -} diff --git a/ct/x509/sec1.go b/ct/x509/sec1.go deleted file mode 100644 index 1bc8efa..0000000 --- a/ct/x509/sec1.go +++ /dev/null @@ -1,85 +0,0 @@ -// 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 -} diff --git a/ct/x509/sec1_test.go b/ct/x509/sec1_test.go deleted file mode 100644 index 95f18e7..0000000 --- a/ct/x509/sec1_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// 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) - } -} diff --git a/ct/x509/verify.go b/ct/x509/verify.go deleted file mode 100644 index e82dbc1..0000000 --- a/ct/x509/verify.go +++ /dev/null @@ -1,476 +0,0 @@ -// 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 -} diff --git a/ct/x509/verify_test.go b/ct/x509/verify_test.go deleted file mode 100644 index df7ef32..0000000 --- a/ct/x509/verify_test.go +++ /dev/null @@ -1,975 +0,0 @@ -// 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-----` diff --git a/ct/x509/x509.go b/ct/x509/x509.go deleted file mode 100644 index 5356778..0000000 --- a/ct/x509/x509.go +++ /dev/null @@ -1,1644 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package x509 parses X.509-encoded keys and certificates. -// -// START CT CHANGES -// This is a fork of the go library crypto/x509 package, it's more relaxed -// about certificates that it'll accept, and exports the TBSCertificate -// structure. -// END CT CHANGES -package x509 - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "crypto/sha1" - // START CT CHANGES - "src.agwa.name/ctwatch/ct/asn1" - "src.agwa.name/ctwatch/ct/x509/pkix" - // END CT CHANGES - "encoding/pem" - "errors" - // START CT CHANGES - "fmt" - // END CT CHANGES - "io" - "math/big" - "net" - "time" -) - -// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo -// in RFC 3280. -type pkixPublicKey struct { - Algo pkix.AlgorithmIdentifier - BitString asn1.BitString -} - -// ParsePKIXPublicKey parses a DER encoded public key. These values are -// typically found in PEM blocks with "BEGIN PUBLIC KEY". -func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { - var pki publicKeyInfo - if _, err = asn1.Unmarshal(derBytes, &pki); err != nil { - return - } - algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) - if algo == UnknownPublicKeyAlgorithm { - return nil, errors.New("x509: unknown public key algorithm") - } - return parsePublicKey(algo, &pki) -} - -func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) { - switch pub := pub.(type) { - case *rsa.PublicKey: - publicKeyBytes, err = asn1.Marshal(rsaPublicKey{ - N: pub.N, - E: pub.E, - }) - publicKeyAlgorithm.Algorithm = oidPublicKeyRSA - // This is a NULL parameters value which is technically - // superfluous, but most other code includes it and, by - // doing this, we match their public key hashes. - publicKeyAlgorithm.Parameters = asn1.RawValue{ - Tag: 5, - } - case *ecdsa.PublicKey: - publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) - oid, ok := oidFromNamedCurve(pub.Curve) - if !ok { - return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve") - } - publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA - var paramBytes []byte - paramBytes, err = asn1.Marshal(oid) - if err != nil { - return - } - publicKeyAlgorithm.Parameters.FullBytes = paramBytes - default: - return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported") - } - - return publicKeyBytes, publicKeyAlgorithm, nil -} - -// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. -func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { - var publicKeyBytes []byte - var publicKeyAlgorithm pkix.AlgorithmIdentifier - var err error - - if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { - return nil, err - } - - pkix := pkixPublicKey{ - Algo: publicKeyAlgorithm, - BitString: asn1.BitString{ - Bytes: publicKeyBytes, - BitLength: 8 * len(publicKeyBytes), - }, - } - - ret, _ := asn1.Marshal(pkix) - return ret, nil -} - -// These structures reflect the ASN.1 structure of X.509 certificates.: - -type certificate struct { - Raw asn1.RawContent - TBSCertificate tbsCertificate - SignatureAlgorithm pkix.AlgorithmIdentifier - SignatureValue asn1.BitString -} - -type tbsCertificate struct { - Raw asn1.RawContent - Version int `asn1:"optional,explicit,default:1,tag:0"` - SerialNumber *big.Int - SignatureAlgorithm pkix.AlgorithmIdentifier - Issuer asn1.RawValue - Validity validity - Subject asn1.RawValue - PublicKey publicKeyInfo - UniqueId asn1.BitString `asn1:"optional,tag:1"` - SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` - Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` -} - -type dsaAlgorithmParameters struct { - P, Q, G *big.Int -} - -type dsaSignature struct { - R, S *big.Int -} - -type ecdsaSignature dsaSignature - -type validity struct { - NotBefore, NotAfter time.Time -} - -type publicKeyInfo struct { - Raw asn1.RawContent - Algorithm pkix.AlgorithmIdentifier - PublicKey asn1.BitString -} - -// RFC 5280, 4.2.1.1 -type authKeyId struct { - Id []byte `asn1:"optional,tag:0"` -} - -type SignatureAlgorithm int - -const ( - UnknownSignatureAlgorithm SignatureAlgorithm = iota - MD2WithRSA - MD5WithRSA - SHA1WithRSA - SHA256WithRSA - SHA384WithRSA - SHA512WithRSA - DSAWithSHA1 - DSAWithSHA256 - ECDSAWithSHA1 - ECDSAWithSHA256 - ECDSAWithSHA384 - ECDSAWithSHA512 -) - -type PublicKeyAlgorithm int - -const ( - UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota - RSA - DSA - ECDSA -) - -// OIDs for signature algorithms -// -// pkcs-1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } -// -// -// RFC 3279 2.2.1 RSA Signature Algorithms -// -// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } -// -// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } -// -// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } -// -// dsaWithSha1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } -// -// RFC 3279 2.2.3 ECDSA Signature Algorithm -// -// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-x962(10045) -// signatures(4) ecdsa-with-SHA1(1)} -// -// -// RFC 4055 5 PKCS #1 Version 1.5 -// -// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } -// -// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } -// -// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } -// -// -// RFC 5758 3.1 DSA Signature Algorithms -// -// dsaWithSha256 OBJECT IDENTIFIER ::= { -// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) -// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} -// -// RFC 5758 3.2 ECDSA Signature Algorithm -// -// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } -// -// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 } -// -// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 } - -var ( - oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} - oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} - oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} - oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} - oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} - oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} - oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} - oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} - oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} - oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} - oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} - oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} -) - -func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { - switch { - case oid.Equal(oidSignatureMD2WithRSA): - return MD2WithRSA - case oid.Equal(oidSignatureMD5WithRSA): - return MD5WithRSA - case oid.Equal(oidSignatureSHA1WithRSA): - return SHA1WithRSA - case oid.Equal(oidSignatureSHA256WithRSA): - return SHA256WithRSA - case oid.Equal(oidSignatureSHA384WithRSA): - return SHA384WithRSA - case oid.Equal(oidSignatureSHA512WithRSA): - return SHA512WithRSA - case oid.Equal(oidSignatureDSAWithSHA1): - return DSAWithSHA1 - case oid.Equal(oidSignatureDSAWithSHA256): - return DSAWithSHA256 - case oid.Equal(oidSignatureECDSAWithSHA1): - return ECDSAWithSHA1 - case oid.Equal(oidSignatureECDSAWithSHA256): - return ECDSAWithSHA256 - case oid.Equal(oidSignatureECDSAWithSHA384): - return ECDSAWithSHA384 - case oid.Equal(oidSignatureECDSAWithSHA512): - return ECDSAWithSHA512 - } - return UnknownSignatureAlgorithm -} - -// RFC 3279, 2.3 Public Key Algorithms -// -// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// rsadsi(113549) pkcs(1) 1 } -// -// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } -// -// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) -// x9-57(10040) x9cm(4) 1 } -// -// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters -// -// id-ecPublicKey OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } -var ( - oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} - oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} - oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} -) - -func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { - switch { - case oid.Equal(oidPublicKeyRSA): - return RSA - case oid.Equal(oidPublicKeyDSA): - return DSA - case oid.Equal(oidPublicKeyECDSA): - return ECDSA - } - return UnknownPublicKeyAlgorithm -} - -// RFC 5480, 2.1.1.1. Named Curve -// -// secp224r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 33 } -// -// secp256r1 OBJECT IDENTIFIER ::= { -// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) -// prime(1) 7 } -// -// secp384r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 34 } -// -// secp521r1 OBJECT IDENTIFIER ::= { -// iso(1) identified-organization(3) certicom(132) curve(0) 35 } -// -// NB: secp256r1 is equivalent to prime256v1 -var ( - oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} - oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} - oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} - oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} -) - -func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve { - switch { - case oid.Equal(oidNamedCurveP224): - return elliptic.P224() - case oid.Equal(oidNamedCurveP256): - return elliptic.P256() - case oid.Equal(oidNamedCurveP384): - return elliptic.P384() - case oid.Equal(oidNamedCurveP521): - return elliptic.P521() - } - return nil -} - -func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { - switch curve { - case elliptic.P224(): - return oidNamedCurveP224, true - case elliptic.P256(): - return oidNamedCurveP256, true - case elliptic.P384(): - return oidNamedCurveP384, true - case elliptic.P521(): - return oidNamedCurveP521, true - } - - return nil, false -} - -// KeyUsage represents the set of actions that are valid for a given key. It's -// a bitmap of the KeyUsage* constants. -type KeyUsage int - -const ( - KeyUsageDigitalSignature KeyUsage = 1 << iota - KeyUsageContentCommitment - KeyUsageKeyEncipherment - KeyUsageDataEncipherment - KeyUsageKeyAgreement - KeyUsageCertSign - KeyUsageCRLSign - KeyUsageEncipherOnly - KeyUsageDecipherOnly -) - -// RFC 5280, 4.2.1.12 Extended Key Usage -// -// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } -// -// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } -// -// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } -// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } -// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } -// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } -// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } -// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } -var ( - oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} - oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} - oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} - oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} - oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} - oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5} - oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6} - oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7} - oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} - oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} - oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3} - oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1} -) - -// ExtKeyUsage represents an extended set of actions that are valid for a given key. -// Each of the ExtKeyUsage* constants define a unique action. -type ExtKeyUsage int - -const ( - ExtKeyUsageAny ExtKeyUsage = iota - ExtKeyUsageServerAuth - ExtKeyUsageClientAuth - ExtKeyUsageCodeSigning - ExtKeyUsageEmailProtection - ExtKeyUsageIPSECEndSystem - ExtKeyUsageIPSECTunnel - ExtKeyUsageIPSECUser - ExtKeyUsageTimeStamping - ExtKeyUsageOCSPSigning - ExtKeyUsageMicrosoftServerGatedCrypto - ExtKeyUsageNetscapeServerGatedCrypto -) - -// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID. -var extKeyUsageOIDs = []struct { - extKeyUsage ExtKeyUsage - oid asn1.ObjectIdentifier -}{ - {ExtKeyUsageAny, oidExtKeyUsageAny}, - {ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth}, - {ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth}, - {ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning}, - {ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection}, - {ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem}, - {ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel}, - {ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser}, - {ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping}, - {ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning}, - {ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto}, - {ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto}, -} - -func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) { - for _, pair := range extKeyUsageOIDs { - if oid.Equal(pair.oid) { - return pair.extKeyUsage, true - } - } - return -} - -func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) { - for _, pair := range extKeyUsageOIDs { - if eku == pair.extKeyUsage { - return pair.oid, true - } - } - return -} - -// A Certificate represents an X.509 certificate. -type Certificate struct { - Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). - RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. - RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. - RawSubject []byte // DER encoded Subject - RawIssuer []byte // DER encoded Issuer - - Signature []byte - SignatureAlgorithm SignatureAlgorithm - - PublicKeyAlgorithm PublicKeyAlgorithm - PublicKey interface{} - - Version int - SerialNumber *big.Int - Issuer pkix.Name - Subject pkix.Name - NotBefore, NotAfter time.Time // Validity bounds. - KeyUsage KeyUsage - - // Extensions contains raw X.509 extensions. When parsing certificates, - // this can be used to extract non-critical extensions that are not - // parsed by this package. When marshaling certificates, the Extensions - // field is ignored, see ExtraExtensions. - Extensions []pkix.Extension - - // ExtraExtensions contains extensions to be copied, raw, into any - // marshaled certificates. Values override any extensions that would - // otherwise be produced based on the other fields. The ExtraExtensions - // field is not populated when parsing certificates, see Extensions. - ExtraExtensions []pkix.Extension - - ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. - UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. - - BasicConstraintsValid bool // if true then the next two fields are valid. - IsCA bool - MaxPathLen int - - SubjectKeyId []byte - AuthorityKeyId []byte - - // RFC 5280, 4.2.2.1 (Authority Information Access) - OCSPServer []string - IssuingCertificateURL []string - - // Subject Alternate Name values - DNSNames []string - EmailAddresses []string - IPAddresses []net.IP - - // Name constraints - PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. - PermittedDNSDomains []string - - // CRL Distribution Points - CRLDistributionPoints []string - - PolicyIdentifiers []asn1.ObjectIdentifier -} - -// ErrUnsupportedAlgorithm results from attempting to perform an operation that -// involves algorithms that are not currently implemented. -var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented") - -// ConstraintViolationError results when a requested usage is not permitted by -// a certificate. For example: checking a signature when the public key isn't a -// certificate signing key. -type ConstraintViolationError struct{} - -func (ConstraintViolationError) Error() string { - return "x509: invalid signature: parent certificate cannot sign this kind of certificate" -} - -func (c *Certificate) Equal(other *Certificate) bool { - return bytes.Equal(c.Raw, other.Raw) -} - -// Entrust have a broken root certificate (CN=Entrust.net Certification -// Authority (2048)) which isn't marked as a CA certificate and is thus invalid -// according to PKIX. -// We recognise this certificate by its SubjectPublicKeyInfo and exempt it -// from the Basic Constraints requirement. -// See http://www.entrust.net/knowledge-base/technote.cfm?tn=7869 -// -// TODO(agl): remove this hack once their reissued root is sufficiently -// widespread. -var entrustBrokenSPKI = []byte{ - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, - 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, - 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, - 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde, 0x05, - 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, - 0x7f, 0xc7, 0x4b, 0x7e, 0x5a, 0x9f, 0xb3, 0xff, - 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba, 0x10, - 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, - 0x28, 0xce, 0xc0, 0xe6, 0x0e, 0x06, 0x91, 0x50, - 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb, 0xd8, - 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, - 0x96, 0xdc, 0xbc, 0xaa, 0xfa, 0x52, 0x77, 0x04, - 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd, 0x3c, - 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, - 0xf9, 0xc1, 0xb1, 0x3f, 0x72, 0x86, 0xf2, 0x38, - 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28, 0xda, - 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, - 0xc1, 0x65, 0x77, 0x76, 0x24, 0x4c, 0x98, 0xf7, - 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb, 0x37, - 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, - 0x20, 0x09, 0x49, 0x36, 0x24, 0x69, 0x42, 0xf6, - 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9, 0x3c, - 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, - 0xd7, 0xf7, 0x0a, 0x6f, 0xef, 0x2e, 0xd8, 0xd5, - 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68, 0xe2, - 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, - 0x51, 0x43, 0x9d, 0xe0, 0xb2, 0xc4, 0x67, 0xb4, - 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f, 0x4b, - 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, - 0x6c, 0x78, 0x90, 0x95, 0xde, 0xca, 0x3a, 0x48, - 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75, 0x05, - 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, - 0xe4, 0x1a, 0x15, 0xdc, 0x87, 0x23, 0xaa, 0xb2, - 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8, 0x3d, - 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, - 0x55, 0x02, 0x03, 0x01, 0x00, 0x01, -} - -// CheckSignatureFrom verifies that the signature on c is a valid signature -// from parent. -func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { - // RFC 5280, 4.2.1.9: - // "If the basic constraints extension is not present in a version 3 - // certificate, or the extension is present but the cA boolean is not - // asserted, then the certified public key MUST NOT be used to verify - // certificate signatures." - // (except for Entrust, see comment above entrustBrokenSPKI) - if (parent.Version == 3 && !parent.BasicConstraintsValid || - parent.BasicConstraintsValid && !parent.IsCA) && - !bytes.Equal(c.RawSubjectPublicKeyInfo, entrustBrokenSPKI) { - return ConstraintViolationError{} - } - - if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCertSign == 0 { - return ConstraintViolationError{} - } - - if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { - return ErrUnsupportedAlgorithm - } - - // TODO(agl): don't ignore the path length constraint. - - return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature) -} - -// CheckSignature verifies that signature is a valid signature over signed from -// c's public key. -func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) { - var hashType crypto.Hash - - switch algo { - case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1: - hashType = crypto.SHA1 - case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256: - hashType = crypto.SHA256 - case SHA384WithRSA, ECDSAWithSHA384: - hashType = crypto.SHA384 - case SHA512WithRSA, ECDSAWithSHA512: - hashType = crypto.SHA512 - default: - return ErrUnsupportedAlgorithm - } - - if !hashType.Available() { - return ErrUnsupportedAlgorithm - } - h := hashType.New() - - h.Write(signed) - digest := h.Sum(nil) - - switch pub := c.PublicKey.(type) { - case *rsa.PublicKey: - return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) - case *dsa.PublicKey: - dsaSig := new(dsaSignature) - if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { - return err - } - if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return errors.New("x509: DSA signature contained zero or negative values") - } - if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return errors.New("x509: DSA verification failure") - } - return - case *ecdsa.PublicKey: - ecdsaSig := new(ecdsaSignature) - if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { - return err - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("x509: ECDSA signature contained zero or negative values") - } - if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("x509: ECDSA verification failure") - } - return - } - return ErrUnsupportedAlgorithm -} - -// CheckCRLSignature checks that the signature in crl is from c. -func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) { - algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) - return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) -} - -// START CT CHANGES -type UnhandledCriticalExtension struct { - ID asn1.ObjectIdentifier -} - -func (h UnhandledCriticalExtension) Error() string { - return fmt.Sprintf("x509: unhandled critical extension (%v)", h.ID) -} - -// END CT CHANGES - -type basicConstraints struct { - IsCA bool `asn1:"optional"` - MaxPathLen int `asn1:"optional,default:-1"` -} - -// RFC 5280 4.2.1.4 -type policyInformation struct { - Policy asn1.ObjectIdentifier - // policyQualifiers omitted -} - -// RFC 5280, 4.2.1.10 -type nameConstraints struct { - Permitted []generalSubtree `asn1:"optional,tag:0"` - Excluded []generalSubtree `asn1:"optional,tag:1"` -} - -type generalSubtree struct { - Name string `asn1:"tag:2,optional,ia5"` -} - -// RFC 5280, 4.2.2.1 -type authorityInfoAccess struct { - Method asn1.ObjectIdentifier - Location asn1.RawValue -} - -// RFC 5280, 4.2.1.14 -type distributionPoint struct { - DistributionPoint distributionPointName `asn1:"optional,tag:0"` - Reason asn1.BitString `asn1:"optional,tag:1"` - CRLIssuer asn1.RawValue `asn1:"optional,tag:2"` -} - -type distributionPointName struct { - FullName asn1.RawValue `asn1:"optional,tag:0"` - RelativeName pkix.RDNSequence `asn1:"optional,tag:1"` -} - -func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { - // START CT CHANGES - var nfe NonFatalErrors - // END CT CHANGES - asn1Data := keyData.PublicKey.RightAlign() - switch algo { - case RSA: - p := new(rsaPublicKey) - _, err := asn1.Unmarshal(asn1Data, p) - if err != nil { - return nil, err - } - - if p.N.Sign() <= 0 { - // START CT CHANGES - nfe.AddError(errors.New("x509: RSA modulus is not a positive number")) - // END CT CHANGES - } - if p.E <= 0 { - return nil, errors.New("x509: RSA public exponent is not a positive number") - } - - pub := &rsa.PublicKey{ - E: p.E, - N: p.N, - } - // START CT CHANGES - if nfe.HasError() { - return pub, nfe - } - // END CT CHANGES - return pub, nil - case DSA: - var p *big.Int - _, err := asn1.Unmarshal(asn1Data, &p) - if err != nil { - return nil, err - } - paramsData := keyData.Algorithm.Parameters.FullBytes - params := new(dsaAlgorithmParameters) - _, err = asn1.Unmarshal(paramsData, params) - if err != nil { - return nil, err - } - if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { - return nil, errors.New("x509: zero or negative DSA parameter") - } - pub := &dsa.PublicKey{ - Parameters: dsa.Parameters{ - P: params.P, - Q: params.Q, - G: params.G, - }, - Y: p, - } - return pub, nil - case ECDSA: - paramsData := keyData.Algorithm.Parameters.FullBytes - namedCurveOID := new(asn1.ObjectIdentifier) - _, err := asn1.Unmarshal(paramsData, namedCurveOID) - if err != nil { - return nil, err - } - namedCurve := namedCurveFromOID(*namedCurveOID) - if namedCurve == nil { - return nil, errors.New("x509: unsupported elliptic curve") - } - x, y := elliptic.Unmarshal(namedCurve, asn1Data) - if x == nil { - return nil, errors.New("x509: failed to unmarshal elliptic curve point") - } - pub := &ecdsa.PublicKey{ - Curve: namedCurve, - X: x, - Y: y, - } - return pub, nil - default: - return nil, nil - } -} - -// START CT CHANGES - -// NonFatalErrors is an error type which can hold a number of other errors. -// It's used to collect a range of non-fatal errors which occur while parsing -// a certificate, that way we can still match on certs which technically are -// invalid. -type NonFatalErrors struct { - Errors []error -} - -// Adds an error to the list of errors contained by NonFatalErrors. -func (e *NonFatalErrors) AddError(err error) { - e.Errors = append(e.Errors, err) -} - -// Attempts to add errors from a second NonFatalErrors to this NonFatalErrors. -// If the passed error is not a NonFatalErrors, return false. -func (e *NonFatalErrors) AddNonFatalErrors(err error) bool { - nfe, isNfe := err.(NonFatalErrors) - if isNfe { - e.Errors = append(e.Errors, nfe.Errors...) - } - return isNfe -} - -// Returns a string consisting of the values of Error() from all of the errors -// contained in |e| -func (e NonFatalErrors) Error() string { - r := "NonFatalErrors: " - for _, err := range e.Errors { - r += err.Error() + "; " - } - return r -} - -// Returns true if |e| contains at least one error -func (e *NonFatalErrors) HasError() bool { - return len(e.Errors) > 0 -} - -// END CT CHANGES - -func parseCertificate(in *certificate) (*Certificate, error) { - // START CT CHANGES - var nfe NonFatalErrors - // END CT CHANGES - - out := new(Certificate) - out.Raw = in.Raw - out.RawTBSCertificate = in.TBSCertificate.Raw - out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw - out.RawSubject = in.TBSCertificate.Subject.FullBytes - out.RawIssuer = in.TBSCertificate.Issuer.FullBytes - - out.Signature = in.SignatureValue.RightAlign() - out.SignatureAlgorithm = - getSignatureAlgorithmFromOID(in.TBSCertificate.SignatureAlgorithm.Algorithm) - - out.PublicKeyAlgorithm = - getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm) - var err error - out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey) - // START CT CHANGES - if err != nil && !nfe.AddNonFatalErrors(err) { - // START CT CHANGES - return nil, err - } - - if in.TBSCertificate.SerialNumber.Sign() < 0 { - // START CT CHANGES - nfe.AddError(errors.New("x509: negative serial number")) - // END CT CHANGES - } - - out.Version = in.TBSCertificate.Version + 1 - out.SerialNumber = in.TBSCertificate.SerialNumber - - var issuer, subject pkix.RDNSequence - if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil { - return nil, err - } - if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil { - return nil, err - } - - out.Issuer.FillFromRDNSequence(&issuer) - out.Subject.FillFromRDNSequence(&subject) - - out.NotBefore = in.TBSCertificate.Validity.NotBefore - out.NotAfter = in.TBSCertificate.Validity.NotAfter - - for _, e := range in.TBSCertificate.Extensions { - out.Extensions = append(out.Extensions, e) - - if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 { - switch e.Id[3] { - case 15: - // RFC 5280, 4.2.1.3 - var usageBits asn1.BitString - _, err := asn1.Unmarshal(e.Value, &usageBits) - - if err == nil { - var usage int - for i := 0; i < 9; i++ { - if usageBits.At(i) != 0 { - usage |= 1 << uint(i) - } - } - out.KeyUsage = KeyUsage(usage) - continue - } - case 19: - // RFC 5280, 4.2.1.9 - var constraints basicConstraints - _, err := asn1.Unmarshal(e.Value, &constraints) - - if err == nil { - out.BasicConstraintsValid = true - out.IsCA = constraints.IsCA - out.MaxPathLen = constraints.MaxPathLen - continue - } - case 17: - // RFC 5280, 4.2.1.6 - - // SubjectAltName ::= GeneralNames - // - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - // - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - var seq asn1.RawValue - _, err := asn1.Unmarshal(e.Value, &seq) - if err != nil { - return nil, err - } - if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { - return nil, asn1.StructuralError{Msg: "bad SAN sequence"} - } - - parsedName := false - - rest := seq.Bytes - for len(rest) > 0 { - var v asn1.RawValue - rest, err = asn1.Unmarshal(rest, &v) - if err != nil { - return nil, err - } - switch v.Tag { - case 1: - out.EmailAddresses = append(out.EmailAddresses, string(v.Bytes)) - parsedName = true - case 2: - out.DNSNames = append(out.DNSNames, string(v.Bytes)) - parsedName = true - case 7: - switch len(v.Bytes) { - case net.IPv4len, net.IPv6len: - out.IPAddresses = append(out.IPAddresses, v.Bytes) - default: - // START CT CHANGES - nfe.AddError(fmt.Errorf("x509: certificate contained IP address of length %d : %v", len(v.Bytes), v.Bytes)) - // END CT CHANGES - } - } - } - - if parsedName { - continue - } - // If we didn't parse any of the names then we - // fall through to the critical check below. - - case 30: - // RFC 5280, 4.2.1.10 - - // NameConstraints ::= SEQUENCE { - // permittedSubtrees [0] GeneralSubtrees OPTIONAL, - // excludedSubtrees [1] GeneralSubtrees OPTIONAL } - // - // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree - // - // GeneralSubtree ::= SEQUENCE { - // base GeneralName, - // minimum [0] BaseDistance DEFAULT 0, - // maximum [1] BaseDistance OPTIONAL } - // - // BaseDistance ::= INTEGER (0..MAX) - - var constraints nameConstraints - _, err := asn1.Unmarshal(e.Value, &constraints) - if err != nil { - return nil, err - } - - if len(constraints.Excluded) > 0 && e.Critical { - // START CT CHANGES - nfe.AddError(UnhandledCriticalExtension{e.Id}) - // END CT CHANGES - } - - for _, subtree := range constraints.Permitted { - if len(subtree.Name) == 0 { - if e.Critical { - // START CT CHANGES - nfe.AddError(UnhandledCriticalExtension{e.Id}) - // END CT CHANGES - } - continue - } - out.PermittedDNSDomains = append(out.PermittedDNSDomains, subtree.Name) - } - continue - - case 31: - // RFC 5280, 4.2.1.14 - - // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint - // - // DistributionPoint ::= SEQUENCE { - // distributionPoint [0] DistributionPointName OPTIONAL, - // reasons [1] ReasonFlags OPTIONAL, - // cRLIssuer [2] GeneralNames OPTIONAL } - // - // DistributionPointName ::= CHOICE { - // fullName [0] GeneralNames, - // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - - var cdp []distributionPoint - _, err := asn1.Unmarshal(e.Value, &cdp) - if err != nil { - return nil, err - } - - for _, dp := range cdp { - var n asn1.RawValue - _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n) - if err != nil { - return nil, err - } - - if n.Tag == 6 { - out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes)) - } - } - continue - - case 35: - // RFC 5280, 4.2.1.1 - var a authKeyId - _, err = asn1.Unmarshal(e.Value, &a) - if err != nil { - return nil, err - } - out.AuthorityKeyId = a.Id - continue - - case 37: - // RFC 5280, 4.2.1.12. Extended Key Usage - - // id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } - // - // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId - // - // KeyPurposeId ::= OBJECT IDENTIFIER - - var keyUsage []asn1.ObjectIdentifier - _, err = asn1.Unmarshal(e.Value, &keyUsage) - if err != nil { - return nil, err - } - - for _, u := range keyUsage { - if extKeyUsage, ok := extKeyUsageFromOID(u); ok { - out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage) - } else { - out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u) - } - } - - continue - - case 14: - // RFC 5280, 4.2.1.2 - var keyid []byte - _, err = asn1.Unmarshal(e.Value, &keyid) - if err != nil { - return nil, err - } - out.SubjectKeyId = keyid - continue - - case 32: - // RFC 5280 4.2.1.4: Certificate Policies - var policies []policyInformation - if _, err = asn1.Unmarshal(e.Value, &policies); err != nil { - return nil, err - } - out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies)) - for i, policy := range policies { - out.PolicyIdentifiers[i] = policy.Policy - } - } - } else if e.Id.Equal(oidExtensionAuthorityInfoAccess) { - // RFC 5280 4.2.2.1: Authority Information Access - var aia []authorityInfoAccess - if _, err = asn1.Unmarshal(e.Value, &aia); err != nil { - return nil, err - } - - for _, v := range aia { - // GeneralName: uniformResourceIdentifier [6] IA5String - if v.Location.Tag != 6 { - continue - } - if v.Method.Equal(oidAuthorityInfoAccessOcsp) { - out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes)) - } else if v.Method.Equal(oidAuthorityInfoAccessIssuers) { - out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes)) - } - } - } - - if e.Critical { - // START CT CHANGES - nfe.AddError(UnhandledCriticalExtension{e.Id}) - // END CT CHANGES - } - } - // START CT CHANGES - if nfe.HasError() { - return out, nfe - } - // END CT CHANGES - return out, nil -} - -// START CT CHANGES - -// ParseTBSCertificate parses a single TBSCertificate from the given ASN.1 DER data. -// The parsed data is returned in a Certificate struct for ease of access. -func ParseTBSCertificate(asn1Data []byte) (*Certificate, error) { - var tbsCert tbsCertificate - rest, err := asn1.Unmarshal(asn1Data, &tbsCert) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, asn1.SyntaxError{Msg: "trailing data"} - } - return parseCertificate(&certificate{ - Raw: tbsCert.Raw, - TBSCertificate: tbsCert}) -} - -// END CT CHANGES - -// ParseCertificate parses a single certificate from the given ASN.1 DER data. -func ParseCertificate(asn1Data []byte) (*Certificate, error) { - var cert certificate - rest, err := asn1.Unmarshal(asn1Data, &cert) - if err != nil { - return nil, err - } - if len(rest) > 0 { - return nil, asn1.SyntaxError{Msg: "trailing data"} - } - - return parseCertificate(&cert) -} - -// ParseCertificates parses one or more certificates from the given ASN.1 DER -// data. The certificates must be concatenated with no intermediate padding. -func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { - var v []*certificate - - for len(asn1Data) > 0 { - cert := new(certificate) - var err error - asn1Data, err = asn1.Unmarshal(asn1Data, cert) - if err != nil { - return nil, err - } - v = append(v, cert) - } - - ret := make([]*Certificate, len(v)) - for i, ci := range v { - cert, err := parseCertificate(ci) - if err != nil { - return nil, err - } - ret[i] = cert - } - - return ret, nil -} - -func reverseBitsInAByte(in byte) byte { - b1 := in>>4 | in<<4 - b2 := b1>>2&0x33 | b1<<2&0xcc - b3 := b2>>1&0x55 | b2<<1&0xaa - return b3 -} - -var ( - oidExtensionSubjectKeyId = []int{2, 5, 29, 14} - oidExtensionKeyUsage = []int{2, 5, 29, 15} - oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37} - oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} - oidExtensionBasicConstraints = []int{2, 5, 29, 19} - oidExtensionSubjectAltName = []int{2, 5, 29, 17} - oidExtensionCertificatePolicies = []int{2, 5, 29, 32} - oidExtensionNameConstraints = []int{2, 5, 29, 30} - oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31} - oidExtensionAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1} -) - -var ( - oidAuthorityInfoAccessOcsp = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1} - oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2} -) - -// oidNotInExtensions returns whether an extension with the given oid exists in -// extensions. -func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool { - for _, e := range extensions { - if e.Id.Equal(oid) { - return true - } - } - return false -} - -func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { - ret = make([]pkix.Extension, 10 /* maximum number of elements. */) - n := 0 - - if template.KeyUsage != 0 && - !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) { - ret[n].Id = oidExtensionKeyUsage - ret[n].Critical = true - - var a [2]byte - a[0] = reverseBitsInAByte(byte(template.KeyUsage)) - a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8)) - - l := 1 - if a[1] != 0 { - l = 2 - } - - ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) - if err != nil { - return - } - n++ - } - - if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) && - !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) { - ret[n].Id = oidExtensionExtendedKeyUsage - - var oids []asn1.ObjectIdentifier - for _, u := range template.ExtKeyUsage { - if oid, ok := oidFromExtKeyUsage(u); ok { - oids = append(oids, oid) - } else { - panic("internal error") - } - } - - oids = append(oids, template.UnknownExtKeyUsage...) - - ret[n].Value, err = asn1.Marshal(oids) - if err != nil { - return - } - n++ - } - - if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) { - ret[n].Id = oidExtensionBasicConstraints - ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) - ret[n].Critical = true - if err != nil { - return - } - n++ - } - - if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) { - ret[n].Id = oidExtensionSubjectKeyId - ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) - if err != nil { - return - } - n++ - } - - if len(template.AuthorityKeyId) > 0 && !oidInExtensions(oidExtensionAuthorityKeyId, template.ExtraExtensions) { - ret[n].Id = oidExtensionAuthorityKeyId - ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId}) - if err != nil { - return - } - n++ - } - - if (len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0) && - !oidInExtensions(oidExtensionAuthorityInfoAccess, template.ExtraExtensions) { - ret[n].Id = oidExtensionAuthorityInfoAccess - var aiaValues []authorityInfoAccess - for _, name := range template.OCSPServer { - aiaValues = append(aiaValues, authorityInfoAccess{ - Method: oidAuthorityInfoAccessOcsp, - Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}, - }) - } - for _, name := range template.IssuingCertificateURL { - aiaValues = append(aiaValues, authorityInfoAccess{ - Method: oidAuthorityInfoAccessIssuers, - Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}, - }) - } - ret[n].Value, err = asn1.Marshal(aiaValues) - if err != nil { - return - } - n++ - } - - if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0) && - !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) { - ret[n].Id = oidExtensionSubjectAltName - var rawValues []asn1.RawValue - for _, name := range template.DNSNames { - rawValues = append(rawValues, asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)}) - } - for _, email := range template.EmailAddresses { - rawValues = append(rawValues, asn1.RawValue{Tag: 1, Class: 2, Bytes: []byte(email)}) - } - for _, rawIP := range template.IPAddresses { - // If possible, we always want to encode IPv4 addresses in 4 bytes. - ip := rawIP.To4() - if ip == nil { - ip = rawIP - } - rawValues = append(rawValues, asn1.RawValue{Tag: 7, Class: 2, Bytes: ip}) - } - ret[n].Value, err = asn1.Marshal(rawValues) - if err != nil { - return - } - n++ - } - - if len(template.PolicyIdentifiers) > 0 && - !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) { - ret[n].Id = oidExtensionCertificatePolicies - policies := make([]policyInformation, len(template.PolicyIdentifiers)) - for i, policy := range template.PolicyIdentifiers { - policies[i].Policy = policy - } - ret[n].Value, err = asn1.Marshal(policies) - if err != nil { - return - } - n++ - } - - if len(template.PermittedDNSDomains) > 0 && - !oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) { - ret[n].Id = oidExtensionNameConstraints - ret[n].Critical = template.PermittedDNSDomainsCritical - - var out nameConstraints - out.Permitted = make([]generalSubtree, len(template.PermittedDNSDomains)) - for i, permitted := range template.PermittedDNSDomains { - out.Permitted[i] = generalSubtree{Name: permitted} - } - ret[n].Value, err = asn1.Marshal(out) - if err != nil { - return - } - n++ - } - - if len(template.CRLDistributionPoints) > 0 && - !oidInExtensions(oidExtensionCRLDistributionPoints, template.ExtraExtensions) { - ret[n].Id = oidExtensionCRLDistributionPoints - - var crlDp []distributionPoint - for _, name := range template.CRLDistributionPoints { - rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}) - - dp := distributionPoint{ - DistributionPoint: distributionPointName{ - FullName: asn1.RawValue{Tag: 0, Class: 2, Bytes: rawFullName}, - }, - } - crlDp = append(crlDp, dp) - } - - ret[n].Value, err = asn1.Marshal(crlDp) - if err != nil { - return - } - n++ - } - - // Adding another extension here? Remember to update the maximum number - // of elements in the make() at the top of the function. - - return append(ret[:n], template.ExtraExtensions...), nil -} - -func subjectBytes(cert *Certificate) ([]byte, error) { - if len(cert.RawSubject) > 0 { - return cert.RawSubject, nil - } - - return asn1.Marshal(cert.Subject.ToRDNSequence()) -} - -// CreateCertificate creates a new certificate based on a template. The -// following members of template are used: SerialNumber, Subject, NotBefore, -// NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid, -// IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, -// PermittedDNSDomains. -// -// The certificate is signed by parent. If parent is equal to template then the -// certificate is self-signed. The parameter pub is the public key of the -// signee and priv is the private key of the signer. -// -// The returned slice is the certificate in DER encoding. -// -// The only supported key types are RSA and ECDSA (*rsa.PublicKey or -// *ecdsa.PublicKey for pub, *rsa.PrivateKey or *ecdsa.PublicKey for priv). -func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { - var publicKeyBytes []byte - var publicKeyAlgorithm pkix.AlgorithmIdentifier - - if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { - return nil, err - } - - var signatureAlgorithm pkix.AlgorithmIdentifier - var hashFunc crypto.Hash - - switch priv := priv.(type) { - case *rsa.PrivateKey: - signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA - hashFunc = crypto.SHA1 - case *ecdsa.PrivateKey: - switch priv.Curve { - case elliptic.P224(), elliptic.P256(): - hashFunc = crypto.SHA256 - signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256 - case elliptic.P384(): - hashFunc = crypto.SHA384 - signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384 - case elliptic.P521(): - hashFunc = crypto.SHA512 - signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512 - default: - return nil, errors.New("x509: unknown elliptic curve") - } - default: - return nil, errors.New("x509: only RSA and ECDSA private keys supported") - } - - if err != nil { - return - } - - if len(parent.SubjectKeyId) > 0 { - template.AuthorityKeyId = parent.SubjectKeyId - } - - extensions, err := buildExtensions(template) - if err != nil { - return - } - - asn1Issuer, err := subjectBytes(parent) - if err != nil { - return - } - - asn1Subject, err := subjectBytes(template) - if err != nil { - return - } - - encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes} - c := tbsCertificate{ - Version: 2, - SerialNumber: template.SerialNumber, - SignatureAlgorithm: signatureAlgorithm, - Issuer: asn1.RawValue{FullBytes: asn1Issuer}, - Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()}, - Subject: asn1.RawValue{FullBytes: asn1Subject}, - PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey}, - Extensions: extensions, - } - - tbsCertContents, err := asn1.Marshal(c) - if err != nil { - return - } - - c.Raw = tbsCertContents - - h := hashFunc.New() - h.Write(tbsCertContents) - digest := h.Sum(nil) - - var signature []byte - - switch priv := priv.(type) { - case *rsa.PrivateKey: - signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest) - case *ecdsa.PrivateKey: - var r, s *big.Int - if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil { - signature, err = asn1.Marshal(ecdsaSignature{r, s}) - } - default: - panic("internal error") - } - - if err != nil { - return - } - - cert, err = asn1.Marshal(certificate{ - nil, - c, - signatureAlgorithm, - asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, - }) - return -} - -// pemCRLPrefix is the magic string that indicates that we have a PEM encoded -// CRL. -var pemCRLPrefix = []byte("-----BEGIN X509 CRL") - -// pemType is the type of a PEM encoded CRL. -var pemType = "X509 CRL" - -// ParseCRL parses a CRL from the given bytes. It's often the case that PEM -// encoded CRLs will appear where they should be DER encoded, so this function -// will transparently handle PEM encoding as long as there isn't any leading -// garbage. -func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err error) { - if bytes.HasPrefix(crlBytes, pemCRLPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == pemType { - crlBytes = block.Bytes - } - } - return ParseDERCRL(crlBytes) -} - -// ParseDERCRL parses a DER encoded CRL from the given bytes. -func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) { - certList = new(pkix.CertificateList) - _, err = asn1.Unmarshal(derBytes, certList) - if err != nil { - certList = nil - } - return -} - -// CreateCRL returns a DER encoded CRL, signed by this Certificate, that -// contains the given list of revoked certificates. -// -// The only supported key type is RSA (*rsa.PrivateKey for priv). -func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { - rsaPriv, ok := priv.(*rsa.PrivateKey) - if !ok { - return nil, errors.New("x509: non-RSA private keys not supported") - } - tbsCertList := pkix.TBSCertificateList{ - Version: 2, - Signature: pkix.AlgorithmIdentifier{ - Algorithm: oidSignatureSHA1WithRSA, - }, - Issuer: c.Subject.ToRDNSequence(), - ThisUpdate: now.UTC(), - NextUpdate: expiry.UTC(), - RevokedCertificates: revokedCerts, - } - - tbsCertListContents, err := asn1.Marshal(tbsCertList) - if err != nil { - return - } - - h := sha1.New() - h.Write(tbsCertListContents) - digest := h.Sum(nil) - - signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) - if err != nil { - return - } - - return asn1.Marshal(pkix.CertificateList{ - TBSCertList: tbsCertList, - SignatureAlgorithm: pkix.AlgorithmIdentifier{ - Algorithm: oidSignatureSHA1WithRSA, - }, - SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, - }) -} diff --git a/ct/x509/x509_test.go b/ct/x509/x509_test.go deleted file mode 100644 index 56f251e..0000000 --- a/ct/x509/x509_test.go +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the 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" diff --git a/dnsnames.go b/dnsnames.go deleted file mode 100644 index 52fae57..0000000 --- a/dnsnames.go +++ /dev/null @@ -1,198 +0,0 @@ -package ctwatch - -import ( - "fmt" - "errors" - "bytes" - "encoding/binary" - "encoding/asn1" - "crypto/x509/pkix" -) - -var ( - oidExtensionSubjectAltName = []int{2, 5, 29, 17} - oidCommonName = []int{2, 5, 4, 3} -) - -type rdnSequence []relativeDistinguishedNameSET -type relativeDistinguishedNameSET []attributeTypeAndValue -type attributeTypeAndValue struct { - Type asn1.ObjectIdentifier - Value asn1.RawValue -} - -type tbsCertificate struct { - Version int `asn1:"optional,explicit,default:1,tag:0"` - SerialNumber asn1.RawValue - SignatureAlgorithm asn1.RawValue - Issuer asn1.RawValue - Validity asn1.RawValue - Subject asn1.RawValue - PublicKey asn1.RawValue - UniqueId asn1.BitString `asn1:"optional,tag:1"` - SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` - Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` -} - -type certificate struct { - TBSCertificate asn1.RawValue - SignatureAlgorithm asn1.RawValue - SignatureValue asn1.RawValue -} - -func stringFromByteSlice (chars []byte) string { - runes := make([]rune, len(chars)) - for i, ch := range chars { - runes[i] = rune(ch) - } - return string(runes) -} - -func stringFromUint16Slice (chars []uint16) string { - runes := make([]rune, len(chars)) - for i, ch := range chars { - runes[i] = rune(ch) - } - return string(runes) -} - -func stringFromUint32Slice (chars []uint32) string { - runes := make([]rune, len(chars)) - for i, ch := range chars { - runes[i] = rune(ch) - } - return string(runes) -} - -func decodeString (value *asn1.RawValue) (string, error) { - if !value.IsCompound && value.Class == 0 { - if value.Tag == 12 { - // UTF8String - return string(value.Bytes), nil - } else if value.Tag == 19 || value.Tag == 22 || value.Tag == 20 { - // * PrintableString - subset of ASCII - // * IA5String - ASCII - // * TeletexString - 8 bit charset; not quite ISO-8859-1, but often treated as such - - // Don't enforce character set rules. Allow any 8 bit character, since - // CAs routinely mess this up - return stringFromByteSlice(value.Bytes), nil - } else if value.Tag == 30 { - // BMPString - Unicode, encoded in big-endian format using two octets - runes := make([]uint16, len(value.Bytes) / 2) - if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil { - return "", errors.New("Malformed BMPString: " + err.Error()) - } - return stringFromUint16Slice(runes), nil - } else if value.Tag == 28 { - // UniversalString - Unicode, encoded in big-endian format using four octets - runes := make([]uint32, len(value.Bytes) / 4) - if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil { - return "", errors.New("Malformed UniversalString: " + err.Error()) - } - return stringFromUint32Slice(runes), nil - } - } - return "", errors.New("Not a string") -} - -func getCNs (rdns *rdnSequence) ([]string, error) { - var cns []string - - for _, rdn := range *rdns { - if len(rdn) == 0 { - continue - } - atv := rdn[0] - if atv.Type.Equal(oidCommonName) { - cnString, err := decodeString(&atv.Value) - if err != nil { - return nil, errors.New("Error decoding CN: " + err.Error()) - } - cns = append(cns, cnString) - } - } - - return cns, nil -} - -func parseSANExtension (value []byte) ([]string, error) { - var dnsNames []string - var seq asn1.RawValue - if rest, err := asn1.Unmarshal(value, &seq); err != nil { - return nil, errors.New("failed to parse subjectAltName extension: " + err.Error()) - } else if len(rest) != 0 { - // Don't complain if the SAN is followed by exactly one zero byte, - // which is a common error. - if !(len(rest) == 1 && rest[0] == 0) { - return nil, fmt.Errorf("trailing data in subjectAltName extension: %v", rest) - } - } - if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { - return nil, errors.New("failed to parse subjectAltName extension: bad SAN sequence") - } - - rest := seq.Bytes - for len(rest) > 0 { - var val asn1.RawValue - var err error - rest, err = asn1.Unmarshal(rest, &val) - if err != nil { - return nil, errors.New("failed to parse subjectAltName extension item: " + err.Error()) - } - switch val.Tag { - case 2: - dnsNames = append(dnsNames, string(val.Bytes)) - } - } - - return dnsNames, nil -} - -func ExtractDNSNamesFromTBS (tbsBytes []byte) ([]string, error) { - var dnsNames []string - - var tbs tbsCertificate - if rest, err := asn1.Unmarshal(tbsBytes, &tbs); err != nil { - return nil, errors.New("failed to parse TBS: " + err.Error()) - } else if len(rest) > 0 { - return nil, fmt.Errorf("trailing data after TBS: %v", rest) - } - - // Extract Common Name from Subject - var subject rdnSequence - if rest, err := asn1.Unmarshal(tbs.Subject.FullBytes, &subject); err != nil { - return nil, errors.New("failed to parse certificate subject: " + err.Error()) - } else if len(rest) != 0 { - return nil, fmt.Errorf("trailing data in certificate subject: %v", rest) - } - cns, err := getCNs(&subject) - if err != nil { - return nil, errors.New("failed to process certificate subject: " + err.Error()) - } - dnsNames = append(dnsNames, cns...) - - // Extract DNS names from SubjectAlternativeName extension - for _, ext := range tbs.Extensions { - if ext.Id.Equal(oidExtensionSubjectAltName) { - dnsSans, err := parseSANExtension(ext.Value) - if err != nil { - return nil, err - } - dnsNames = append(dnsNames, dnsSans...) - } - } - - return dnsNames, nil -} - -func ExtractDNSNames (certBytes []byte) ([]string, error) { - var cert certificate - if rest, err := asn1.Unmarshal(certBytes, &cert); err != nil { - return nil, errors.New("failed to parse certificate: " + err.Error()) - } else if len(rest) > 0 { - return nil, fmt.Errorf("trailing data after certificate: %v", rest) - } - - return ExtractDNSNamesFromTBS(cert.TBSCertificate.FullBytes) -} diff --git a/helpers.go b/helpers.go index 060c274..c7051b9 100644 --- a/helpers.go +++ b/helpers.go @@ -18,8 +18,6 @@ import ( "encoding/json" "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) { @@ -48,72 +46,24 @@ func WriteSTHFile (path string, sth *ct.SignedTreeHead) error { return ioutil.WriteFile(path, sthJson, 0666) } -func EntryDNSNames (entry *ct.LogEntry) ([]string, error) { - switch entry.Leaf.TimestampedEntry.EntryType { - case ct.X509LogEntryType: - return ExtractDNSNames(entry.Leaf.TimestampedEntry.X509Entry) - case ct.PrecertLogEntryType: - return ExtractDNSNamesFromTBS(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate) - } - panic("EntryDNSNames: entry is neither precert nor x509") +func IsPrecert (entry *ct.LogEntry) bool { + return entry.Leaf.TimestampedEntry.EntryType == ct.PrecertLogEntryType } -func ParseEntryCertificate (entry *ct.LogEntry) (*x509.Certificate, error) { - 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) - } else { - panic("ParseEntryCertificate: entry is neither precert nor x509") +func GetFullChain (entry *ct.LogEntry) [][]byte { + certs := make([][]byte, 0, len(entry.Chain) + 1) + + if entry.Leaf.TimestampedEntry.EntryType == ct.X509LogEntryType { + certs = append(certs, entry.Leaf.TimestampedEntry.X509Entry) } -} - -func appendDnArray (buf *bytes.Buffer, code string, values []string) { - for _, value := range values { - if buf.Len() != 0 { - buf.WriteString(", ") - } - buf.WriteString(code) - buf.WriteString("=") - buf.WriteString(value) - } -} - -func appendDnValue (buf *bytes.Buffer, code string, value string) { - if value != "" { - appendDnArray(buf, code, []string{value}) - } -} - -func formatDN (name pkix.Name) (string) { - // C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware - var buf bytes.Buffer - appendDnArray(&buf, "C", name.Country) - appendDnArray(&buf, "ST", name.Province) - appendDnArray(&buf, "L", name.Locality) - appendDnArray(&buf, "O", name.Organization) - appendDnArray(&buf, "OU", name.OrganizationalUnit) - appendDnValue(&buf, "CN", name.CommonName) - return buf.String() -} - -func allDNSNames (cert *x509.Certificate) []string { - dnsNames := []string{} - - if cert.Subject.CommonName != "" { - dnsNames = append(dnsNames, cert.Subject.CommonName) + for _, cert := range entry.Chain { + certs = append(certs, cert) } - for _, dnsName := range cert.DNSNames { - if dnsName != cert.Subject.CommonName { - dnsNames = append(dnsNames, dnsName) - } - } - - return dnsNames + return certs } -func formatSerial (serial *big.Int) string { +func formatSerialNumber (serial *big.Int) string { if serial != nil { return fmt.Sprintf("%x", serial) } else { @@ -126,91 +76,154 @@ func sha256hex (data []byte) string { return hex.EncodeToString(sum[:]) } -func GetRawCert (entry *ct.LogEntry) []byte { - switch entry.Leaf.TimestampedEntry.EntryType { - case ct.X509LogEntryType: - return entry.Leaf.TimestampedEntry.X509Entry - case ct.PrecertLogEntryType: - return entry.Chain[0] - } - panic("GetRawCert: entry is neither precert nor x509") -} - -func IsPrecert (entry *ct.LogEntry) bool { - switch entry.Leaf.TimestampedEntry.EntryType { - case ct.PrecertLogEntryType: - return true - case ct.X509LogEntryType: - return false - } - panic("IsPrecert: entry is neither precert nor x509") -} - type EntryInfo struct { - LogUri string - Entry *ct.LogEntry - ParsedCert *x509.Certificate - ParseError error - CertInfo CertInfo - Filename string + LogUri string + Entry *ct.LogEntry + IsPrecert bool + FullChain [][]byte // first entry is logged X509 cert or pre-cert + CertInfo *CertInfo + ParseError error // set iff CertInfo is nil + Filename string } type CertInfo struct { - DnsNames []string - SubjectDn string - IssuerDn string - Serial string - PubkeyHash string - NotBefore *time.Time - NotAfter *time.Time + TBS *TBSCertificate + + DNSNames []string + DNSNamesParseError error + Subject RDNSequence + SubjectParseError error + Issuer RDNSequence + IssuerParseError error + SerialNumber *big.Int + SerialNumberParseError error + Validity *CertValidity + ValidityParseError error + Constraints *BasicConstraints + ConstraintsParseError error } -func MakeCertInfo (cert *x509.Certificate) CertInfo { - return CertInfo { - DnsNames: allDNSNames(cert), - SubjectDn: formatDN(cert.Subject), - IssuerDn: formatDN(cert.Issuer), - Serial: formatSerial(cert.SerialNumber), - PubkeyHash: sha256hex(cert.RawSubjectPublicKeyInfo), - NotBefore: &cert.NotBefore, - NotAfter: &cert.NotAfter, +func MakeCertInfoFromTBS (tbs *TBSCertificate) *CertInfo { + info := &CertInfo{TBS: tbs} + + info.DNSNames, info.DNSNamesParseError = tbs.ParseDNSNames() + info.Subject, info.SubjectParseError = tbs.ParseSubject() + info.Issuer, info.IssuerParseError = tbs.ParseIssuer() + info.SerialNumber, info.SerialNumberParseError = tbs.ParseSerialNumber() + info.Validity, info.ValidityParseError = tbs.ParseValidity() + info.Constraints, info.ConstraintsParseError = tbs.ParseBasicConstraints() + + return info +} + +func MakeCertInfoFromRawTBS (tbsBytes []byte) (*CertInfo, error) { + tbs, err := ParseTBSCertificate(tbsBytes) + if err != nil { + return nil, err + } + return MakeCertInfoFromTBS(tbs), nil +} + +func MakeCertInfoFromRawCert (certBytes []byte) (*CertInfo, error) { + cert, err := ParseCertificate(certBytes) + if err != nil { + return nil, err + } + return MakeCertInfoFromRawTBS(cert.GetRawTBSCertificate()) +} + +func MakeCertInfo (entry *ct.LogEntry) (*CertInfo, error) { + switch entry.Leaf.TimestampedEntry.EntryType { + case ct.X509LogEntryType: + return MakeCertInfoFromRawCert(entry.Leaf.TimestampedEntry.X509Entry) + + case ct.PrecertLogEntryType: + return MakeCertInfoFromRawTBS(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate) + + default: + return nil, fmt.Errorf("MakeCertInfoFromCTEntry: unknown CT entry type (neither X509 nor precert)") } } -func (info *CertInfo) dnsNamesFriendlyString () string { - if info.DnsNames != nil { - return strings.Join(info.DnsNames, ", ") +func (info *CertInfo) dnsNamesString () string { + if info.DNSNamesParseError == nil { + return strings.Join(info.DNSNames, ", ") } else { - return "*** UNKNOWN ***" + return "" } } +func (info *CertInfo) NotBefore () *time.Time { + if info.ValidityParseError == nil { + return &info.Validity.NotBefore + } else { + return nil + } +} + +func (info *CertInfo) NotAfter () *time.Time { + if info.ValidityParseError == nil { + return &info.Validity.NotAfter + } else { + return nil + } +} + +func (info *CertInfo) PubkeyHash () string { + return sha256hex(info.TBS.GetRawPublicKey()) +} + func (info *CertInfo) Environ () []string { - var env []string - if info.DnsNames != nil { env = append(env, "DNS_NAMES=" + strings.Join(info.DnsNames, ",")) } - if info.SubjectDn != "" { env = append(env, "SUBJECT_DN=" + info.SubjectDn) } - if info.IssuerDn != "" { env = append(env, "ISSUER_DN=" + info.IssuerDn) } - if info.Serial != "" { env = append(env, "SERIAL=" + info.Serial) } - if info.PubkeyHash != "" { env = append(env, "PUBKEY_HASH=" + info.PubkeyHash) } - if info.NotBefore != nil { env = append(env, "NOT_BEFORE=" + strconv.FormatInt(info.NotBefore.Unix(), 10)) } - if info.NotAfter != nil { env = append(env, "NOT_AFTER=" + strconv.FormatInt(info.NotAfter.Unix(), 10)) } + env := make([]string, 0, 10) + + env = append(env, "PUBKEY_HASH=" + info.PubkeyHash()) + + if info.DNSNamesParseError != nil { + env = append(env, "DNS_NAMES_PARSE_ERROR=" + info.DNSNamesParseError.Error()) + } else { + env = append(env, "DNS_NAMES=" + strings.Join(info.DNSNames, ",")) + } + + if info.SerialNumberParseError != nil { + env = append(env, "SERIAL_PARSE_ERROR=" + info.SerialNumberParseError.Error()) + } else { + env = append(env, "SERIAL=" + formatSerialNumber(info.SerialNumber)) + } + + if info.ValidityParseError != nil { + env = append(env, "VALIDITY_PARSE_ERROR=" + info.ValidityParseError.Error()) + } else { + env = append(env, "NOT_BEFORE=" + info.Validity.NotBefore.String()) + env = append(env, "NOT_BEFORE_UNIXTIME=" + strconv.FormatInt(info.Validity.NotBefore.Unix(), 10)) + env = append(env, "NOT_AFTER=" + info.Validity.NotAfter.String()) + env = append(env, "NOT_AFTER_UNIXTIME=" + strconv.FormatInt(info.Validity.NotAfter.Unix(), 10)) + } + + if info.SubjectParseError != nil { + env = append(env, "SUBJECT_PARSE_ERROR=" + info.SubjectParseError.Error()) + } else { + env = append(env, "SUBJECT_DN=" + info.Subject.String()) + } + + if info.IssuerParseError != nil { + env = append(env, "ISSUER_PARSE_ERROR=" + info.IssuerParseError.Error()) + } else { + env = append(env, "ISSUER_DN=" + info.Issuer.String()) + } + return env } -func (info *EntryInfo) GetRawCert () []byte { - return GetRawCert(info.Entry) -} - func (info *EntryInfo) Fingerprint () string { - return sha256hex(info.GetRawCert()) -} - -func (info *EntryInfo) IsPrecert () bool { - return IsPrecert(info.Entry) + if len(info.FullChain) > 0 { + return sha256hex(info.FullChain[0]) + } else { + return "" + } } func (info *EntryInfo) typeString () string { - if info.IsPrecert() { + if info.IsPrecert { return "precert" } else { return "cert" @@ -218,7 +231,7 @@ func (info *EntryInfo) typeString () string { } func (info *EntryInfo) typeFriendlyString () string { - if info.IsPrecert() { + if info.IsPrecert { return "Pre-certificate" } else { return "Certificate" @@ -237,7 +250,7 @@ func (info *EntryInfo) Environ () []string { env := []string{ "FINGERPRINT=" + info.Fingerprint(), "CERT_TYPE=" + info.typeString(), - "CERT_PARSEABLE=" + yesnoString(info.ParsedCert != nil), + "CERT_PARSEABLE=" + yesnoString(info.ParseError == nil), "LOG_URI=" + info.LogUri, "ENTRY_INDEX=" + strconv.FormatInt(info.Entry.Index, 10), } @@ -245,37 +258,44 @@ func (info *EntryInfo) Environ () []string { if info.Filename != "" { env = append(env, "CERT_FILENAME=" + info.Filename) } - if info.ParseError != nil { + if info.ParseError == nil { + certEnv := info.CertInfo.Environ() + env = append(env, certEnv...) + } else { env = append(env, "PARSE_ERROR=" + info.ParseError.Error()) } - certEnv := info.CertInfo.Environ() - env = append(env, certEnv...) - return env } +func writeField (out io.Writer, name string, value interface{}, err error) { + if err == nil { + fmt.Fprintf(out, "\t%13s = %s\n", name, value) + } else { + fmt.Fprintf(out, "\t%13s = *** UNKNOWN (%s) ***\n", name, err) + } +} + func (info *EntryInfo) Write (out io.Writer) { fingerprint := info.Fingerprint() fmt.Fprintf(out, "%s:\n", fingerprint) if info.ParseError != nil { - if info.ParsedCert != nil { - fmt.Fprintf(out, "\tParse Warning = *** %s ***\n", info.ParseError) - } else { - fmt.Fprintf(out, "\t Parse Error = *** %s ***\n", info.ParseError) - } + writeField(out, "Parse Error", "*** " + info.ParseError.Error() + " ***", nil) + } else { + writeField(out, "DNS Names", info.CertInfo.dnsNamesString(), info.CertInfo.DNSNamesParseError) + writeField(out, "Pubkey", info.CertInfo.PubkeyHash(), nil) + writeField(out, "Subject", info.CertInfo.Subject, info.CertInfo.SubjectParseError) + writeField(out, "Issuer", info.CertInfo.Issuer, info.CertInfo.IssuerParseError) + writeField(out, "Serial", info.CertInfo.SerialNumber, info.CertInfo.SerialNumberParseError) + writeField(out, "Not Before", info.CertInfo.NotBefore(), info.CertInfo.ValidityParseError) + writeField(out, "Not After", info.CertInfo.NotAfter(), info.CertInfo.ValidityParseError) + } + writeField(out, "Type", info.typeFriendlyString(), nil) + writeField(out, "Log Entry", fmt.Sprintf("%d @ %s", info.Entry.Index, info.LogUri), nil) + writeField(out, "crt.sh", "https://crt.sh/?q=" + fingerprint, nil) + if info.Filename != "" { + writeField(out, "Filename", info.Filename, nil) } - fmt.Fprintf(out, "\t DNS Names = %s\n", info.CertInfo.dnsNamesFriendlyString()) - if info.CertInfo.PubkeyHash != "" { fmt.Fprintf(out, "\t Pubkey = %s\n", info.CertInfo.PubkeyHash) } - if info.CertInfo.SubjectDn != "" { fmt.Fprintf(out, "\t Subject = %s\n", info.CertInfo.SubjectDn) } - if info.CertInfo.IssuerDn != "" { fmt.Fprintf(out, "\t Issuer = %s\n", info.CertInfo.IssuerDn) } - if info.CertInfo.Serial != "" { fmt.Fprintf(out, "\t Serial = %s\n", info.CertInfo.Serial) } - if info.CertInfo.NotBefore != nil { fmt.Fprintf(out, "\t Not Before = %s\n", *info.CertInfo.NotBefore) } - if info.CertInfo.NotAfter != nil { fmt.Fprintf(out, "\t Not After = %s\n", *info.CertInfo.NotAfter) } - fmt.Fprintf(out, "\t Type = %s\n", info.typeFriendlyString()) - fmt.Fprintf(out, "\t Log Entry = %d @ %s\n", info.Entry.Index, info.LogUri) - fmt.Fprintf(out, "\t crt.sh = https://crt.sh/?q=%s\n", fingerprint) - if info.Filename != "" { fmt.Fprintf(out, "\t Filename = %s\n", info.Filename) } } func (info *EntryInfo) InvokeHookScript (command string) error { @@ -295,13 +315,17 @@ func (info *EntryInfo) InvokeHookScript (command string) error { return nil } -func WriteCertRepository (repoPath string, entry *ct.LogEntry) (bool, string, error) { - fingerprint := sha256hex(GetRawCert(entry)) +func WriteCertRepository (repoPath string, isPrecert bool, certs [][]byte) (bool, string, error) { + if len(certs) == 0 { + return false, "", fmt.Errorf("Cannot write an empty certificate chain") + } + + fingerprint := sha256hex(certs[0]) prefixPath := filepath.Join(repoPath, fingerprint[0:2]) var filenameSuffix string - if entry.Leaf.TimestampedEntry.EntryType == ct.PrecertLogEntryType { + if isPrecert { filenameSuffix = ".precert.pem" - } else if entry.Leaf.TimestampedEntry.EntryType == ct.X509LogEntryType { + } else { filenameSuffix = ".cert.pem" } if err := os.Mkdir(prefixPath, 0777); err != nil && !os.IsExist(err) { @@ -316,14 +340,8 @@ func WriteCertRepository (repoPath string, entry *ct.LogEntry) (bool, string, er return false, path, fmt.Errorf("Failed to open %s for writing: %s", path, err) } } - if entry.Leaf.TimestampedEntry.EntryType == ct.X509LogEntryType { - if err := pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: entry.Leaf.TimestampedEntry.X509Entry}); err != nil { - file.Close() - return false, path, fmt.Errorf("Error writing to %s: %s", path, err) - } - } - for _, chainCert := range entry.Chain { - if err := pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: chainCert}); err != nil { + for _, cert := range certs { + if err := pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil { file.Close() return false, path, fmt.Errorf("Error writing to %s: %s", path, err) } diff --git a/x509.go b/x509.go new file mode 100644 index 0000000..ad88537 --- /dev/null +++ b/x509.go @@ -0,0 +1,300 @@ +package ctwatch + +import ( + "fmt" + "bytes" + "errors" + "encoding/asn1" + "math/big" + "time" +) + +var ( + oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} + oidExtensionBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19} + oidCountry = asn1.ObjectIdentifier{2, 5, 4, 6} + oidOrganization = asn1.ObjectIdentifier{2, 5, 4, 10} + oidOrganizationalUnit = asn1.ObjectIdentifier{2, 5, 4, 11} + oidCommonName = asn1.ObjectIdentifier{2, 5, 4, 3} + oidSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5} + oidLocality = asn1.ObjectIdentifier{2, 5, 4, 7} + oidProvince = asn1.ObjectIdentifier{2, 5, 4, 8} + oidStreetAddress = asn1.ObjectIdentifier{2, 5, 4, 9} + oidPostalCode = asn1.ObjectIdentifier{2, 5, 4, 17} +) + +type CertValidity struct { + NotBefore time.Time + NotAfter time.Time +} + +type BasicConstraints struct { + IsCA bool `asn1:"optional"` + MaxPathLen int `asn1:"optional,default:-1"` +} + +type Extension struct { + Id asn1.ObjectIdentifier + Critical bool `asn1:"optional"` + Value []byte +} + +type RDNSequence []RelativeDistinguishedNameSET +type RelativeDistinguishedNameSET []AttributeTypeAndValue +type AttributeTypeAndValue struct { + Type asn1.ObjectIdentifier + Value asn1.RawValue +} + +type TBSCertificate struct { + Raw asn1.RawContent + + Version int `asn1:"optional,explicit,default:1,tag:0"` + SerialNumber asn1.RawValue + SignatureAlgorithm asn1.RawValue + Issuer asn1.RawValue + Validity asn1.RawValue + Subject asn1.RawValue + PublicKey asn1.RawValue + UniqueId asn1.BitString `asn1:"optional,tag:1"` + SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` + Extensions []Extension `asn1:"optional,explicit,tag:3"` +} + +type Certificate struct { + Raw asn1.RawContent + + TBSCertificate asn1.RawValue + SignatureAlgorithm asn1.RawValue + SignatureValue asn1.RawValue +} + + +func (rdns RDNSequence) ParseCNs () ([]string, error) { + var cns []string + + for _, rdn := range rdns { + if len(rdn) == 0 { + continue + } + atv := rdn[0] + if atv.Type.Equal(oidCommonName) { + cnString, err := decodeASN1String(&atv.Value) + if err != nil { + return nil, errors.New("Error decoding CN: " + err.Error()) + } + cns = append(cns, cnString) + } + } + + return cns, nil +} + +func rdnLabel (oid asn1.ObjectIdentifier) string { + switch { + case oid.Equal(oidCountry): return "C" + case oid.Equal(oidOrganization): return "O" + case oid.Equal(oidOrganizationalUnit): return "OU" + case oid.Equal(oidCommonName): return "CN" + case oid.Equal(oidSerialNumber): return "serialNumber" + case oid.Equal(oidLocality): return "L" + case oid.Equal(oidProvince): return "ST" + case oid.Equal(oidStreetAddress): return "street" + case oid.Equal(oidPostalCode): return "postalCode" + } + return oid.String() +} + +func (rdns RDNSequence) String () string { + var buf bytes.Buffer + + for _, rdn := range rdns { + if len(rdn) == 0 { + continue + } + atv := rdn[0] + + if buf.Len() != 0 { + buf.WriteString(", ") + } + buf.WriteString(rdnLabel(atv.Type)) + buf.WriteString("=") + valueString, err := decodeASN1String(&atv.Value) + if err == nil { + buf.WriteString(valueString) + } else { + fmt.Fprintf(&buf, "%v", atv.Value.FullBytes) + } + } + + return buf.String() +} + +func ParseTBSCertificate (tbsBytes []byte) (*TBSCertificate, error) { + var tbs TBSCertificate + if rest, err := asn1.Unmarshal(tbsBytes, &tbs); err != nil { + return nil, errors.New("failed to parse TBS: " + err.Error()) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after TBS: %v", rest) + } + return &tbs, nil +} + +func (tbs *TBSCertificate) ParseValidity () (*CertValidity, error) { + var validity CertValidity + if rest, err := asn1.Unmarshal(tbs.Validity.FullBytes, &validity); err != nil { + return nil, errors.New("failed to parse validity: " + err.Error()) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after validity: %v", rest) + } + return &validity, nil +} + +func (tbs *TBSCertificate) ParseBasicConstraints () (*BasicConstraints, error) { + constraintExts := tbs.GetExtension(oidExtensionBasicConstraints) + if len(constraintExts) == 0 { + return nil, nil + } else if len(constraintExts) > 1 { + return nil, fmt.Errorf("Certificate has more than one Basic Constraints extension") + } + + var constraints BasicConstraints + if rest, err := asn1.Unmarshal(constraintExts[0].Value, &constraints); err != nil { + return nil, errors.New("failed to parse Basic Constraints: " + err.Error()) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after Basic Constraints: %v", rest) + } + return &constraints, nil +} + +func (tbs *TBSCertificate) ParseSerialNumber () (*big.Int, error) { + serialNumber := big.NewInt(0) + if rest, err := asn1.Unmarshal(tbs.SerialNumber.FullBytes, &serialNumber); err != nil { + return nil, errors.New("failed to parse serial number: " + err.Error()) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after serial number: %v", rest) + } + return serialNumber, nil +} + +func (tbs *TBSCertificate) GetRawPublicKey () []byte { + return tbs.PublicKey.FullBytes +} + +func (tbs *TBSCertificate) GetRawSubject () []byte { + return tbs.Subject.FullBytes +} + +func (tbs *TBSCertificate) GetRawIssuer () []byte { + return tbs.Issuer.FullBytes +} + +func (tbs *TBSCertificate) ParseSubject () (RDNSequence, error) { + var subject RDNSequence + if rest, err := asn1.Unmarshal(tbs.GetRawSubject(), &subject); err != nil { + return nil, errors.New("failed to parse certificate subject: " + err.Error()) + } else if len(rest) != 0 { + return nil, fmt.Errorf("trailing data in certificate subject: %v", rest) + } + return subject, nil +} + +func (tbs *TBSCertificate) ParseIssuer () (RDNSequence, error) { + var issuer RDNSequence + if rest, err := asn1.Unmarshal(tbs.GetRawIssuer(), &issuer); err != nil { + return nil, errors.New("failed to parse certificate issuer: " + err.Error()) + } else if len(rest) != 0 { + return nil, fmt.Errorf("trailing data in certificate issuer: %v", rest) + } + return issuer, nil +} + +func (tbs *TBSCertificate) ParseDNSNames () ([]string, error) { + var dnsNames []string + + // Extract Common Name from Subject + subject, err := tbs.ParseSubject() + if err != nil { + return nil, err + } + cns, err := subject.ParseCNs() + if err != nil { + return nil, errors.New("failed to process certificate subject: " + err.Error()) + } + dnsNames = append(dnsNames, cns...) + + // Extract DNS names from SubjectAlternativeName extension + for _, sanExt := range tbs.GetExtension(oidExtensionSubjectAltName) { + dnsSans, err := parseSANExtension(sanExt.Value) + if err != nil { + return nil, err + } + dnsNames = append(dnsNames, dnsSans...) + } + + return dnsNames, nil +} + +func (tbs *TBSCertificate) GetExtension (id asn1.ObjectIdentifier) []Extension { + var exts []Extension + for _, ext := range tbs.Extensions { + if ext.Id.Equal(id) { + exts = append(exts, ext) + } + } + return exts +} + + +func ParseCertificate (certBytes []byte) (*Certificate, error) { + var cert Certificate + if rest, err := asn1.Unmarshal(certBytes, &cert); err != nil { + return nil, errors.New("failed to parse certificate: " + err.Error()) + } else if len(rest) > 0 { + return nil, fmt.Errorf("trailing data after certificate: %v", rest) + } + return &cert, nil +} + +func (cert *Certificate) GetRawTBSCertificate () []byte { + return cert.TBSCertificate.FullBytes +} + +func (cert *Certificate) ParseTBSCertificate () (*TBSCertificate, error) { + return ParseTBSCertificate(cert.GetRawTBSCertificate()) +} + + +func parseSANExtension (value []byte) ([]string, error) { + var dnsNames []string + var seq asn1.RawValue + if rest, err := asn1.Unmarshal(value, &seq); err != nil { + return nil, errors.New("failed to parse subjectAltName extension: " + err.Error()) + } else if len(rest) != 0 { + // Don't complain if the SAN is followed by exactly one zero byte, + // which is a common error. + if !(len(rest) == 1 && rest[0] == 0) { + return nil, fmt.Errorf("trailing data in subjectAltName extension: %v", rest) + } + } + if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { + return nil, errors.New("failed to parse subjectAltName extension: bad SAN sequence") + } + + rest := seq.Bytes + for len(rest) > 0 { + var val asn1.RawValue + var err error + rest, err = asn1.Unmarshal(rest, &val) + if err != nil { + return nil, errors.New("failed to parse subjectAltName extension item: " + err.Error()) + } + switch val.Tag { + case 2: + dnsNames = append(dnsNames, string(val.Bytes)) + } + } + + return dnsNames, nil +} +