Run gofmt

Signed-off-by: Jonathan Rudenberg <jonathan@titanous.com>
This commit is contained in:
Jonathan Rudenberg 2016-07-28 14:55:46 -04:00
parent 38b9c920eb
commit acc6781f29
16 changed files with 451 additions and 410 deletions

17
asn1.go
View File

@ -10,14 +10,14 @@
package certspotter package certspotter
import ( import (
"errors"
"bytes" "bytes"
"encoding/binary"
"encoding/asn1" "encoding/asn1"
"encoding/binary"
"errors"
"unicode/utf8" "unicode/utf8"
) )
func stringFromByteSlice (chars []byte) string { func stringFromByteSlice(chars []byte) string {
runes := make([]rune, len(chars)) runes := make([]rune, len(chars))
for i, ch := range chars { for i, ch := range chars {
runes[i] = rune(ch) runes[i] = rune(ch)
@ -25,7 +25,7 @@ func stringFromByteSlice (chars []byte) string {
return string(runes) return string(runes)
} }
func stringFromUint16Slice (chars []uint16) string { func stringFromUint16Slice(chars []uint16) string {
runes := make([]rune, len(chars)) runes := make([]rune, len(chars))
for i, ch := range chars { for i, ch := range chars {
runes[i] = rune(ch) runes[i] = rune(ch)
@ -33,7 +33,7 @@ func stringFromUint16Slice (chars []uint16) string {
return string(runes) return string(runes)
} }
func stringFromUint32Slice (chars []uint32) string { func stringFromUint32Slice(chars []uint32) string {
runes := make([]rune, len(chars)) runes := make([]rune, len(chars))
for i, ch := range chars { for i, ch := range chars {
runes[i] = rune(ch) runes[i] = rune(ch)
@ -41,7 +41,7 @@ func stringFromUint32Slice (chars []uint32) string {
return string(runes) return string(runes)
} }
func decodeASN1String (value *asn1.RawValue) (string, error) { func decodeASN1String(value *asn1.RawValue) (string, error) {
if !value.IsCompound && value.Class == 0 { if !value.IsCompound && value.Class == 0 {
if value.Tag == 12 { if value.Tag == 12 {
// UTF8String // UTF8String
@ -59,14 +59,14 @@ func decodeASN1String (value *asn1.RawValue) (string, error) {
return stringFromByteSlice(value.Bytes), nil return stringFromByteSlice(value.Bytes), nil
} else if value.Tag == 30 { } else if value.Tag == 30 {
// BMPString - Unicode, encoded in big-endian format using two octets // BMPString - Unicode, encoded in big-endian format using two octets
runes := make([]uint16, len(value.Bytes) / 2) runes := make([]uint16, len(value.Bytes)/2)
if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil { if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil {
return "", errors.New("Malformed BMPString: " + err.Error()) return "", errors.New("Malformed BMPString: " + err.Error())
} }
return stringFromUint16Slice(runes), nil return stringFromUint16Slice(runes), nil
} else if value.Tag == 28 { } else if value.Tag == 28 {
// UniversalString - Unicode, encoded in big-endian format using four octets // UniversalString - Unicode, encoded in big-endian format using four octets
runes := make([]uint32, len(value.Bytes) / 4) runes := make([]uint32, len(value.Bytes)/4)
if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil { if err := binary.Read(bytes.NewReader(value.Bytes), binary.BigEndian, runes); err != nil {
return "", errors.New("Malformed UniversalString: " + err.Error()) return "", errors.New("Malformed UniversalString: " + err.Error())
} }
@ -75,4 +75,3 @@ func decodeASN1String (value *asn1.RawValue) (string, error) {
} }
return "", errors.New("Not a string") return "", errors.New("Not a string")
} }

View File

@ -10,22 +10,22 @@
package certspotter package certspotter
import ( import (
"time"
"strconv"
"errors"
"unicode"
"encoding/asn1" "encoding/asn1"
"errors"
"strconv"
"time"
"unicode"
) )
func isDigit (b byte) bool { func isDigit(b byte) bool {
return unicode.IsDigit(rune(b)) return unicode.IsDigit(rune(b))
} }
func bytesToInt (bytes []byte) (int, error) { func bytesToInt(bytes []byte) (int, error) {
return strconv.Atoi(string(bytes)) return strconv.Atoi(string(bytes))
} }
func parseUTCTime (bytes []byte) (time.Time, error) { func parseUTCTime(bytes []byte) (time.Time, error) {
var err error var err error
var year, month, day int var year, month, day int
var hour, min, sec int var hour, min, sec int
@ -36,19 +36,29 @@ func parseUTCTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("UTCTime is too short") return time.Time{}, errors.New("UTCTime is too short")
} }
year, err = bytesToInt(bytes[0:2]) year, err = bytesToInt(bytes[0:2])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
month, err = bytesToInt(bytes[2:4]) month, err = bytesToInt(bytes[2:4])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
day, err = bytesToInt(bytes[4:6]) day, err = bytesToInt(bytes[4:6])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
hour, err = bytesToInt(bytes[6:8]) hour, err = bytesToInt(bytes[6:8])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
min, err = bytesToInt(bytes[8:10]) min, err = bytesToInt(bytes[8:10])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
bytes = bytes[10:] bytes = bytes[10:]
@ -72,12 +82,16 @@ func parseUTCTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("UTCTime positive timezone offset is too short") return time.Time{}, errors.New("UTCTime positive timezone offset is too short")
} }
tzHour, err := bytesToInt(bytes[1:3]) tzHour, err := bytesToInt(bytes[1:3])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
tzMin, err := bytesToInt(bytes[3:5]) tzMin, err := bytesToInt(bytes[3:5])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
tz = time.FixedZone("", tzHour * 3600 + tzMin * 60) tz = time.FixedZone("", tzHour*3600+tzMin*60)
bytes = bytes[5:] bytes = bytes[5:]
} else if bytes[0] == '-' { } else if bytes[0] == '-' {
// -hhmm // -hhmm
@ -85,12 +99,16 @@ func parseUTCTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("UTCTime negative timezone offset is too short") return time.Time{}, errors.New("UTCTime negative timezone offset is too short")
} }
tzHour, err := bytesToInt(bytes[1:3]) tzHour, err := bytesToInt(bytes[1:3])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
tzMin, err := bytesToInt(bytes[3:5]) tzMin, err := bytesToInt(bytes[3:5])
if err != nil { return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("UTCTime contains invalid integer: " + err.Error())
}
tz = time.FixedZone("", -1 * (tzHour * 3600 + tzMin * 60)) tz = time.FixedZone("", -1*(tzHour*3600+tzMin*60))
bytes = bytes[5:] bytes = bytes[5:]
} }
} else { } else {
@ -111,7 +129,7 @@ func parseUTCTime (bytes []byte) (time.Time, error) {
return time.Date(year, time.Month(month), day, hour, min, sec, 0, tz), nil return time.Date(year, time.Month(month), day, hour, min, sec, 0, tz), nil
} }
func parseGeneralizedTime (bytes []byte) (time.Time, error) { func parseGeneralizedTime(bytes []byte) (time.Time, error) {
var err error var err error
var year, month, day int var year, month, day int
var hour, min, sec, ms int var hour, min, sec, ms int
@ -122,16 +140,24 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("GeneralizedTime is too short") return time.Time{}, errors.New("GeneralizedTime is too short")
} }
year, err = bytesToInt(bytes[0:4]) year, err = bytesToInt(bytes[0:4])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
month, err = bytesToInt(bytes[4:6]) month, err = bytesToInt(bytes[4:6])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
day, err = bytesToInt(bytes[6:8]) day, err = bytesToInt(bytes[6:8])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
hour, err = bytesToInt(bytes[8:10]) hour, err = bytesToInt(bytes[8:10])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
bytes = bytes[10:] bytes = bytes[10:]
@ -174,12 +200,16 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("GeneralizedTime positive timezone offset is too short") return time.Time{}, errors.New("GeneralizedTime positive timezone offset is too short")
} }
tzHour, err := bytesToInt(bytes[1:3]) tzHour, err := bytesToInt(bytes[1:3])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
tzMin, err := bytesToInt(bytes[3:5]) tzMin, err := bytesToInt(bytes[3:5])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
tz = time.FixedZone("", tzHour * 3600 + tzMin * 60) tz = time.FixedZone("", tzHour*3600+tzMin*60)
bytes = bytes[5:] bytes = bytes[5:]
} else if bytes[0] == '-' { } else if bytes[0] == '-' {
// -hhmm // -hhmm
@ -187,12 +217,16 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("GeneralizedTime negative timezone offset is too short") return time.Time{}, errors.New("GeneralizedTime negative timezone offset is too short")
} }
tzHour, err := bytesToInt(bytes[1:3]) tzHour, err := bytesToInt(bytes[1:3])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
tzMin, err := bytesToInt(bytes[3:5]) tzMin, err := bytesToInt(bytes[3:5])
if err != nil { return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error()) } if err != nil {
return time.Time{}, errors.New("GeneralizedTime contains invalid integer: " + err.Error())
}
tz = time.FixedZone("", -1 * (tzHour * 3600 + tzMin * 60)) tz = time.FixedZone("", -1*(tzHour*3600+tzMin*60))
bytes = bytes[5:] bytes = bytes[5:]
} }
} else { } else {
@ -203,10 +237,10 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
return time.Time{}, errors.New("GeneralizedTime has trailing garbage") return time.Time{}, errors.New("GeneralizedTime has trailing garbage")
} }
return time.Date(year, time.Month(month), day, hour, min, sec, ms * 1000 * 1000, tz), nil return time.Date(year, time.Month(month), day, hour, min, sec, ms*1000*1000, tz), nil
} }
func decodeASN1Time (value *asn1.RawValue) (time.Time, error) { func decodeASN1Time(value *asn1.RawValue) (time.Time, error) {
if !value.IsCompound && value.Class == 0 { if !value.IsCompound && value.Class == 0 {
if value.Tag == asn1.TagUTCTime { if value.Tag == asn1.TagUTCTime {
return parseUTCTime(value.Bytes) return parseUTCTime(value.Bytes)

View File

@ -15,31 +15,31 @@ import (
) )
type timeTest struct { type timeTest struct {
in string in string
ok bool ok bool
out time.Time out time.Time
} }
var utcTimeTests = []timeTest{ var utcTimeTests = []timeTest{
{ "9502101525Z", true, time.Date(1995, time.February, 10, 15, 25, 0, 0, time.UTC) }, {"9502101525Z", true, time.Date(1995, time.February, 10, 15, 25, 0, 0, time.UTC)},
{ "950210152542Z", true, time.Date(1995, time.February, 10, 15, 25, 42, 0, time.UTC) }, {"950210152542Z", true, time.Date(1995, time.February, 10, 15, 25, 42, 0, time.UTC)},
{ "1502101525Z", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC) }, {"1502101525Z", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC)},
{ "150210152542Z", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC) }, {"150210152542Z", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC)},
{ "1502101525+1000", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", 10 * 3600)) }, {"1502101525+1000", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", 10*3600))},
{ "1502101525-1000", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", -1 * (10 * 3600))) }, {"1502101525-1000", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", -1*(10*3600)))},
{ "1502101525+1035", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", 10 * 3600 + 35 * 60)) }, {"1502101525+1035", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", 10*3600+35*60))},
{ "1502101525-1035", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", -1 * (10 * 3600 + 35 * 60))) }, {"1502101525-1035", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", -1*(10*3600+35*60)))},
{ "150210152542+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", 10 * 3600)) }, {"150210152542+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", 10*3600))},
{ "150210152542-1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", -1 * (10 * 3600))) }, {"150210152542-1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", -1*(10*3600)))},
{ "150210152542+1035", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", 10 * 3600 + 35 * 60)) }, {"150210152542+1035", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", 10*3600+35*60))},
{ "150210152542-1035", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", -1 * (10 * 3600 + 35 * 60))) }, {"150210152542-1035", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", -1*(10*3600+35*60)))},
{ "1502101525", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC) }, {"1502101525", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC)},
{ "150210152542", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC) }, {"150210152542", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC)},
{ "", false, time.Time{} }, {"", false, time.Time{}},
{ "123", false, time.Time{} }, {"123", false, time.Time{}},
{ "150210152542-10", false, time.Time{} }, {"150210152542-10", false, time.Time{}},
{ "150210152542F", false, time.Time{} }, {"150210152542F", false, time.Time{}},
{ "150210152542ZF", false, time.Time{} }, {"150210152542ZF", false, time.Time{}},
} }
func TestUTCTime(t *testing.T) { func TestUTCTime(t *testing.T) {
@ -62,44 +62,43 @@ func TestUTCTime(t *testing.T) {
} }
var generalizedTimeTests = []timeTest{ var generalizedTimeTests = []timeTest{
{ "2015021015", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.UTC) }, {"2015021015", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.UTC)},
{ "201502101525", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC) }, {"201502101525", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC)},
{ "20150210152542", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC) }, {"20150210152542", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC)},
{ "20150210152542.123", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.UTC) }, {"20150210152542.123", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.UTC)},
{ "20150210152542.12", false, time.Time{} }, {"20150210152542.12", false, time.Time{}},
{ "20150210152542.1", false, time.Time{} }, {"20150210152542.1", false, time.Time{}},
{ "20150210152542.", false, time.Time{} }, {"20150210152542.", false, time.Time{}},
{ "2015021015Z", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.UTC) }, {"2015021015Z", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.UTC)},
{ "201502101525Z", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC) }, {"201502101525Z", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.UTC)},
{ "20150210152542Z", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC) }, {"20150210152542Z", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.UTC)},
{ "20150210152542.123Z", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.UTC) }, {"20150210152542.123Z", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.UTC)},
{ "20150210152542.12Z", false, time.Time{} }, {"20150210152542.12Z", false, time.Time{}},
{ "20150210152542.1Z", false, time.Time{} }, {"20150210152542.1Z", false, time.Time{}},
{ "20150210152542.Z", false, time.Time{} }, {"20150210152542.Z", false, time.Time{}},
{ "2015021015+1000", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.FixedZone("", 10 * 3600)) }, {"2015021015+1000", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.FixedZone("", 10*3600))},
{ "201502101525+1000", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", 10 * 3600)) }, {"201502101525+1000", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", 10*3600))},
{ "20150210152542+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", 10 * 3600)) }, {"20150210152542+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", 10*3600))},
{ "20150210152542.123+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.FixedZone("", 10 * 3600)) }, {"20150210152542.123+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.FixedZone("", 10*3600))},
{ "20150210152542.12+1000", false, time.Time{} }, {"20150210152542.12+1000", false, time.Time{}},
{ "20150210152542.1+1000", false, time.Time{} }, {"20150210152542.1+1000", false, time.Time{}},
{ "20150210152542.+1000", false, time.Time{} }, {"20150210152542.+1000", false, time.Time{}},
{ "2015021015-0835", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.FixedZone("", -1 * (8 * 3600 + 35 * 60))) }, {"2015021015-0835", true, time.Date(2015, time.February, 10, 15, 0, 0, 0, time.FixedZone("", -1*(8*3600+35*60)))},
{ "201502101525-0835", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", -1 * (8 * 3600 + 35 * 60))) }, {"201502101525-0835", true, time.Date(2015, time.February, 10, 15, 25, 0, 0, time.FixedZone("", -1*(8*3600+35*60)))},
{ "20150210152542-0835", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", -1 * (8 * 3600 + 35 * 60))) }, {"20150210152542-0835", true, time.Date(2015, time.February, 10, 15, 25, 42, 0, time.FixedZone("", -1*(8*3600+35*60)))},
{ "20150210152542.123-0835", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.FixedZone("", -1 * (8 * 3600 + 35 * 60))) }, {"20150210152542.123-0835", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.FixedZone("", -1*(8*3600+35*60)))},
{ "20150210152542.12-0835", false, time.Time{} }, {"20150210152542.12-0835", false, time.Time{}},
{ "20150210152542.1-0835", false, time.Time{} }, {"20150210152542.1-0835", false, time.Time{}},
{ "20150210152542.-0835", false, time.Time{} }, {"20150210152542.-0835", false, time.Time{}},
{"", false, time.Time{}},
{ "", false, time.Time{} }, {"123", false, time.Time{}},
{ "123", false, time.Time{} }, {"2015021015+1000Z", false, time.Time{}},
{ "2015021015+1000Z", false, time.Time{} }, {"2015021015x", false, time.Time{}},
{ "2015021015x", false, time.Time{} }, {"201502101525Zf", false, time.Time{}},
{ "201502101525Zf", false, time.Time{} },
} }
func TestGeneralizedTime(t *testing.T) { func TestGeneralizedTime(t *testing.T) {

View File

@ -10,19 +10,19 @@
package certspotter package certspotter
import ( import (
"software.sslmate.com/src/certspotter/ct"
"bytes" "bytes"
"crypto/sha256" "crypto/sha256"
"software.sslmate.com/src/certspotter/ct"
) )
func reverseHashes (hashes []ct.MerkleTreeNode) { func reverseHashes(hashes []ct.MerkleTreeNode) {
for i := 0; i < len(hashes) / 2; i++ { for i := 0; i < len(hashes)/2; i++ {
j := len(hashes) - i - 1 j := len(hashes) - i - 1
hashes[i], hashes[j] = hashes[j], hashes[i] hashes[i], hashes[j] = hashes[j], hashes[i]
} }
} }
func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead, second *ct.SignedTreeHead) (bool, *MerkleTreeBuilder) { func VerifyConsistencyProof(proof ct.ConsistencyProof, first *ct.SignedTreeHead, second *ct.SignedTreeHead) (bool, *MerkleTreeBuilder) {
if second.TreeSize < first.TreeSize { if second.TreeSize < first.TreeSize {
// Can't be consistent if tree got smaller // Can't be consistent if tree got smaller
return false, nil return false, nil
@ -46,7 +46,7 @@ func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead
lastNode := second.TreeSize - 1 lastNode := second.TreeSize - 1
// While we're the right child, everything is in both trees, so move one level up. // While we're the right child, everything is in both trees, so move one level up.
for node % 2 == 1 { for node%2 == 1 {
node /= 2 node /= 2
lastNode /= 2 lastNode /= 2
} }
@ -68,7 +68,7 @@ func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead
leftHashes = append(leftHashes, newHash) leftHashes = append(leftHashes, newHash)
for node > 0 { for node > 0 {
if node % 2 == 1 { if node%2 == 1 {
// node is a right child; left sibling exists in both trees // node is a right child; left sibling exists in both trees
if len(proof) == 0 { if len(proof) == 0 {
return false, nil return false, nil
@ -112,14 +112,14 @@ func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead
return true, &MerkleTreeBuilder{stack: leftHashes, size: first.TreeSize} return true, &MerkleTreeBuilder{stack: leftHashes, size: first.TreeSize}
} }
func hashLeaf (leafBytes []byte) ct.MerkleTreeNode { func hashLeaf(leafBytes []byte) ct.MerkleTreeNode {
hasher := sha256.New() hasher := sha256.New()
hasher.Write([]byte{0x00}) hasher.Write([]byte{0x00})
hasher.Write(leafBytes) hasher.Write(leafBytes)
return hasher.Sum(nil) return hasher.Sum(nil)
} }
func hashChildren (left ct.MerkleTreeNode, right ct.MerkleTreeNode) ct.MerkleTreeNode { func hashChildren(left ct.MerkleTreeNode, right ct.MerkleTreeNode) ct.MerkleTreeNode {
hasher := sha256.New() hasher := sha256.New()
hasher.Write([]byte{0x01}) hasher.Write([]byte{0x01})
hasher.Write(left) hasher.Write(left)
@ -128,15 +128,15 @@ func hashChildren (left ct.MerkleTreeNode, right ct.MerkleTreeNode) ct.MerkleTre
} }
type MerkleTreeBuilder struct { type MerkleTreeBuilder struct {
stack []ct.MerkleTreeNode stack []ct.MerkleTreeNode
size uint64 // number of hashes added so far size uint64 // number of hashes added so far
} }
func (builder *MerkleTreeBuilder) Add (hash ct.MerkleTreeNode) { func (builder *MerkleTreeBuilder) Add(hash ct.MerkleTreeNode) {
builder.stack = append(builder.stack, hash) builder.stack = append(builder.stack, hash)
builder.size++ builder.size++
size := builder.size size := builder.size
for size % 2 == 0 { for size%2 == 0 {
left, right := builder.stack[len(builder.stack)-2], builder.stack[len(builder.stack)-1] left, right := builder.stack[len(builder.stack)-2], builder.stack[len(builder.stack)-1]
builder.stack = builder.stack[:len(builder.stack)-2] builder.stack = builder.stack[:len(builder.stack)-2]
builder.stack = append(builder.stack, hashChildren(left, right)) builder.stack = append(builder.stack, hashChildren(left, right))
@ -144,7 +144,7 @@ func (builder *MerkleTreeBuilder) Add (hash ct.MerkleTreeNode) {
} }
} }
func (builder *MerkleTreeBuilder) Finish () ct.MerkleTreeNode { func (builder *MerkleTreeBuilder) Finish() ct.MerkleTreeNode {
if len(builder.stack) == 0 { if len(builder.stack) == 0 {
panic("MerkleTreeBuilder.Finish called on an empty tree") panic("MerkleTreeBuilder.Finish called on an empty tree")
} }

View File

@ -10,29 +10,29 @@
package main package main
import ( import (
"bufio"
"flag" "flag"
"fmt" "fmt"
"os"
"io" "io"
"bufio" "os"
"strings"
"path/filepath" "path/filepath"
"strings"
"golang.org/x/net/idna" "golang.org/x/net/idna"
"software.sslmate.com/src/certspotter" "software.sslmate.com/src/certspotter"
"software.sslmate.com/src/certspotter/ct"
"software.sslmate.com/src/certspotter/cmd" "software.sslmate.com/src/certspotter/cmd"
"software.sslmate.com/src/certspotter/ct"
) )
func defaultStateDir () string { func defaultStateDir() string {
if envVar := os.Getenv("CERTSPOTTER_STATE_DIR"); envVar != "" { if envVar := os.Getenv("CERTSPOTTER_STATE_DIR"); envVar != "" {
return envVar return envVar
} else { } else {
return cmd.DefaultStateDir("certspotter") return cmd.DefaultStateDir("certspotter")
} }
} }
func defaultConfigDir () string { func defaultConfigDir() string {
if envVar := os.Getenv("CERTSPOTTER_CONFIG_DIR"); envVar != "" { if envVar := os.Getenv("CERTSPOTTER_CONFIG_DIR"); envVar != "" {
return envVar return envVar
} else { } else {
@ -40,9 +40,9 @@ func defaultConfigDir () string {
} }
} }
func trimTrailingDots (value string) string { func trimTrailingDots(value string) string {
length := len(value) length := len(value)
for length > 0 && value[length - 1] == '.' { for length > 0 && value[length-1] == '.' {
length-- length--
} }
return value[0:length] return value[0:length]
@ -52,15 +52,16 @@ var stateDir = flag.String("state_dir", defaultStateDir(), "Directory for storin
var watchlistFilename = flag.String("watchlist", filepath.Join(defaultConfigDir(), "watchlist"), "File containing identifiers to watch (- for stdin)") var watchlistFilename = flag.String("watchlist", filepath.Join(defaultConfigDir(), "watchlist"), "File containing identifiers to watch (- for stdin)")
type watchlistItem struct { type watchlistItem struct {
Domain []string Domain []string
AcceptSuffix bool AcceptSuffix bool
} }
var watchlist []watchlistItem var watchlist []watchlistItem
func parseWatchlistItem (str string) (watchlistItem, error) { func parseWatchlistItem(str string) (watchlistItem, error) {
if str == "." { // "." as in root zone (matches everything) if str == "." { // "." as in root zone (matches everything)
return watchlistItem{ return watchlistItem{
Domain: []string{}, Domain: []string{},
AcceptSuffix: true, AcceptSuffix: true,
}, nil }, nil
} else { } else {
@ -74,13 +75,13 @@ func parseWatchlistItem (str string) (watchlistItem, error) {
return watchlistItem{}, fmt.Errorf("Invalid domain `%s': %s", str, err) return watchlistItem{}, fmt.Errorf("Invalid domain `%s': %s", str, err)
} }
return watchlistItem{ return watchlistItem{
Domain: strings.Split(asciiDomain, "."), Domain: strings.Split(asciiDomain, "."),
AcceptSuffix: acceptSuffix, AcceptSuffix: acceptSuffix,
}, nil }, nil
} }
} }
func readWatchlist (reader io.Reader) ([]watchlistItem, error) { func readWatchlist(reader io.Reader) ([]watchlistItem, error) {
items := []watchlistItem{} items := []watchlistItem{}
scanner := bufio.NewScanner(reader) scanner := bufio.NewScanner(reader)
for scanner.Scan() { for scanner.Scan() {
@ -97,18 +98,18 @@ func readWatchlist (reader io.Reader) ([]watchlistItem, error) {
return items, scanner.Err() return items, scanner.Err()
} }
func dnsLabelMatches (certLabel string, watchLabel string) bool { func dnsLabelMatches(certLabel string, watchLabel string) bool {
// For fail-safe behavior, if a label was unparsable, it matches everything. // For fail-safe behavior, if a label was unparsable, it matches everything.
// Similarly, redacted labels match everything, since the label _might_ be // Similarly, redacted labels match everything, since the label _might_ be
// for a name we're interested in. // for a name we're interested in.
return certLabel == "*" || return certLabel == "*" ||
certLabel == "?" || certLabel == "?" ||
certLabel == certspotter.UnparsableDNSLabelPlaceholder || certLabel == certspotter.UnparsableDNSLabelPlaceholder ||
certspotter.MatchesWildcard(watchLabel, certLabel) certspotter.MatchesWildcard(watchLabel, certLabel)
} }
func dnsNameMatches (dnsName []string, watchDomain []string, acceptSuffix bool) bool { func dnsNameMatches(dnsName []string, watchDomain []string, acceptSuffix bool) bool {
for len(dnsName) > 0 && len(watchDomain) > 0 { for len(dnsName) > 0 && len(watchDomain) > 0 {
certLabel := dnsName[len(dnsName)-1] certLabel := dnsName[len(dnsName)-1]
watchLabel := watchDomain[len(watchDomain)-1] watchLabel := watchDomain[len(watchDomain)-1]
@ -124,7 +125,7 @@ func dnsNameMatches (dnsName []string, watchDomain []string, acceptSuffix bool)
return len(watchDomain) == 0 && (acceptSuffix || len(dnsName) == 0) return len(watchDomain) == 0 && (acceptSuffix || len(dnsName) == 0)
} }
func dnsNameIsWatched (dnsName string) bool { func dnsNameIsWatched(dnsName string) bool {
labels := strings.Split(dnsName, ".") labels := strings.Split(dnsName, ".")
for _, item := range watchlist { for _, item := range watchlist {
if dnsNameMatches(labels, item.Domain, item.AcceptSuffix) { if dnsNameMatches(labels, item.Domain, item.AcceptSuffix) {
@ -134,7 +135,7 @@ func dnsNameIsWatched (dnsName string) bool {
return false return false
} }
func anyDnsNameIsWatched (dnsNames []string) bool { func anyDnsNameIsWatched(dnsNames []string) bool {
for _, dnsName := range dnsNames { for _, dnsName := range dnsNames {
if dnsNameIsWatched(dnsName) { if dnsNameIsWatched(dnsName) {
return true return true
@ -143,12 +144,12 @@ func anyDnsNameIsWatched (dnsNames []string) bool {
return false return false
} }
func processEntry (scanner *certspotter.Scanner, entry *ct.LogEntry) { func processEntry(scanner *certspotter.Scanner, entry *ct.LogEntry) {
info := certspotter.EntryInfo{ info := certspotter.EntryInfo{
LogUri: scanner.LogUri, LogUri: scanner.LogUri,
Entry: entry, Entry: entry,
IsPrecert: certspotter.IsPrecert(entry), IsPrecert: certspotter.IsPrecert(entry),
FullChain: certspotter.GetFullChain(entry), FullChain: certspotter.GetFullChain(entry),
} }
info.CertInfo, info.ParseError = certspotter.MakeCertInfoFromLogEntry(entry) info.CertInfo, info.ParseError = certspotter.MakeCertInfoFromLogEntry(entry)

View File

@ -10,18 +10,18 @@
package cmd package cmd
import ( import (
"bytes"
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"log" "log"
"os" "os"
"bytes"
"os/user" "os/user"
"encoding/json"
"sync"
"strings"
"path/filepath" "path/filepath"
"time"
"strconv" "strconv"
"strings"
"sync"
"time"
"software.sslmate.com/src/certspotter" "software.sslmate.com/src/certspotter"
"software.sslmate.com/src/certspotter/ct" "software.sslmate.com/src/certspotter/ct"
@ -39,7 +39,7 @@ var stateDir string
var printMutex sync.Mutex var printMutex sync.Mutex
func homedir () string { func homedir() string {
home := os.Getenv("HOME") home := os.Getenv("HOME")
if home != "" { if home != "" {
return home return home
@ -51,15 +51,15 @@ func homedir () string {
panic("Unable to determine home directory") panic("Unable to determine home directory")
} }
func DefaultStateDir (programName string) string { func DefaultStateDir(programName string) string {
return filepath.Join(homedir(), "." + programName) return filepath.Join(homedir(), "."+programName)
} }
func DefaultConfigDir (programName string) string { func DefaultConfigDir(programName string) string {
return filepath.Join(homedir(), "." + programName) return filepath.Join(homedir(), "."+programName)
} }
func LogEntry (info *certspotter.EntryInfo) { func LogEntry(info *certspotter.EntryInfo) {
if !*noSave { if !*noSave {
var alreadyPresent bool var alreadyPresent bool
var err error var err error
@ -84,24 +84,24 @@ func LogEntry (info *certspotter.EntryInfo) {
} }
} }
func defangLogUri (logUri string) string { func defangLogUri(logUri string) string {
return strings.Replace(strings.Replace(logUri, "://", "_", 1), "/", "_", -1) return strings.Replace(strings.Replace(logUri, "://", "_", 1), "/", "_", -1)
} }
func saveEvidence (logUri string, firstSTH *ct.SignedTreeHead, secondSTH *ct.SignedTreeHead, proof ct.ConsistencyProof) (string, string, string, error) { func saveEvidence(logUri string, firstSTH *ct.SignedTreeHead, secondSTH *ct.SignedTreeHead, proof ct.ConsistencyProof) (string, string, string, error) {
now := strconv.FormatInt(time.Now().Unix(), 10) now := strconv.FormatInt(time.Now().Unix(), 10)
firstFilename := filepath.Join(stateDir, "evidence", defangLogUri(logUri) + ".inconsistent." + now + ".first") firstFilename := filepath.Join(stateDir, "evidence", defangLogUri(logUri)+".inconsistent."+now+".first")
if err := certspotter.WriteSTHFile(firstFilename, firstSTH); err != nil { if err := certspotter.WriteSTHFile(firstFilename, firstSTH); err != nil {
return "", "", "", err return "", "", "", err
} }
secondFilename := filepath.Join(stateDir, "evidence", defangLogUri(logUri) + ".inconsistent." + now + ".second") secondFilename := filepath.Join(stateDir, "evidence", defangLogUri(logUri)+".inconsistent."+now+".second")
if err := certspotter.WriteSTHFile(secondFilename, secondSTH); err != nil { if err := certspotter.WriteSTHFile(secondFilename, secondSTH); err != nil {
return "", "", "", err return "", "", "", err
} }
proofFilename := filepath.Join(stateDir, "evidence", defangLogUri(logUri) + ".inconsistent." + now + ".proof") proofFilename := filepath.Join(stateDir, "evidence", defangLogUri(logUri)+".inconsistent."+now+".proof")
if err := certspotter.WriteProofFile(proofFilename, proof); err != nil { if err := certspotter.WriteProofFile(proofFilename, proof); err != nil {
return "", "", "", err return "", "", "", err
} }
@ -109,7 +109,7 @@ func saveEvidence (logUri string, firstSTH *ct.SignedTreeHead, secondSTH *ct.Sig
return firstFilename, secondFilename, proofFilename, nil return firstFilename, secondFilename, proofFilename, nil
} }
func Main (argStateDir string, processCallback certspotter.ProcessCallback) int { func Main(argStateDir string, processCallback certspotter.ProcessCallback) int {
stateDir = argStateDir stateDir = argStateDir
var logs []certspotter.LogInfo var logs []certspotter.LogInfo
@ -171,9 +171,9 @@ func Main (argStateDir string, processCallback certspotter.ProcessCallback) int
} }
opts := certspotter.ScannerOptions{ opts := certspotter.ScannerOptions{
BatchSize: *batchSize, BatchSize: *batchSize,
NumWorkers: *numWorkers, NumWorkers: *numWorkers,
Quiet: !*verbose, Quiet: !*verbose,
} }
scanner := certspotter.NewScanner(logUri, logKey, &opts) scanner := certspotter.NewScanner(logUri, logKey, &opts)
@ -186,7 +186,7 @@ func Main (argStateDir string, processCallback certspotter.ProcessCallback) int
if *verbose { if *verbose {
if prevSTH != nil { if prevSTH != nil {
log.Printf("Existing log; scanning %d new entries since previous scan (previous size %d, previous root hash = %x)", latestSTH.TreeSize - prevSTH.TreeSize, prevSTH.TreeSize, prevSTH.SHA256RootHash) log.Printf("Existing log; scanning %d new entries since previous scan (previous size %d, previous root hash = %x)", latestSTH.TreeSize-prevSTH.TreeSize, prevSTH.TreeSize, prevSTH.SHA256RootHash)
} else if *allTime { } else if *allTime {
log.Printf("new log; scanning all %d entries in the log", latestSTH.TreeSize) log.Printf("new log; scanning all %d entries in the log", latestSTH.TreeSize)
} else { } else {

View File

@ -14,11 +14,11 @@ import (
"os" "os"
"software.sslmate.com/src/certspotter" "software.sslmate.com/src/certspotter"
"software.sslmate.com/src/certspotter/ct"
"software.sslmate.com/src/certspotter/cmd" "software.sslmate.com/src/certspotter/cmd"
"software.sslmate.com/src/certspotter/ct"
) )
func DefaultStateDir () string { func DefaultStateDir() string {
if envVar := os.Getenv("CTPARSEWATCH_STATE_DIR"); envVar != "" { if envVar := os.Getenv("CTPARSEWATCH_STATE_DIR"); envVar != "" {
return envVar return envVar
} else { } else {
@ -28,12 +28,12 @@ func DefaultStateDir () string {
var stateDir = flag.String("state_dir", DefaultStateDir(), "Directory for storing state") var stateDir = flag.String("state_dir", DefaultStateDir(), "Directory for storing state")
func processEntry (scanner *certspotter.Scanner, entry *ct.LogEntry) { func processEntry(scanner *certspotter.Scanner, entry *ct.LogEntry) {
info := certspotter.EntryInfo{ info := certspotter.EntryInfo{
LogUri: scanner.LogUri, LogUri: scanner.LogUri,
Entry: entry, Entry: entry,
IsPrecert: certspotter.IsPrecert(entry), IsPrecert: certspotter.IsPrecert(entry),
FullChain: certspotter.GetFullChain(entry), FullChain: certspotter.GetFullChain(entry),
} }
info.CertInfo, info.ParseError = certspotter.MakeCertInfoFromLogEntry(entry) info.CertInfo, info.ParseError = certspotter.MakeCertInfoFromLogEntry(entry)

View File

@ -13,15 +13,15 @@ import (
"net/http" "net/http"
"time" "time"
"software.sslmate.com/src/certspotter/ct"
"github.com/mreiferson/go-httpclient" "github.com/mreiferson/go-httpclient"
"software.sslmate.com/src/certspotter/ct"
) )
// URI paths for CT Log endpoints // URI paths for CT Log endpoints
const ( const (
GetSTHPath = "/ct/v1/get-sth" GetSTHPath = "/ct/v1/get-sth"
GetEntriesPath = "/ct/v1/get-entries" GetEntriesPath = "/ct/v1/get-entries"
GetSTHConsistencyPath = "/ct/v1/get-sth-consistency" GetSTHConsistencyPath = "/ct/v1/get-sth-consistency"
) )
// LogClient represents a client for a given CT Log instance // LogClient represents a client for a given CT Log instance
@ -84,7 +84,7 @@ func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
if err != nil { if err != nil {
return err return err
} }
// req.Header.Set("Keep-Alive", "timeout=15, max=100") // req.Header.Set("Keep-Alive", "timeout=15, max=100")
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
var body []byte var body []byte
if resp != nil { if resp != nil {
@ -97,7 +97,7 @@ func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
if err != nil { if err != nil {
return err return err
} }
if resp.StatusCode / 100 != 2 { if resp.StatusCode/100 != 2 {
return fmt.Errorf("GET %s: %s (%s)", uri, resp.Status, string(body)) return fmt.Errorf("GET %s: %s (%s)", uri, resp.Status, string(body))
} }
if err = json.Unmarshal(body, &res); err != nil { if err = json.Unmarshal(body, &res); err != nil {

View File

@ -232,9 +232,9 @@ func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
// LogEntry represents the contents of an entry in a CT log, see section 3.1. // LogEntry represents the contents of an entry in a CT log, see section 3.1.
type LogEntry struct { type LogEntry struct {
Index int64 Index int64
Leaf MerkleTreeLeaf Leaf MerkleTreeLeaf
Chain []ASN1Cert Chain []ASN1Cert
LeafBytes []byte LeafBytes []byte
} }

View File

@ -10,26 +10,26 @@
package certspotter package certspotter
import ( import (
"fmt"
"time"
"os"
"os/exec"
"bytes" "bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os"
"os/exec"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"crypto/sha256" "time"
"encoding/hex"
"encoding/pem"
"encoding/json"
"software.sslmate.com/src/certspotter/ct" "software.sslmate.com/src/certspotter/ct"
) )
func ReadSTHFile (path string) (*ct.SignedTreeHead, error) { func ReadSTHFile(path string) (*ct.SignedTreeHead, error) {
content, err := ioutil.ReadFile(path) content, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@ -46,7 +46,7 @@ func ReadSTHFile (path string) (*ct.SignedTreeHead, error) {
return &sth, nil return &sth, nil
} }
func WriteSTHFile (path string, sth *ct.SignedTreeHead) error { func WriteSTHFile(path string, sth *ct.SignedTreeHead) error {
sthJson, err := json.MarshalIndent(sth, "", "\t") sthJson, err := json.MarshalIndent(sth, "", "\t")
if err != nil { if err != nil {
return err return err
@ -55,7 +55,7 @@ func WriteSTHFile (path string, sth *ct.SignedTreeHead) error {
return ioutil.WriteFile(path, sthJson, 0666) return ioutil.WriteFile(path, sthJson, 0666)
} }
func WriteProofFile (path string, proof ct.ConsistencyProof) error { func WriteProofFile(path string, proof ct.ConsistencyProof) error {
proofJson, err := json.MarshalIndent(proof, "", "\t") proofJson, err := json.MarshalIndent(proof, "", "\t")
if err != nil { if err != nil {
return err return err
@ -64,12 +64,12 @@ func WriteProofFile (path string, proof ct.ConsistencyProof) error {
return ioutil.WriteFile(path, proofJson, 0666) return ioutil.WriteFile(path, proofJson, 0666)
} }
func IsPrecert (entry *ct.LogEntry) bool { func IsPrecert(entry *ct.LogEntry) bool {
return entry.Leaf.TimestampedEntry.EntryType == ct.PrecertLogEntryType return entry.Leaf.TimestampedEntry.EntryType == ct.PrecertLogEntryType
} }
func GetFullChain (entry *ct.LogEntry) [][]byte { func GetFullChain(entry *ct.LogEntry) [][]byte {
certs := make([][]byte, 0, len(entry.Chain) + 1) certs := make([][]byte, 0, len(entry.Chain)+1)
if entry.Leaf.TimestampedEntry.EntryType == ct.X509LogEntryType { if entry.Leaf.TimestampedEntry.EntryType == ct.X509LogEntryType {
certs = append(certs, entry.Leaf.TimestampedEntry.X509Entry) certs = append(certs, entry.Leaf.TimestampedEntry.X509Entry)
@ -81,7 +81,7 @@ func GetFullChain (entry *ct.LogEntry) [][]byte {
return certs return certs
} }
func formatSerialNumber (serial *big.Int) string { func formatSerialNumber(serial *big.Int) string {
if serial != nil { if serial != nil {
return fmt.Sprintf("%x", serial) return fmt.Sprintf("%x", serial)
} else { } else {
@ -89,45 +89,45 @@ func formatSerialNumber (serial *big.Int) string {
} }
} }
func sha256sum (data []byte) []byte { func sha256sum(data []byte) []byte {
sum := sha256.Sum256(data) sum := sha256.Sum256(data)
return sum[:] return sum[:]
} }
func sha256hex (data []byte) string { func sha256hex(data []byte) string {
return hex.EncodeToString(sha256sum(data)) return hex.EncodeToString(sha256sum(data))
} }
type EntryInfo struct { type EntryInfo struct {
LogUri string LogUri string
Entry *ct.LogEntry Entry *ct.LogEntry
IsPrecert bool IsPrecert bool
FullChain [][]byte // first entry is logged X509 cert or pre-cert FullChain [][]byte // first entry is logged X509 cert or pre-cert
CertInfo *CertInfo CertInfo *CertInfo
ParseError error // set iff CertInfo is nil ParseError error // set iff CertInfo is nil
Identifiers *Identifiers Identifiers *Identifiers
IdentifiersParseError error IdentifiersParseError error
Filename string Filename string
} }
type CertInfo struct { type CertInfo struct {
TBS *TBSCertificate TBS *TBSCertificate
Subject RDNSequence Subject RDNSequence
SubjectParseError error SubjectParseError error
Issuer RDNSequence Issuer RDNSequence
IssuerParseError error IssuerParseError error
SANs []SubjectAltName SANs []SubjectAltName
SANsParseError error SANsParseError error
SerialNumber *big.Int SerialNumber *big.Int
SerialNumberParseError error SerialNumberParseError error
Validity *CertValidity Validity *CertValidity
ValidityParseError error ValidityParseError error
IsCA *bool IsCA *bool
IsCAParseError error IsCAParseError error
} }
func MakeCertInfoFromTBS (tbs *TBSCertificate) *CertInfo { func MakeCertInfoFromTBS(tbs *TBSCertificate) *CertInfo {
info := &CertInfo{TBS: tbs} info := &CertInfo{TBS: tbs}
info.Subject, info.SubjectParseError = tbs.ParseSubject() info.Subject, info.SubjectParseError = tbs.ParseSubject()
@ -140,7 +140,7 @@ func MakeCertInfoFromTBS (tbs *TBSCertificate) *CertInfo {
return info return info
} }
func MakeCertInfoFromRawTBS (tbsBytes []byte) (*CertInfo, error) { func MakeCertInfoFromRawTBS(tbsBytes []byte) (*CertInfo, error) {
tbs, err := ParseTBSCertificate(tbsBytes) tbs, err := ParseTBSCertificate(tbsBytes)
if err != nil { if err != nil {
return nil, err return nil, err
@ -148,7 +148,7 @@ func MakeCertInfoFromRawTBS (tbsBytes []byte) (*CertInfo, error) {
return MakeCertInfoFromTBS(tbs), nil return MakeCertInfoFromTBS(tbs), nil
} }
func MakeCertInfoFromRawCert (certBytes []byte) (*CertInfo, error) { func MakeCertInfoFromRawCert(certBytes []byte) (*CertInfo, error) {
cert, err := ParseCertificate(certBytes) cert, err := ParseCertificate(certBytes)
if err != nil { if err != nil {
return nil, err return nil, err
@ -156,7 +156,7 @@ func MakeCertInfoFromRawCert (certBytes []byte) (*CertInfo, error) {
return MakeCertInfoFromRawTBS(cert.GetRawTBSCertificate()) return MakeCertInfoFromRawTBS(cert.GetRawTBSCertificate())
} }
func MakeCertInfoFromLogEntry (entry *ct.LogEntry) (*CertInfo, error) { func MakeCertInfoFromLogEntry(entry *ct.LogEntry) (*CertInfo, error) {
switch entry.Leaf.TimestampedEntry.EntryType { switch entry.Leaf.TimestampedEntry.EntryType {
case ct.X509LogEntryType: case ct.X509LogEntryType:
return MakeCertInfoFromRawCert(entry.Leaf.TimestampedEntry.X509Entry) return MakeCertInfoFromRawCert(entry.Leaf.TimestampedEntry.X509Entry)
@ -169,7 +169,7 @@ func MakeCertInfoFromLogEntry (entry *ct.LogEntry) (*CertInfo, error) {
} }
} }
func (info *CertInfo) NotBefore () *time.Time { func (info *CertInfo) NotBefore() *time.Time {
if info.ValidityParseError == nil { if info.ValidityParseError == nil {
return &info.Validity.NotBefore return &info.Validity.NotBefore
} else { } else {
@ -177,7 +177,7 @@ func (info *CertInfo) NotBefore () *time.Time {
} }
} }
func (info *CertInfo) NotAfter () *time.Time { func (info *CertInfo) NotAfter() *time.Time {
if info.ValidityParseError == nil { if info.ValidityParseError == nil {
return &info.Validity.NotAfter return &info.Validity.NotAfter
} else { } else {
@ -185,44 +185,44 @@ func (info *CertInfo) NotAfter () *time.Time {
} }
} }
func (info *CertInfo) PubkeyHash () string { func (info *CertInfo) PubkeyHash() string {
return sha256hex(info.TBS.GetRawPublicKey()) return sha256hex(info.TBS.GetRawPublicKey())
} }
func (info *CertInfo) PubkeyHashBytes () []byte { func (info *CertInfo) PubkeyHashBytes() []byte {
return sha256sum(info.TBS.GetRawPublicKey()) return sha256sum(info.TBS.GetRawPublicKey())
} }
func (info *CertInfo) Environ () []string { func (info *CertInfo) Environ() []string {
env := make([]string, 0, 10) env := make([]string, 0, 10)
env = append(env, "PUBKEY_HASH=" + info.PubkeyHash()) env = append(env, "PUBKEY_HASH="+info.PubkeyHash())
if info.SerialNumberParseError != nil { if info.SerialNumberParseError != nil {
env = append(env, "SERIAL_PARSE_ERROR=" + info.SerialNumberParseError.Error()) env = append(env, "SERIAL_PARSE_ERROR="+info.SerialNumberParseError.Error())
} else { } else {
env = append(env, "SERIAL=" + formatSerialNumber(info.SerialNumber)) env = append(env, "SERIAL="+formatSerialNumber(info.SerialNumber))
} }
if info.ValidityParseError != nil { if info.ValidityParseError != nil {
env = append(env, "VALIDITY_PARSE_ERROR=" + info.ValidityParseError.Error()) env = append(env, "VALIDITY_PARSE_ERROR="+info.ValidityParseError.Error())
} else { } else {
env = append(env, "NOT_BEFORE=" + info.Validity.NotBefore.String()) 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_BEFORE_UNIXTIME="+strconv.FormatInt(info.Validity.NotBefore.Unix(), 10))
env = append(env, "NOT_AFTER=" + info.Validity.NotAfter.String()) env = append(env, "NOT_AFTER="+info.Validity.NotAfter.String())
env = append(env, "NOT_AFTER_UNIXTIME=" + strconv.FormatInt(info.Validity.NotAfter.Unix(), 10)) env = append(env, "NOT_AFTER_UNIXTIME="+strconv.FormatInt(info.Validity.NotAfter.Unix(), 10))
} }
if info.SubjectParseError != nil { if info.SubjectParseError != nil {
env = append(env, "SUBJECT_PARSE_ERROR=" + info.SubjectParseError.Error()) env = append(env, "SUBJECT_PARSE_ERROR="+info.SubjectParseError.Error())
} else { } else {
env = append(env, "SUBJECT_DN=" + info.Subject.String()) env = append(env, "SUBJECT_DN="+info.Subject.String())
} }
if info.IssuerParseError != nil { if info.IssuerParseError != nil {
env = append(env, "ISSUER_PARSE_ERROR=" + info.IssuerParseError.Error()) env = append(env, "ISSUER_PARSE_ERROR="+info.IssuerParseError.Error())
} else { } else {
env = append(env, "ISSUER_DN=" + info.Issuer.String()) env = append(env, "ISSUER_DN="+info.Issuer.String())
} }
// TODO: include SANs in environment // TODO: include SANs in environment
@ -230,7 +230,7 @@ func (info *CertInfo) Environ () []string {
return env return env
} }
func (info *EntryInfo) HasParseErrors () bool { func (info *EntryInfo) HasParseErrors() bool {
return info.ParseError != nil || return info.ParseError != nil ||
info.IdentifiersParseError != nil || info.IdentifiersParseError != nil ||
info.CertInfo.SubjectParseError != nil || info.CertInfo.SubjectParseError != nil ||
@ -241,7 +241,7 @@ func (info *EntryInfo) HasParseErrors () bool {
info.CertInfo.IsCAParseError != nil info.CertInfo.IsCAParseError != nil
} }
func (info *EntryInfo) Fingerprint () string { func (info *EntryInfo) Fingerprint() string {
if len(info.FullChain) > 0 { if len(info.FullChain) > 0 {
return sha256hex(info.FullChain[0]) return sha256hex(info.FullChain[0])
} else { } else {
@ -249,7 +249,7 @@ func (info *EntryInfo) Fingerprint () string {
} }
} }
func (info *EntryInfo) FingerprintBytes () []byte { func (info *EntryInfo) FingerprintBytes() []byte {
if len(info.FullChain) > 0 { if len(info.FullChain) > 0 {
return sha256sum(info.FullChain[0]) return sha256sum(info.FullChain[0])
} else { } else {
@ -257,7 +257,7 @@ func (info *EntryInfo) FingerprintBytes () []byte {
} }
} }
func (info *EntryInfo) typeString () string { func (info *EntryInfo) typeString() string {
if info.IsPrecert { if info.IsPrecert {
return "precert" return "precert"
} else { } else {
@ -265,7 +265,7 @@ func (info *EntryInfo) typeString () string {
} }
} }
func (info *EntryInfo) typeFriendlyString () string { func (info *EntryInfo) typeFriendlyString() string {
if info.IsPrecert { if info.IsPrecert {
return "Pre-certificate" return "Pre-certificate"
} else { } else {
@ -273,7 +273,7 @@ func (info *EntryInfo) typeFriendlyString () string {
} }
} }
func yesnoString (value bool) string { func yesnoString(value bool) string {
if value { if value {
return "yes" return "yes"
} else { } else {
@ -281,7 +281,7 @@ func yesnoString (value bool) string {
} }
} }
func (info *EntryInfo) Environ () []string { func (info *EntryInfo) Environ() []string {
env := []string{ env := []string{
"FINGERPRINT=" + info.Fingerprint(), "FINGERPRINT=" + info.Fingerprint(),
"CERT_TYPE=" + info.typeString(), "CERT_TYPE=" + info.typeString(),
@ -291,25 +291,25 @@ func (info *EntryInfo) Environ () []string {
} }
if info.Filename != "" { if info.Filename != "" {
env = append(env, "CERT_FILENAME=" + info.Filename) env = append(env, "CERT_FILENAME="+info.Filename)
} }
if info.ParseError != nil { if info.ParseError != nil {
env = append(env, "PARSE_ERROR=" + info.ParseError.Error()) env = append(env, "PARSE_ERROR="+info.ParseError.Error())
} else if info.CertInfo != nil { } else if info.CertInfo != nil {
certEnv := info.CertInfo.Environ() certEnv := info.CertInfo.Environ()
env = append(env, certEnv...) env = append(env, certEnv...)
} }
if info.IdentifiersParseError != nil { if info.IdentifiersParseError != nil {
env = append(env, "IDENTIFIERS_PARSE_ERROR=" + info.IdentifiersParseError.Error()) env = append(env, "IDENTIFIERS_PARSE_ERROR="+info.IdentifiersParseError.Error())
} else if info.Identifiers != nil { } else if info.Identifiers != nil {
env = append(env, "DNS_NAMES=" + info.Identifiers.dnsNamesString(",")) env = append(env, "DNS_NAMES="+info.Identifiers.dnsNamesString(","))
env = append(env, "IP_ADDRESSES=" + info.Identifiers.ipAddrsString(",")) env = append(env, "IP_ADDRESSES="+info.Identifiers.ipAddrsString(","))
} }
return env return env
} }
func writeField (out io.Writer, name string, value interface{}, err error) { func writeField(out io.Writer, name string, value interface{}, err error) {
if err == nil { if err == nil {
fmt.Fprintf(out, "\t%13s = %s\n", name, value) fmt.Fprintf(out, "\t%13s = %s\n", name, value)
} else { } else {
@ -317,7 +317,7 @@ func writeField (out io.Writer, name string, value interface{}, err error) {
} }
} }
func (info *EntryInfo) Write (out io.Writer) { func (info *EntryInfo) Write(out io.Writer) {
fingerprint := info.Fingerprint() fingerprint := info.Fingerprint()
fmt.Fprintf(out, "%s:\n", fingerprint) fmt.Fprintf(out, "%s:\n", fingerprint)
if info.IdentifiersParseError != nil { if info.IdentifiersParseError != nil {
@ -331,7 +331,7 @@ func (info *EntryInfo) Write (out io.Writer) {
} }
} }
if info.ParseError != nil { if info.ParseError != nil {
writeField(out, "Parse Error", "*** " + info.ParseError.Error() + " ***", nil) writeField(out, "Parse Error", "*** "+info.ParseError.Error()+" ***", nil)
} else if info.CertInfo != nil { } else if info.CertInfo != nil {
writeField(out, "Pubkey", info.CertInfo.PubkeyHash(), nil) writeField(out, "Pubkey", info.CertInfo.PubkeyHash(), nil)
writeField(out, "Issuer", info.CertInfo.Issuer, info.CertInfo.IssuerParseError) writeField(out, "Issuer", info.CertInfo.Issuer, info.CertInfo.IssuerParseError)
@ -339,13 +339,13 @@ func (info *EntryInfo) Write (out io.Writer) {
writeField(out, "Not After", info.CertInfo.NotAfter(), info.CertInfo.ValidityParseError) writeField(out, "Not After", info.CertInfo.NotAfter(), info.CertInfo.ValidityParseError)
} }
writeField(out, "Log Entry", fmt.Sprintf("%d @ %s (%s)", info.Entry.Index, info.LogUri, info.typeFriendlyString()), nil) writeField(out, "Log Entry", fmt.Sprintf("%d @ %s (%s)", info.Entry.Index, info.LogUri, info.typeFriendlyString()), nil)
writeField(out, "crt.sh", "https://crt.sh/?sha256=" + fingerprint, nil) writeField(out, "crt.sh", "https://crt.sh/?sha256="+fingerprint, nil)
if info.Filename != "" { if info.Filename != "" {
writeField(out, "Filename", info.Filename, nil) writeField(out, "Filename", info.Filename, nil)
} }
} }
func (info *EntryInfo) InvokeHookScript (command string) error { func (info *EntryInfo) InvokeHookScript(command string) error {
cmd := exec.Command(command) cmd := exec.Command(command)
cmd.Env = os.Environ() cmd.Env = os.Environ()
infoEnv := info.Environ() infoEnv := info.Environ()
@ -362,7 +362,7 @@ func (info *EntryInfo) InvokeHookScript (command string) error {
return nil return nil
} }
func WriteCertRepository (repoPath string, isPrecert bool, certs [][]byte) (bool, string, error) { func WriteCertRepository(repoPath string, isPrecert bool, certs [][]byte) (bool, string, error) {
if len(certs) == 0 { if len(certs) == 0 {
return false, "", fmt.Errorf("Cannot write an empty certificate chain") return false, "", fmt.Errorf("Cannot write an empty certificate chain")
} }
@ -378,7 +378,7 @@ func WriteCertRepository (repoPath string, isPrecert bool, certs [][]byte) (bool
if err := os.Mkdir(prefixPath, 0777); err != nil && !os.IsExist(err) { if err := os.Mkdir(prefixPath, 0777); err != nil && !os.IsExist(err) {
return false, "", fmt.Errorf("Failed to create prefix directory %s: %s", prefixPath, err) return false, "", fmt.Errorf("Failed to create prefix directory %s: %s", prefixPath, err)
} }
path := filepath.Join(prefixPath, fingerprint + filenameSuffix) path := filepath.Join(prefixPath, fingerprint+filenameSuffix)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
if err != nil { if err != nil {
if os.IsExist(err) { if os.IsExist(err) {
@ -400,7 +400,7 @@ func WriteCertRepository (repoPath string, isPrecert bool, certs [][]byte) (bool
return false, path, nil return false, path, nil
} }
func MatchesWildcard (dnsName string, pattern string) bool { func MatchesWildcard(dnsName string, pattern string) bool {
for len(pattern) > 0 { for len(pattern) > 0 {
if pattern[0] == '*' { if pattern[0] == '*' {
if len(dnsName) > 0 && dnsName[0] != '.' && MatchesWildcard(dnsName[1:], pattern) { if len(dnsName) > 0 && dnsName[0] != '.' && MatchesWildcard(dnsName[1:], pattern) {

View File

@ -13,7 +13,7 @@ import (
"testing" "testing"
) )
func doWildcardTest (t *testing.T, dnsName string, wildcard string, expected bool) { func doWildcardTest(t *testing.T, dnsName string, wildcard string, expected bool) {
if MatchesWildcard(dnsName, wildcard) != expected { if MatchesWildcard(dnsName, wildcard) != expected {
t.Errorf("MatchesWildcard(%q, %q) != %v", dnsName, wildcard, expected) t.Errorf("MatchesWildcard(%q, %q) != %v", dnsName, wildcard, expected)
} }

View File

@ -11,10 +11,10 @@ package certspotter
import ( import (
"bytes" "bytes"
"strings"
"net"
"unicode/utf8"
"golang.org/x/net/idna" "golang.org/x/net/idna"
"net"
"strings"
"unicode/utf8"
) )
const UnparsableDNSLabelPlaceholder = "<unparsable>" const UnparsableDNSLabelPlaceholder = "<unparsable>"
@ -34,24 +34,24 @@ type UnknownIdentifier struct {
*/ */
type Identifiers struct { type Identifiers struct {
DNSNames []string // stored as ASCII, with IDNs in Punycode DNSNames []string // stored as ASCII, with IDNs in Punycode
IPAddrs []net.IP IPAddrs []net.IP
//Unknowns []UnknownIdentifier //Unknowns []UnknownIdentifier
} }
func NewIdentifiers () *Identifiers { func NewIdentifiers() *Identifiers {
return &Identifiers{ return &Identifiers{
DNSNames: []string{}, DNSNames: []string{},
IPAddrs: []net.IP{}, IPAddrs: []net.IP{},
//Unknowns: []UnknownIdentifier{}, //Unknowns: []UnknownIdentifier{},
} }
} }
func parseIPAddrString (str string) net.IP { func parseIPAddrString(str string) net.IP {
return net.ParseIP(str) return net.ParseIP(str)
} }
func isASCIIString (value []byte) bool { func isASCIIString(value []byte) bool {
for _, b := range value { for _, b := range value {
if b > 127 { if b > 127 {
return false return false
@ -59,10 +59,10 @@ func isASCIIString (value []byte) bool {
} }
return true return true
} }
func isUTF8String (value []byte) bool { func isUTF8String(value []byte) bool {
return utf8.Valid(value) return utf8.Valid(value)
} }
func latin1ToUTF8 (value []byte) string { func latin1ToUTF8(value []byte) string {
runes := make([]rune, len(value)) runes := make([]rune, len(value))
for i, b := range value { for i, b := range value {
runes[i] = rune(b) runes[i] = rune(b)
@ -72,10 +72,10 @@ func latin1ToUTF8 (value []byte) string {
// Make sure the DNS label doesn't have any weird characters that // Make sure the DNS label doesn't have any weird characters that
// could cause trouble during later processing. // could cause trouble during later processing.
func isSaneDNSLabelChar (ch rune) bool { func isSaneDNSLabelChar(ch rune) bool {
return ch == '\t' || (ch >= 32 && ch <= 126) return ch == '\t' || (ch >= 32 && ch <= 126)
} }
func isSaneDNSLabel (label string) bool { func isSaneDNSLabel(label string) bool {
for _, ch := range label { for _, ch := range label {
if !isSaneDNSLabelChar(ch) { if !isSaneDNSLabelChar(ch) {
return false return false
@ -84,7 +84,7 @@ func isSaneDNSLabel (label string) bool {
return true return true
} }
func trimHttpPrefixString (value string) string { func trimHttpPrefixString(value string) string {
if strings.HasPrefix(value, "http://") { if strings.HasPrefix(value, "http://") {
return value[7:] return value[7:]
} else if strings.HasPrefix(value, "https://") { } else if strings.HasPrefix(value, "https://") {
@ -94,7 +94,7 @@ func trimHttpPrefixString (value string) string {
} }
} }
func trimHttpPrefixBytes (value []byte) []byte { func trimHttpPrefixBytes(value []byte) []byte {
if bytes.HasPrefix(value, []byte("http://")) { if bytes.HasPrefix(value, []byte("http://")) {
return value[7:] return value[7:]
} else if bytes.HasPrefix(value, []byte("https://")) { } else if bytes.HasPrefix(value, []byte("https://")) {
@ -104,9 +104,9 @@ func trimHttpPrefixBytes (value []byte) []byte {
} }
} }
func trimTrailingDots (value string) string { func trimTrailingDots(value string) string {
length := len(value) length := len(value)
for length > 0 && value[length - 1] == '.' { for length > 0 && value[length-1] == '.' {
length-- length--
} }
return value[0:length] return value[0:length]
@ -117,7 +117,7 @@ func trimTrailingDots (value string) string {
// 2. Trim trailing dots // 2. Trim trailing dots
// 3. Convert to lower case // 3. Convert to lower case
// 4. Replace totally nonsensical labels (e.g. having non-printable characters) with a placeholder // 4. Replace totally nonsensical labels (e.g. having non-printable characters) with a placeholder
func sanitizeDNSName (value string) string { func sanitizeDNSName(value string) string {
value = strings.ToLower(trimTrailingDots(strings.TrimSpace(value))) value = strings.ToLower(trimTrailingDots(strings.TrimSpace(value)))
labels := strings.Split(value, ".") labels := strings.Split(value, ".")
for i, label := range labels { for i, label := range labels {
@ -129,7 +129,7 @@ func sanitizeDNSName (value string) string {
} }
// Like sanitizeDNSName, but labels that are Unicode are converted to Punycode. // Like sanitizeDNSName, but labels that are Unicode are converted to Punycode.
func sanitizeUnicodeDNSName (value string) string { func sanitizeUnicodeDNSName(value string) string {
value = strings.ToLower(trimTrailingDots(strings.TrimSpace(value))) value = strings.ToLower(trimTrailingDots(strings.TrimSpace(value)))
labels := strings.Split(value, ".") labels := strings.Split(value, ".")
for i, label := range labels { for i, label := range labels {
@ -142,13 +142,13 @@ func sanitizeUnicodeDNSName (value string) string {
return strings.Join(labels, ".") return strings.Join(labels, ".")
} }
func (ids *Identifiers) appendDNSName (dnsName string) { func (ids *Identifiers) appendDNSName(dnsName string) {
if dnsName != "" { if dnsName != "" {
ids.DNSNames = append(ids.DNSNames, dnsName) ids.DNSNames = append(ids.DNSNames, dnsName)
} }
} }
func (ids *Identifiers) addDnsSANfinal (value []byte) { func (ids *Identifiers) addDnsSANfinal(value []byte) {
if ipaddr := parseIPAddrString(string(value)); ipaddr != nil { if ipaddr := parseIPAddrString(string(value)); ipaddr != nil {
// Stupid CAs put IP addresses in DNS SANs because stupid Microsoft // Stupid CAs put IP addresses in DNS SANs because stupid Microsoft
// used to not support IP address SANs. Since there's no way for an IP // used to not support IP address SANs. Since there's no way for an IP
@ -169,7 +169,7 @@ func (ids *Identifiers) addDnsSANfinal (value []byte) {
} }
} }
func (ids *Identifiers) addDnsSANnonull (value []byte) { func (ids *Identifiers) addDnsSANnonull(value []byte) {
if slashIndex := bytes.IndexByte(value, '/'); slashIndex != -1 { if slashIndex := bytes.IndexByte(value, '/'); slashIndex != -1 {
// If the value contains a slash, then this might be a URL, // If the value contains a slash, then this might be a URL,
// so process the part of the value up to the first slash, // so process the part of the value up to the first slash,
@ -181,7 +181,7 @@ func (ids *Identifiers) addDnsSANnonull (value []byte) {
ids.addDnsSANfinal(value) ids.addDnsSANfinal(value)
} }
func (ids *Identifiers) AddDnsSAN (value []byte) { func (ids *Identifiers) AddDnsSAN(value []byte) {
// Trim http:// and https:// prefixes, which are all too common in the wild, // Trim http:// and https:// prefixes, which are all too common in the wild,
// so http://example.com becomes just example.com. Even though clients // so http://example.com becomes just example.com. Even though clients
// should never successfully validate a DNS name like http://example.com, // should never successfully validate a DNS name like http://example.com,
@ -198,7 +198,7 @@ func (ids *Identifiers) AddDnsSAN (value []byte) {
ids.addDnsSANnonull(value) ids.addDnsSANnonull(value)
} }
func (ids *Identifiers) addCNfinal (value string) { func (ids *Identifiers) addCNfinal(value string) {
if ipaddr := parseIPAddrString(value); ipaddr != nil { if ipaddr := parseIPAddrString(value); ipaddr != nil {
ids.IPAddrs = append(ids.IPAddrs, ipaddr) ids.IPAddrs = append(ids.IPAddrs, ipaddr)
} else if !strings.ContainsRune(value, ' ') { } else if !strings.ContainsRune(value, ' ') {
@ -207,7 +207,7 @@ func (ids *Identifiers) addCNfinal (value string) {
} }
} }
func (ids *Identifiers) addCNnonull (value string) { func (ids *Identifiers) addCNnonull(value string) {
if slashIndex := strings.IndexRune(value, '/'); slashIndex != -1 { if slashIndex := strings.IndexRune(value, '/'); slashIndex != -1 {
// If the value contains a slash, then this might be a URL, // If the value contains a slash, then this might be a URL,
// so process the part of the value up to the first slash, // so process the part of the value up to the first slash,
@ -219,7 +219,7 @@ func (ids *Identifiers) addCNnonull (value string) {
ids.addCNfinal(value) ids.addCNfinal(value)
} }
func (ids *Identifiers) AddCN (value string) { func (ids *Identifiers) AddCN(value string) {
// Trim http:// and https:// prefixes, which are all too common in the wild, // Trim http:// and https:// prefixes, which are all too common in the wild,
// so http://example.com becomes just example.com. Even though clients // so http://example.com becomes just example.com. Even though clients
// should never successfully validate a DNS name like http://example.com, // should never successfully validate a DNS name like http://example.com,
@ -236,15 +236,15 @@ func (ids *Identifiers) AddCN (value string) {
ids.addCNnonull(value) ids.addCNnonull(value)
} }
func (ids *Identifiers) AddIPAddress (value net.IP) { func (ids *Identifiers) AddIPAddress(value net.IP) {
ids.IPAddrs = append(ids.IPAddrs, value) ids.IPAddrs = append(ids.IPAddrs, value)
} }
func (ids *Identifiers) dnsNamesString (sep string) string { func (ids *Identifiers) dnsNamesString(sep string) string {
return strings.Join(ids.DNSNames, sep) return strings.Join(ids.DNSNames, sep)
} }
func (ids *Identifiers) ipAddrsString (sep string) string { func (ids *Identifiers) ipAddrsString(sep string) string {
str := "" str := ""
for _, ipAddr := range ids.IPAddrs { for _, ipAddr := range ids.IPAddrs {
if str != "" { if str != "" {
@ -255,7 +255,7 @@ func (ids *Identifiers) ipAddrsString (sep string) string {
return str return str
} }
func (cert *CertInfo) ParseIdentifiers () (*Identifiers, error) { func (cert *CertInfo) ParseIdentifiers() (*Identifiers, error) {
ids := NewIdentifiers() ids := NewIdentifiers()
if cert.SubjectParseError != nil { if cert.SubjectParseError != nil {

24
logs.go
View File

@ -10,26 +10,26 @@
package certspotter package certspotter
import ( import (
"encoding/base64"
"crypto" "crypto"
"crypto/x509" "crypto/x509"
"encoding/base64"
) )
type LogInfoFile struct { type LogInfoFile struct {
Logs []LogInfo `json:"logs"` Logs []LogInfo `json:"logs"`
} }
type LogInfo struct { type LogInfo struct {
Description string `json:"description"` Description string `json:"description"`
Key []byte `json:"key"` Key []byte `json:"key"`
Url string `json:"url"` Url string `json:"url"`
MMD int `json:"maximum_merge_delay"` MMD int `json:"maximum_merge_delay"`
} }
func (info *LogInfo) FullURI () string { func (info *LogInfo) FullURI() string {
return "https://" + info.Url return "https://" + info.Url
} }
func (info *LogInfo) ParsedPublicKey () (crypto.PublicKey, error) { func (info *LogInfo) ParsedPublicKey() (crypto.PublicKey, error) {
if info.Key != nil { if info.Key != nil {
return x509.ParsePKIXPublicKey(info.Key) return x509.ParsePKIXPublicKey(info.Key)
} else { } else {
@ -92,13 +92,13 @@ var DefaultLogs = []LogInfo{
var UnderwaterLogs = []LogInfo{ var UnderwaterLogs = []LogInfo{
{ {
Description: "Google 'Submariner' log", Description: "Google 'Submariner' log",
Key: mustDecodeBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOfifIGLUV1Voou9JLfA5LZreRLSUMOCeeic8q3Dw0fpRkGMWV0Gtq20fgHQweQJeLVmEByQj9p81uIW4QkWkTw=="), Key: mustDecodeBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOfifIGLUV1Voou9JLfA5LZreRLSUMOCeeic8q3Dw0fpRkGMWV0Gtq20fgHQweQJeLVmEByQj9p81uIW4QkWkTw=="),
Url: "ct.googleapis.com/submariner", Url: "ct.googleapis.com/submariner",
MMD: 86400, MMD: 86400,
}, },
} }
func mustDecodeBase64 (str string) []byte { func mustDecodeBase64(str string) []byte {
bytes, err := base64.StdEncoding.DecodeString(str) bytes, err := base64.StdEncoding.DecodeString(str)
if err != nil { if err != nil {
panic("MustDecodeBase64: " + err.Error()) panic("MustDecodeBase64: " + err.Error())

View File

@ -10,22 +10,23 @@
package certspotter package certspotter
import ( import (
"fmt"
"errors"
"bytes" "bytes"
"encoding/asn1" "encoding/asn1"
"errors"
"fmt"
) )
func bitStringEqual (a, b *asn1.BitString) bool { func bitStringEqual(a, b *asn1.BitString) bool {
return a.BitLength == b.BitLength && bytes.Equal(a.Bytes, b.Bytes) return a.BitLength == b.BitLength && bytes.Equal(a.Bytes, b.Bytes)
} }
var ( var (
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
oidExtensionSCT = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2} oidExtensionSCT = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
oidExtensionCTPoison = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3} oidExtensionCTPoison = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
) )
func ValidatePrecert (precertBytes []byte, tbsBytes []byte) error {
func ValidatePrecert(precertBytes []byte, tbsBytes []byte) error {
precert, err := ParseCertificate(precertBytes) precert, err := ParseCertificate(precertBytes)
if err != nil { if err != nil {
return errors.New("failed to parse pre-certificate: " + err.Error()) return errors.New("failed to parse pre-certificate: " + err.Error())
@ -116,18 +117,18 @@ func ValidatePrecert (precertBytes []byte, tbsBytes []byte) error {
return nil return nil
} }
func ReconstructPrecertTBS (tbs *TBSCertificate) (*TBSCertificate, error) { func ReconstructPrecertTBS(tbs *TBSCertificate) (*TBSCertificate, error) {
precertTBS := TBSCertificate{ precertTBS := TBSCertificate{
Version: tbs.Version, Version: tbs.Version,
SerialNumber: tbs.SerialNumber, SerialNumber: tbs.SerialNumber,
SignatureAlgorithm: tbs.SignatureAlgorithm, SignatureAlgorithm: tbs.SignatureAlgorithm,
Issuer: tbs.Issuer, Issuer: tbs.Issuer,
Validity: tbs.Validity, Validity: tbs.Validity,
Subject: tbs.Subject, Subject: tbs.Subject,
PublicKey: tbs.PublicKey, PublicKey: tbs.PublicKey,
UniqueId: tbs.UniqueId, UniqueId: tbs.UniqueId,
SubjectUniqueId: tbs.SubjectUniqueId, SubjectUniqueId: tbs.SubjectUniqueId,
Extensions: make([]Extension, 0, len(tbs.Extensions)), Extensions: make([]Extension, 0, len(tbs.Extensions)),
} }
for _, ext := range tbs.Extensions { for _, ext := range tbs.Extensions {

View File

@ -13,14 +13,14 @@
package certspotter package certspotter
import ( import (
// "container/list" // "container/list"
"crypto"
"errors"
"fmt" "fmt"
"log" "log"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"crypto"
"errors"
"software.sslmate.com/src/certspotter/ct" "software.sslmate.com/src/certspotter/ct"
"software.sslmate.com/src/certspotter/ct/client" "software.sslmate.com/src/certspotter/ct/client"
@ -29,7 +29,7 @@ import (
type ProcessCallback func(*Scanner, *ct.LogEntry) type ProcessCallback func(*Scanner, *ct.LogEntry)
const ( const (
FETCH_RETRIES = 10 FETCH_RETRIES = 10
FETCH_RETRY_WAIT = 1 FETCH_RETRY_WAIT = 1
) )
@ -48,28 +48,28 @@ type ScannerOptions struct {
// Creates a new ScannerOptions struct with sensible defaults // Creates a new ScannerOptions struct with sensible defaults
func DefaultScannerOptions() *ScannerOptions { func DefaultScannerOptions() *ScannerOptions {
return &ScannerOptions{ return &ScannerOptions{
BatchSize: 1000, BatchSize: 1000,
NumWorkers: 1, NumWorkers: 1,
Quiet: false, Quiet: false,
} }
} }
// Scanner is a tool to scan all the entries in a CT Log. // Scanner is a tool to scan all the entries in a CT Log.
type Scanner struct { type Scanner struct {
// Base URI of CT log // Base URI of CT log
LogUri string LogUri string
// Public key of the log // Public key of the log
publicKey crypto.PublicKey publicKey crypto.PublicKey
// Client used to talk to the CT log instance // Client used to talk to the CT log instance
logClient *client.LogClient logClient *client.LogClient
// Configuration options for this Scanner instance // Configuration options for this Scanner instance
opts ScannerOptions opts ScannerOptions
// Stats // Stats
certsProcessed int64 certsProcessed int64
} }
// fetchRange represents a range of certs to fetch from a CT log // fetchRange represents a range of certs to fetch from a CT log
@ -233,7 +233,7 @@ func (s *Scanner) CheckConsistency(first *ct.SignedTreeHead, second *ct.SignedTr
} }
func (s *Scanner) Scan(startIndex int64, endIndex int64, processCert ProcessCallback, treeBuilder *MerkleTreeBuilder) error { func (s *Scanner) Scan(startIndex int64, endIndex int64, processCert ProcessCallback, treeBuilder *MerkleTreeBuilder) error {
s.Log("Starting scan..."); s.Log("Starting scan...")
s.certsProcessed = 0 s.certsProcessed = 0
startTime := time.Now() startTime := time.Now()

173
x509.go
View File

@ -10,93 +10,93 @@
package certspotter package certspotter
import ( import (
"fmt"
"bytes" "bytes"
"errors"
"encoding/asn1" "encoding/asn1"
"errors"
"fmt"
"math/big" "math/big"
"time"
"net" "net"
"time"
) )
var ( var (
oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
oidExtensionBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19} oidExtensionBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19}
oidCountry = asn1.ObjectIdentifier{2, 5, 4, 6} oidCountry = asn1.ObjectIdentifier{2, 5, 4, 6}
oidOrganization = asn1.ObjectIdentifier{2, 5, 4, 10} oidOrganization = asn1.ObjectIdentifier{2, 5, 4, 10}
oidOrganizationalUnit = asn1.ObjectIdentifier{2, 5, 4, 11} oidOrganizationalUnit = asn1.ObjectIdentifier{2, 5, 4, 11}
oidCommonName = asn1.ObjectIdentifier{2, 5, 4, 3} oidCommonName = asn1.ObjectIdentifier{2, 5, 4, 3}
oidSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5} oidSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5}
oidLocality = asn1.ObjectIdentifier{2, 5, 4, 7} oidLocality = asn1.ObjectIdentifier{2, 5, 4, 7}
oidProvince = asn1.ObjectIdentifier{2, 5, 4, 8} oidProvince = asn1.ObjectIdentifier{2, 5, 4, 8}
oidStreetAddress = asn1.ObjectIdentifier{2, 5, 4, 9} oidStreetAddress = asn1.ObjectIdentifier{2, 5, 4, 9}
oidPostalCode = asn1.ObjectIdentifier{2, 5, 4, 17} oidPostalCode = asn1.ObjectIdentifier{2, 5, 4, 17}
) )
type CertValidity struct { type CertValidity struct {
NotBefore time.Time NotBefore time.Time
NotAfter time.Time NotAfter time.Time
} }
type basicConstraints struct { type basicConstraints struct {
IsCA bool `asn1:"optional"` IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"` MaxPathLen int `asn1:"optional,default:-1"`
} }
type Extension struct { type Extension struct {
Id asn1.ObjectIdentifier Id asn1.ObjectIdentifier
Critical bool `asn1:"optional"` Critical bool `asn1:"optional"`
Value []byte Value []byte
} }
const ( const (
sanOtherName = 0 sanOtherName = 0
sanRfc822Name = 1 sanRfc822Name = 1
sanDNSName = 2 sanDNSName = 2
sanX400Address = 3 sanX400Address = 3
sanDirectoryName = 4 sanDirectoryName = 4
sanEdiPartyName = 5 sanEdiPartyName = 5
sanURI = 6 sanURI = 6
sanIPAddress = 7 sanIPAddress = 7
sanRegisteredID = 8 sanRegisteredID = 8
) )
type SubjectAltName struct { type SubjectAltName struct {
Type int Type int
Value []byte Value []byte
} }
type RDNSequence []RelativeDistinguishedNameSET type RDNSequence []RelativeDistinguishedNameSET
type RelativeDistinguishedNameSET []AttributeTypeAndValue type RelativeDistinguishedNameSET []AttributeTypeAndValue
type AttributeTypeAndValue struct { type AttributeTypeAndValue struct {
Type asn1.ObjectIdentifier Type asn1.ObjectIdentifier
Value asn1.RawValue Value asn1.RawValue
} }
type TBSCertificate struct { type TBSCertificate struct {
Raw asn1.RawContent Raw asn1.RawContent
Version int `asn1:"optional,explicit,default:1,tag:0"` Version int `asn1:"optional,explicit,default:1,tag:0"`
SerialNumber asn1.RawValue SerialNumber asn1.RawValue
SignatureAlgorithm asn1.RawValue SignatureAlgorithm asn1.RawValue
Issuer asn1.RawValue Issuer asn1.RawValue
Validity asn1.RawValue Validity asn1.RawValue
Subject asn1.RawValue Subject asn1.RawValue
PublicKey asn1.RawValue PublicKey asn1.RawValue
UniqueId asn1.BitString `asn1:"optional,tag:1"` UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
Extensions []Extension `asn1:"optional,explicit,tag:3"` Extensions []Extension `asn1:"optional,explicit,tag:3"`
} }
type Certificate struct { type Certificate struct {
Raw asn1.RawContent Raw asn1.RawContent
TBSCertificate asn1.RawValue TBSCertificate asn1.RawValue
SignatureAlgorithm asn1.RawValue SignatureAlgorithm asn1.RawValue
SignatureValue asn1.RawValue SignatureValue asn1.RawValue
} }
func (rdns RDNSequence) ParseCNs() ([]string, error) {
func (rdns RDNSequence) ParseCNs () ([]string, error) {
var cns []string var cns []string
for _, rdn := range rdns { for _, rdn := range rdns {
@ -116,22 +116,31 @@ func (rdns RDNSequence) ParseCNs () ([]string, error) {
return cns, nil return cns, nil
} }
func rdnLabel (oid asn1.ObjectIdentifier) string { func rdnLabel(oid asn1.ObjectIdentifier) string {
switch { switch {
case oid.Equal(oidCountry): return "C" case oid.Equal(oidCountry):
case oid.Equal(oidOrganization): return "O" return "C"
case oid.Equal(oidOrganizationalUnit): return "OU" case oid.Equal(oidOrganization):
case oid.Equal(oidCommonName): return "CN" return "O"
case oid.Equal(oidSerialNumber): return "serialNumber" case oid.Equal(oidOrganizationalUnit):
case oid.Equal(oidLocality): return "L" return "OU"
case oid.Equal(oidProvince): return "ST" case oid.Equal(oidCommonName):
case oid.Equal(oidStreetAddress): return "street" return "CN"
case oid.Equal(oidPostalCode): return "postalCode" 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() return oid.String()
} }
func (rdns RDNSequence) String () string { func (rdns RDNSequence) String() string {
var buf bytes.Buffer var buf bytes.Buffer
for _, rdn := range rdns { for _, rdn := range rdns {
@ -156,7 +165,7 @@ func (rdns RDNSequence) String () string {
return buf.String() return buf.String()
} }
func (san SubjectAltName) String () string { func (san SubjectAltName) String() string {
switch san.Type { switch san.Type {
case sanDNSName: case sanDNSName:
return "DNS:" + string(san.Value) // TODO: escape non-printable characters, '\', and ',' return "DNS:" + string(san.Value) // TODO: escape non-printable characters, '\', and ','
@ -172,7 +181,7 @@ func (san SubjectAltName) String () string {
} }
} }
func ParseTBSCertificate (tbsBytes []byte) (*TBSCertificate, error) { func ParseTBSCertificate(tbsBytes []byte) (*TBSCertificate, error) {
var tbs TBSCertificate var tbs TBSCertificate
if rest, err := asn1.Unmarshal(tbsBytes, &tbs); err != nil { if rest, err := asn1.Unmarshal(tbsBytes, &tbs); err != nil {
return nil, errors.New("failed to parse TBS: " + err.Error()) return nil, errors.New("failed to parse TBS: " + err.Error())
@ -182,10 +191,10 @@ func ParseTBSCertificate (tbsBytes []byte) (*TBSCertificate, error) {
return &tbs, nil return &tbs, nil
} }
func (tbs *TBSCertificate) ParseValidity () (*CertValidity, error) { func (tbs *TBSCertificate) ParseValidity() (*CertValidity, error) {
var rawValidity struct { var rawValidity struct {
NotBefore asn1.RawValue NotBefore asn1.RawValue
NotAfter asn1.RawValue NotAfter asn1.RawValue
} }
if rest, err := asn1.Unmarshal(tbs.Validity.FullBytes, &rawValidity); err != nil { if rest, err := asn1.Unmarshal(tbs.Validity.FullBytes, &rawValidity); err != nil {
return nil, errors.New("failed to parse validity: " + err.Error()) return nil, errors.New("failed to parse validity: " + err.Error())
@ -205,7 +214,7 @@ func (tbs *TBSCertificate) ParseValidity () (*CertValidity, error) {
return &validity, nil return &validity, nil
} }
func (tbs *TBSCertificate) ParseBasicConstraints () (*bool, error) { func (tbs *TBSCertificate) ParseBasicConstraints() (*bool, error) {
isCA := false isCA := false
isNotCA := false isNotCA := false
@ -240,7 +249,7 @@ func (tbs *TBSCertificate) ParseBasicConstraints () (*bool, error) {
} }
} }
func (tbs *TBSCertificate) ParseSerialNumber () (*big.Int, error) { func (tbs *TBSCertificate) ParseSerialNumber() (*big.Int, error) {
serialNumber := big.NewInt(0) serialNumber := big.NewInt(0)
if rest, err := asn1.Unmarshal(tbs.SerialNumber.FullBytes, &serialNumber); err != nil { if rest, err := asn1.Unmarshal(tbs.SerialNumber.FullBytes, &serialNumber); err != nil {
return nil, errors.New("failed to parse serial number: " + err.Error()) return nil, errors.New("failed to parse serial number: " + err.Error())
@ -250,19 +259,19 @@ func (tbs *TBSCertificate) ParseSerialNumber () (*big.Int, error) {
return serialNumber, nil return serialNumber, nil
} }
func (tbs *TBSCertificate) GetRawPublicKey () []byte { func (tbs *TBSCertificate) GetRawPublicKey() []byte {
return tbs.PublicKey.FullBytes return tbs.PublicKey.FullBytes
} }
func (tbs *TBSCertificate) GetRawSubject () []byte { func (tbs *TBSCertificate) GetRawSubject() []byte {
return tbs.Subject.FullBytes return tbs.Subject.FullBytes
} }
func (tbs *TBSCertificate) GetRawIssuer () []byte { func (tbs *TBSCertificate) GetRawIssuer() []byte {
return tbs.Issuer.FullBytes return tbs.Issuer.FullBytes
} }
func (tbs *TBSCertificate) ParseSubject () (RDNSequence, error) { func (tbs *TBSCertificate) ParseSubject() (RDNSequence, error) {
var subject RDNSequence var subject RDNSequence
if rest, err := asn1.Unmarshal(tbs.GetRawSubject(), &subject); err != nil { if rest, err := asn1.Unmarshal(tbs.GetRawSubject(), &subject); err != nil {
return nil, errors.New("failed to parse certificate subject: " + err.Error()) return nil, errors.New("failed to parse certificate subject: " + err.Error())
@ -272,7 +281,7 @@ func (tbs *TBSCertificate) ParseSubject () (RDNSequence, error) {
return subject, nil return subject, nil
} }
func (tbs *TBSCertificate) ParseIssuer () (RDNSequence, error) { func (tbs *TBSCertificate) ParseIssuer() (RDNSequence, error) {
var issuer RDNSequence var issuer RDNSequence
if rest, err := asn1.Unmarshal(tbs.GetRawIssuer(), &issuer); err != nil { if rest, err := asn1.Unmarshal(tbs.GetRawIssuer(), &issuer); err != nil {
return nil, errors.New("failed to parse certificate issuer: " + err.Error()) return nil, errors.New("failed to parse certificate issuer: " + err.Error())
@ -282,7 +291,7 @@ func (tbs *TBSCertificate) ParseIssuer () (RDNSequence, error) {
return issuer, nil return issuer, nil
} }
func (tbs *TBSCertificate) ParseSubjectCommonNames () ([]string, error) { func (tbs *TBSCertificate) ParseSubjectCommonNames() ([]string, error) {
subject, err := tbs.ParseSubject() subject, err := tbs.ParseSubject()
if err != nil { if err != nil {
return nil, err return nil, err
@ -295,7 +304,7 @@ func (tbs *TBSCertificate) ParseSubjectCommonNames () ([]string, error) {
return cns, nil return cns, nil
} }
func (tbs *TBSCertificate) ParseSubjectAltNames () ([]SubjectAltName, error) { func (tbs *TBSCertificate) ParseSubjectAltNames() ([]SubjectAltName, error) {
sans := []SubjectAltName{} sans := []SubjectAltName{}
for _, sanExt := range tbs.GetExtension(oidExtensionSubjectAltName) { for _, sanExt := range tbs.GetExtension(oidExtensionSubjectAltName) {
@ -309,7 +318,7 @@ func (tbs *TBSCertificate) ParseSubjectAltNames () ([]SubjectAltName, error) {
return sans, nil return sans, nil
} }
func (tbs *TBSCertificate) GetExtension (id asn1.ObjectIdentifier) []Extension { func (tbs *TBSCertificate) GetExtension(id asn1.ObjectIdentifier) []Extension {
var exts []Extension var exts []Extension
for _, ext := range tbs.Extensions { for _, ext := range tbs.Extensions {
if ext.Id.Equal(id) { if ext.Id.Equal(id) {
@ -319,8 +328,7 @@ func (tbs *TBSCertificate) GetExtension (id asn1.ObjectIdentifier) []Extension {
return exts return exts
} }
func ParseCertificate(certBytes []byte) (*Certificate, error) {
func ParseCertificate (certBytes []byte) (*Certificate, error) {
var cert Certificate var cert Certificate
if rest, err := asn1.Unmarshal(certBytes, &cert); err != nil { if rest, err := asn1.Unmarshal(certBytes, &cert); err != nil {
return nil, errors.New("failed to parse certificate: " + err.Error()) return nil, errors.New("failed to parse certificate: " + err.Error())
@ -330,15 +338,15 @@ func ParseCertificate (certBytes []byte) (*Certificate, error) {
return &cert, nil return &cert, nil
} }
func (cert *Certificate) GetRawTBSCertificate () []byte { func (cert *Certificate) GetRawTBSCertificate() []byte {
return cert.TBSCertificate.FullBytes return cert.TBSCertificate.FullBytes
} }
func (cert *Certificate) ParseTBSCertificate () (*TBSCertificate, error) { func (cert *Certificate) ParseTBSCertificate() (*TBSCertificate, error) {
return ParseTBSCertificate(cert.GetRawTBSCertificate()) return ParseTBSCertificate(cert.GetRawTBSCertificate())
} }
func parseSANExtension (sans []SubjectAltName, value []byte) ([]SubjectAltName, error) { func parseSANExtension(sans []SubjectAltName, value []byte) ([]SubjectAltName, error) {
var seq asn1.RawValue var seq asn1.RawValue
if rest, err := asn1.Unmarshal(value, &seq); err != nil { if rest, err := asn1.Unmarshal(value, &seq); err != nil {
return nil, errors.New("failed to parse subjectAltName extension: " + err.Error()) return nil, errors.New("failed to parse subjectAltName extension: " + err.Error())
@ -366,4 +374,3 @@ func parseSANExtension (sans []SubjectAltName, value []byte) ([]SubjectAltName,
return sans, nil return sans, nil
} }