Run gofmt
Signed-off-by: Jonathan Rudenberg <jonathan@titanous.com>
This commit is contained in:
parent
38b9c920eb
commit
acc6781f29
17
asn1.go
17
asn1.go
|
@ -10,14 +10,14 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func stringFromByteSlice (chars []byte) string {
|
||||
func stringFromByteSlice(chars []byte) string {
|
||||
runes := make([]rune, len(chars))
|
||||
for i, ch := range chars {
|
||||
runes[i] = rune(ch)
|
||||
|
@ -25,7 +25,7 @@ func stringFromByteSlice (chars []byte) string {
|
|||
return string(runes)
|
||||
}
|
||||
|
||||
func stringFromUint16Slice (chars []uint16) string {
|
||||
func stringFromUint16Slice(chars []uint16) string {
|
||||
runes := make([]rune, len(chars))
|
||||
for i, ch := range chars {
|
||||
runes[i] = rune(ch)
|
||||
|
@ -33,7 +33,7 @@ func stringFromUint16Slice (chars []uint16) string {
|
|||
return string(runes)
|
||||
}
|
||||
|
||||
func stringFromUint32Slice (chars []uint32) string {
|
||||
func stringFromUint32Slice(chars []uint32) string {
|
||||
runes := make([]rune, len(chars))
|
||||
for i, ch := range chars {
|
||||
runes[i] = rune(ch)
|
||||
|
@ -41,7 +41,7 @@ func stringFromUint32Slice (chars []uint32) string {
|
|||
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.Tag == 12 {
|
||||
// UTF8String
|
||||
|
@ -59,14 +59,14 @@ func decodeASN1String (value *asn1.RawValue) (string, error) {
|
|||
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)
|
||||
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)
|
||||
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())
|
||||
}
|
||||
|
@ -75,4 +75,3 @@ func decodeASN1String (value *asn1.RawValue) (string, error) {
|
|||
}
|
||||
return "", errors.New("Not a string")
|
||||
}
|
||||
|
||||
|
|
96
asn1time.go
96
asn1time.go
|
@ -10,22 +10,22 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"time"
|
||||
"strconv"
|
||||
"errors"
|
||||
"unicode"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func isDigit (b byte) bool {
|
||||
func isDigit(b byte) bool {
|
||||
return unicode.IsDigit(rune(b))
|
||||
}
|
||||
|
||||
func bytesToInt (bytes []byte) (int, error) {
|
||||
func bytesToInt(bytes []byte) (int, error) {
|
||||
return strconv.Atoi(string(bytes))
|
||||
}
|
||||
|
||||
func parseUTCTime (bytes []byte) (time.Time, error) {
|
||||
func parseUTCTime(bytes []byte) (time.Time, error) {
|
||||
var err error
|
||||
var year, month, day 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")
|
||||
}
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
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:]
|
||||
|
||||
|
@ -72,12 +82,16 @@ func parseUTCTime (bytes []byte) (time.Time, error) {
|
|||
return time.Time{}, errors.New("UTCTime positive timezone offset is too short")
|
||||
}
|
||||
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])
|
||||
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:]
|
||||
} else if bytes[0] == '-' {
|
||||
// -hhmm
|
||||
|
@ -85,12 +99,16 @@ func parseUTCTime (bytes []byte) (time.Time, error) {
|
|||
return time.Time{}, errors.New("UTCTime negative timezone offset is too short")
|
||||
}
|
||||
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])
|
||||
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:]
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
||||
func parseGeneralizedTime (bytes []byte) (time.Time, error) {
|
||||
func parseGeneralizedTime(bytes []byte) (time.Time, error) {
|
||||
var err error
|
||||
var year, month, day 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")
|
||||
}
|
||||
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])
|
||||
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])
|
||||
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])
|
||||
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:]
|
||||
|
||||
|
@ -174,12 +200,16 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
|
|||
return time.Time{}, errors.New("GeneralizedTime positive timezone offset is too short")
|
||||
}
|
||||
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])
|
||||
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:]
|
||||
} else if bytes[0] == '-' {
|
||||
// -hhmm
|
||||
|
@ -187,12 +217,16 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
|
|||
return time.Time{}, errors.New("GeneralizedTime negative timezone offset is too short")
|
||||
}
|
||||
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])
|
||||
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:]
|
||||
}
|
||||
} else {
|
||||
|
@ -203,10 +237,10 @@ func parseGeneralizedTime (bytes []byte) (time.Time, error) {
|
|||
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.Tag == asn1.TagUTCTime {
|
||||
return parseUTCTime(value.Bytes)
|
||||
|
|
111
asn1time_test.go
111
asn1time_test.go
|
@ -15,31 +15,31 @@ import (
|
|||
)
|
||||
|
||||
type timeTest struct {
|
||||
in string
|
||||
ok bool
|
||||
out time.Time
|
||||
in string
|
||||
ok bool
|
||||
out time.Time
|
||||
}
|
||||
|
||||
var utcTimeTests = []timeTest{
|
||||
{ "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) },
|
||||
{ "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) },
|
||||
{ "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+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))) },
|
||||
{ "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+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))) },
|
||||
{ "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) },
|
||||
{ "", false, time.Time{} },
|
||||
{ "123", false, time.Time{} },
|
||||
{ "150210152542-10", false, time.Time{} },
|
||||
{ "150210152542F", false, time.Time{} },
|
||||
{ "150210152542ZF", false, time.Time{} },
|
||||
{"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)},
|
||||
{"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)},
|
||||
{"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+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)))},
|
||||
{"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+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)))},
|
||||
{"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)},
|
||||
{"", false, time.Time{}},
|
||||
{"123", false, time.Time{}},
|
||||
{"150210152542-10", false, time.Time{}},
|
||||
{"150210152542F", false, time.Time{}},
|
||||
{"150210152542ZF", false, time.Time{}},
|
||||
}
|
||||
|
||||
func TestUTCTime(t *testing.T) {
|
||||
|
@ -62,44 +62,43 @@ func TestUTCTime(t *testing.T) {
|
|||
}
|
||||
|
||||
var generalizedTimeTests = []timeTest{
|
||||
{ "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) },
|
||||
{ "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.12", false, time.Time{} },
|
||||
{ "20150210152542.1", false, time.Time{} },
|
||||
{ "20150210152542.", false, time.Time{} },
|
||||
{"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)},
|
||||
{"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.12", false, time.Time{}},
|
||||
{"20150210152542.1", false, time.Time{}},
|
||||
{"20150210152542.", false, time.Time{}},
|
||||
|
||||
{ "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) },
|
||||
{ "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.12Z", false, time.Time{} },
|
||||
{ "20150210152542.1Z", false, time.Time{} },
|
||||
{ "20150210152542.Z", false, time.Time{} },
|
||||
{"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)},
|
||||
{"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.12Z", false, time.Time{}},
|
||||
{"20150210152542.1Z", 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)) },
|
||||
{ "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.123+1000", true, time.Date(2015, time.February, 10, 15, 25, 42, 123000000, time.FixedZone("", 10 * 3600)) },
|
||||
{ "20150210152542.12+1000", false, time.Time{} },
|
||||
{ "20150210152542.1+1000", false, time.Time{} },
|
||||
{ "20150210152542.+1000", false, time.Time{} },
|
||||
{"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))},
|
||||
{"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.12+1000", false, time.Time{}},
|
||||
{"20150210152542.1+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))) },
|
||||
{ "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.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.1-0835", false, time.Time{} },
|
||||
{ "20150210152542.-0835", false, time.Time{} },
|
||||
{"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)))},
|
||||
{"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.12-0835", false, time.Time{}},
|
||||
{"20150210152542.1-0835", false, time.Time{}},
|
||||
{"20150210152542.-0835", false, time.Time{}},
|
||||
|
||||
|
||||
{ "", false, time.Time{} },
|
||||
{ "123", false, time.Time{} },
|
||||
{ "2015021015+1000Z", false, time.Time{} },
|
||||
{ "2015021015x", false, time.Time{} },
|
||||
{ "201502101525Zf", false, time.Time{} },
|
||||
{"", false, time.Time{}},
|
||||
{"123", false, time.Time{}},
|
||||
{"2015021015+1000Z", false, time.Time{}},
|
||||
{"2015021015x", false, time.Time{}},
|
||||
{"201502101525Zf", false, time.Time{}},
|
||||
}
|
||||
|
||||
func TestGeneralizedTime(t *testing.T) {
|
||||
|
|
26
auditing.go
26
auditing.go
|
@ -10,19 +10,19 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
)
|
||||
|
||||
func reverseHashes (hashes []ct.MerkleTreeNode) {
|
||||
for i := 0; i < len(hashes) / 2; i++ {
|
||||
func reverseHashes(hashes []ct.MerkleTreeNode) {
|
||||
for i := 0; i < len(hashes)/2; i++ {
|
||||
j := len(hashes) - i - 1
|
||||
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 {
|
||||
// Can't be consistent if tree got smaller
|
||||
return false, nil
|
||||
|
@ -46,7 +46,7 @@ func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead
|
|||
lastNode := second.TreeSize - 1
|
||||
|
||||
// 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
|
||||
lastNode /= 2
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead
|
|||
leftHashes = append(leftHashes, newHash)
|
||||
|
||||
for node > 0 {
|
||||
if node % 2 == 1 {
|
||||
if node%2 == 1 {
|
||||
// node is a right child; left sibling exists in both trees
|
||||
if len(proof) == 0 {
|
||||
return false, nil
|
||||
|
@ -112,14 +112,14 @@ func VerifyConsistencyProof (proof ct.ConsistencyProof, first *ct.SignedTreeHead
|
|||
return true, &MerkleTreeBuilder{stack: leftHashes, size: first.TreeSize}
|
||||
}
|
||||
|
||||
func hashLeaf (leafBytes []byte) ct.MerkleTreeNode {
|
||||
func hashLeaf(leafBytes []byte) ct.MerkleTreeNode {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte{0x00})
|
||||
hasher.Write(leafBytes)
|
||||
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.Write([]byte{0x01})
|
||||
hasher.Write(left)
|
||||
|
@ -128,15 +128,15 @@ func hashChildren (left ct.MerkleTreeNode, right ct.MerkleTreeNode) ct.MerkleTre
|
|||
}
|
||||
|
||||
type MerkleTreeBuilder struct {
|
||||
stack []ct.MerkleTreeNode
|
||||
size uint64 // number of hashes added so far
|
||||
stack []ct.MerkleTreeNode
|
||||
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.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]
|
||||
builder.stack = builder.stack[:len(builder.stack)-2]
|
||||
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 {
|
||||
panic("MerkleTreeBuilder.Finish called on an empty tree")
|
||||
}
|
||||
|
|
|
@ -10,29 +10,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"bufio"
|
||||
"strings"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/idna"
|
||||
|
||||
"software.sslmate.com/src/certspotter"
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
"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 != "" {
|
||||
return envVar
|
||||
} else {
|
||||
return cmd.DefaultStateDir("certspotter")
|
||||
}
|
||||
}
|
||||
func defaultConfigDir () string {
|
||||
func defaultConfigDir() string {
|
||||
if envVar := os.Getenv("CERTSPOTTER_CONFIG_DIR"); envVar != "" {
|
||||
return envVar
|
||||
} else {
|
||||
|
@ -40,9 +40,9 @@ func defaultConfigDir () string {
|
|||
}
|
||||
}
|
||||
|
||||
func trimTrailingDots (value string) string {
|
||||
func trimTrailingDots(value string) string {
|
||||
length := len(value)
|
||||
for length > 0 && value[length - 1] == '.' {
|
||||
for length > 0 && value[length-1] == '.' {
|
||||
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)")
|
||||
|
||||
type watchlistItem struct {
|
||||
Domain []string
|
||||
AcceptSuffix bool
|
||||
Domain []string
|
||||
AcceptSuffix bool
|
||||
}
|
||||
|
||||
var watchlist []watchlistItem
|
||||
|
||||
func parseWatchlistItem (str string) (watchlistItem, error) {
|
||||
func parseWatchlistItem(str string) (watchlistItem, error) {
|
||||
if str == "." { // "." as in root zone (matches everything)
|
||||
return watchlistItem{
|
||||
Domain: []string{},
|
||||
Domain: []string{},
|
||||
AcceptSuffix: true,
|
||||
}, nil
|
||||
} else {
|
||||
|
@ -74,13 +75,13 @@ func parseWatchlistItem (str string) (watchlistItem, error) {
|
|||
return watchlistItem{}, fmt.Errorf("Invalid domain `%s': %s", str, err)
|
||||
}
|
||||
return watchlistItem{
|
||||
Domain: strings.Split(asciiDomain, "."),
|
||||
Domain: strings.Split(asciiDomain, "."),
|
||||
AcceptSuffix: acceptSuffix,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func readWatchlist (reader io.Reader) ([]watchlistItem, error) {
|
||||
func readWatchlist(reader io.Reader) ([]watchlistItem, error) {
|
||||
items := []watchlistItem{}
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
|
@ -97,18 +98,18 @@ func readWatchlist (reader io.Reader) ([]watchlistItem, error) {
|
|||
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.
|
||||
// Similarly, redacted labels match everything, since the label _might_ be
|
||||
// for a name we're interested in.
|
||||
|
||||
return certLabel == "*" ||
|
||||
certLabel == "?" ||
|
||||
certLabel == certspotter.UnparsableDNSLabelPlaceholder ||
|
||||
certspotter.MatchesWildcard(watchLabel, certLabel)
|
||||
certLabel == "?" ||
|
||||
certLabel == certspotter.UnparsableDNSLabelPlaceholder ||
|
||||
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 {
|
||||
certLabel := dnsName[len(dnsName)-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)
|
||||
}
|
||||
|
||||
func dnsNameIsWatched (dnsName string) bool {
|
||||
func dnsNameIsWatched(dnsName string) bool {
|
||||
labels := strings.Split(dnsName, ".")
|
||||
for _, item := range watchlist {
|
||||
if dnsNameMatches(labels, item.Domain, item.AcceptSuffix) {
|
||||
|
@ -134,7 +135,7 @@ func dnsNameIsWatched (dnsName string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func anyDnsNameIsWatched (dnsNames []string) bool {
|
||||
func anyDnsNameIsWatched(dnsNames []string) bool {
|
||||
for _, dnsName := range dnsNames {
|
||||
if dnsNameIsWatched(dnsName) {
|
||||
return true
|
||||
|
@ -143,12 +144,12 @@ func anyDnsNameIsWatched (dnsNames []string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func processEntry (scanner *certspotter.Scanner, entry *ct.LogEntry) {
|
||||
func processEntry(scanner *certspotter.Scanner, entry *ct.LogEntry) {
|
||||
info := certspotter.EntryInfo{
|
||||
LogUri: scanner.LogUri,
|
||||
Entry: entry,
|
||||
IsPrecert: certspotter.IsPrecert(entry),
|
||||
FullChain: certspotter.GetFullChain(entry),
|
||||
LogUri: scanner.LogUri,
|
||||
Entry: entry,
|
||||
IsPrecert: certspotter.IsPrecert(entry),
|
||||
FullChain: certspotter.GetFullChain(entry),
|
||||
}
|
||||
|
||||
info.CertInfo, info.ParseError = certspotter.MakeCertInfoFromLogEntry(entry)
|
||||
|
|
|
@ -10,18 +10,18 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"bytes"
|
||||
"os/user"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"software.sslmate.com/src/certspotter"
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
|
@ -39,7 +39,7 @@ var stateDir string
|
|||
|
||||
var printMutex sync.Mutex
|
||||
|
||||
func homedir () string {
|
||||
func homedir() string {
|
||||
home := os.Getenv("HOME")
|
||||
if home != "" {
|
||||
return home
|
||||
|
@ -51,15 +51,15 @@ func homedir () string {
|
|||
panic("Unable to determine home directory")
|
||||
}
|
||||
|
||||
func DefaultStateDir (programName string) string {
|
||||
return filepath.Join(homedir(), "." + programName)
|
||||
func DefaultStateDir(programName string) string {
|
||||
return filepath.Join(homedir(), "."+programName)
|
||||
}
|
||||
|
||||
func DefaultConfigDir (programName string) string {
|
||||
return filepath.Join(homedir(), "." + programName)
|
||||
func DefaultConfigDir(programName string) string {
|
||||
return filepath.Join(homedir(), "."+programName)
|
||||
}
|
||||
|
||||
func LogEntry (info *certspotter.EntryInfo) {
|
||||
func LogEntry(info *certspotter.EntryInfo) {
|
||||
if !*noSave {
|
||||
var alreadyPresent bool
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func saveEvidence (logUri string, firstSTH *ct.SignedTreeHead, secondSTH *ct.Sig
|
|||
return firstFilename, secondFilename, proofFilename, nil
|
||||
}
|
||||
|
||||
func Main (argStateDir string, processCallback certspotter.ProcessCallback) int {
|
||||
func Main(argStateDir string, processCallback certspotter.ProcessCallback) int {
|
||||
stateDir = argStateDir
|
||||
|
||||
var logs []certspotter.LogInfo
|
||||
|
@ -171,9 +171,9 @@ func Main (argStateDir string, processCallback certspotter.ProcessCallback) int
|
|||
}
|
||||
|
||||
opts := certspotter.ScannerOptions{
|
||||
BatchSize: *batchSize,
|
||||
NumWorkers: *numWorkers,
|
||||
Quiet: !*verbose,
|
||||
BatchSize: *batchSize,
|
||||
NumWorkers: *numWorkers,
|
||||
Quiet: !*verbose,
|
||||
}
|
||||
scanner := certspotter.NewScanner(logUri, logKey, &opts)
|
||||
|
||||
|
@ -186,7 +186,7 @@ func Main (argStateDir string, processCallback certspotter.ProcessCallback) int
|
|||
|
||||
if *verbose {
|
||||
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 {
|
||||
log.Printf("new log; scanning all %d entries in the log", latestSTH.TreeSize)
|
||||
} else {
|
||||
|
|
|
@ -14,11 +14,11 @@ import (
|
|||
"os"
|
||||
|
||||
"software.sslmate.com/src/certspotter"
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
"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 != "" {
|
||||
return envVar
|
||||
} else {
|
||||
|
@ -28,12 +28,12 @@ func DefaultStateDir () string {
|
|||
|
||||
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{
|
||||
LogUri: scanner.LogUri,
|
||||
Entry: entry,
|
||||
IsPrecert: certspotter.IsPrecert(entry),
|
||||
FullChain: certspotter.GetFullChain(entry),
|
||||
LogUri: scanner.LogUri,
|
||||
Entry: entry,
|
||||
IsPrecert: certspotter.IsPrecert(entry),
|
||||
FullChain: certspotter.GetFullChain(entry),
|
||||
}
|
||||
|
||||
info.CertInfo, info.ParseError = certspotter.MakeCertInfoFromLogEntry(entry)
|
||||
|
|
|
@ -13,15 +13,15 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
"github.com/mreiferson/go-httpclient"
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
)
|
||||
|
||||
// URI paths for CT Log endpoints
|
||||
const (
|
||||
GetSTHPath = "/ct/v1/get-sth"
|
||||
GetEntriesPath = "/ct/v1/get-entries"
|
||||
GetSTHConsistencyPath = "/ct/v1/get-sth-consistency"
|
||||
GetSTHPath = "/ct/v1/get-sth"
|
||||
GetEntriesPath = "/ct/v1/get-entries"
|
||||
GetSTHConsistencyPath = "/ct/v1/get-sth-consistency"
|
||||
)
|
||||
|
||||
// LogClient represents a client for a given CT Log instance
|
||||
|
@ -84,7 +84,7 @@ func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
|
|||
if err != nil {
|
||||
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)
|
||||
var body []byte
|
||||
if resp != nil {
|
||||
|
@ -97,7 +97,7 @@ func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode / 100 != 2 {
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return fmt.Errorf("GET %s: %s (%s)", uri, resp.Status, string(body))
|
||||
}
|
||||
if err = json.Unmarshal(body, &res); err != nil {
|
||||
|
|
|
@ -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.
|
||||
type LogEntry struct {
|
||||
Index int64
|
||||
Leaf MerkleTreeLeaf
|
||||
Chain []ASN1Cert
|
||||
Index int64
|
||||
Leaf MerkleTreeLeaf
|
||||
Chain []ASN1Cert
|
||||
LeafBytes []byte
|
||||
}
|
||||
|
||||
|
|
160
helpers.go
160
helpers.go
|
@ -10,26 +10,26 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"os"
|
||||
"os/exec"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"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)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
@ -46,7 +46,7 @@ func ReadSTHFile (path string) (*ct.SignedTreeHead, error) {
|
|||
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")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -55,7 +55,7 @@ func WriteSTHFile (path string, sth *ct.SignedTreeHead) error {
|
|||
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")
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -64,12 +64,12 @@ func WriteProofFile (path string, proof ct.ConsistencyProof) error {
|
|||
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
|
||||
}
|
||||
|
||||
func GetFullChain (entry *ct.LogEntry) [][]byte {
|
||||
certs := make([][]byte, 0, len(entry.Chain) + 1)
|
||||
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)
|
||||
|
@ -81,7 +81,7 @@ func GetFullChain (entry *ct.LogEntry) [][]byte {
|
|||
return certs
|
||||
}
|
||||
|
||||
func formatSerialNumber (serial *big.Int) string {
|
||||
func formatSerialNumber(serial *big.Int) string {
|
||||
if serial != nil {
|
||||
return fmt.Sprintf("%x", serial)
|
||||
} 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)
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
func sha256hex (data []byte) string {
|
||||
func sha256hex(data []byte) string {
|
||||
return hex.EncodeToString(sha256sum(data))
|
||||
}
|
||||
|
||||
type EntryInfo struct {
|
||||
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
|
||||
Identifiers *Identifiers
|
||||
IdentifiersParseError error
|
||||
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
|
||||
Identifiers *Identifiers
|
||||
IdentifiersParseError error
|
||||
Filename string
|
||||
}
|
||||
|
||||
type CertInfo struct {
|
||||
TBS *TBSCertificate
|
||||
TBS *TBSCertificate
|
||||
|
||||
Subject RDNSequence
|
||||
SubjectParseError error
|
||||
Issuer RDNSequence
|
||||
IssuerParseError error
|
||||
SANs []SubjectAltName
|
||||
SANsParseError error
|
||||
SerialNumber *big.Int
|
||||
SerialNumberParseError error
|
||||
Validity *CertValidity
|
||||
ValidityParseError error
|
||||
IsCA *bool
|
||||
IsCAParseError error
|
||||
Subject RDNSequence
|
||||
SubjectParseError error
|
||||
Issuer RDNSequence
|
||||
IssuerParseError error
|
||||
SANs []SubjectAltName
|
||||
SANsParseError error
|
||||
SerialNumber *big.Int
|
||||
SerialNumberParseError error
|
||||
Validity *CertValidity
|
||||
ValidityParseError error
|
||||
IsCA *bool
|
||||
IsCAParseError error
|
||||
}
|
||||
|
||||
func MakeCertInfoFromTBS (tbs *TBSCertificate) *CertInfo {
|
||||
func MakeCertInfoFromTBS(tbs *TBSCertificate) *CertInfo {
|
||||
info := &CertInfo{TBS: tbs}
|
||||
|
||||
info.Subject, info.SubjectParseError = tbs.ParseSubject()
|
||||
|
@ -140,7 +140,7 @@ func MakeCertInfoFromTBS (tbs *TBSCertificate) *CertInfo {
|
|||
return info
|
||||
}
|
||||
|
||||
func MakeCertInfoFromRawTBS (tbsBytes []byte) (*CertInfo, error) {
|
||||
func MakeCertInfoFromRawTBS(tbsBytes []byte) (*CertInfo, error) {
|
||||
tbs, err := ParseTBSCertificate(tbsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -148,7 +148,7 @@ func MakeCertInfoFromRawTBS (tbsBytes []byte) (*CertInfo, error) {
|
|||
return MakeCertInfoFromTBS(tbs), nil
|
||||
}
|
||||
|
||||
func MakeCertInfoFromRawCert (certBytes []byte) (*CertInfo, error) {
|
||||
func MakeCertInfoFromRawCert(certBytes []byte) (*CertInfo, error) {
|
||||
cert, err := ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -156,7 +156,7 @@ func MakeCertInfoFromRawCert (certBytes []byte) (*CertInfo, error) {
|
|||
return MakeCertInfoFromRawTBS(cert.GetRawTBSCertificate())
|
||||
}
|
||||
|
||||
func MakeCertInfoFromLogEntry (entry *ct.LogEntry) (*CertInfo, error) {
|
||||
func MakeCertInfoFromLogEntry(entry *ct.LogEntry) (*CertInfo, error) {
|
||||
switch entry.Leaf.TimestampedEntry.EntryType {
|
||||
case ct.X509LogEntryType:
|
||||
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 {
|
||||
return &info.Validity.NotBefore
|
||||
} 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 {
|
||||
return &info.Validity.NotAfter
|
||||
} 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())
|
||||
}
|
||||
|
||||
func (info *CertInfo) PubkeyHashBytes () []byte {
|
||||
func (info *CertInfo) PubkeyHashBytes() []byte {
|
||||
return sha256sum(info.TBS.GetRawPublicKey())
|
||||
}
|
||||
|
||||
func (info *CertInfo) Environ () []string {
|
||||
func (info *CertInfo) Environ() []string {
|
||||
env := make([]string, 0, 10)
|
||||
|
||||
env = append(env, "PUBKEY_HASH=" + info.PubkeyHash())
|
||||
env = append(env, "PUBKEY_HASH="+info.PubkeyHash())
|
||||
|
||||
if info.SerialNumberParseError != nil {
|
||||
env = append(env, "SERIAL_PARSE_ERROR=" + info.SerialNumberParseError.Error())
|
||||
env = append(env, "SERIAL_PARSE_ERROR="+info.SerialNumberParseError.Error())
|
||||
} else {
|
||||
env = append(env, "SERIAL=" + formatSerialNumber(info.SerialNumber))
|
||||
env = append(env, "SERIAL="+formatSerialNumber(info.SerialNumber))
|
||||
}
|
||||
|
||||
if info.ValidityParseError != nil {
|
||||
env = append(env, "VALIDITY_PARSE_ERROR=" + info.ValidityParseError.Error())
|
||||
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))
|
||||
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())
|
||||
env = append(env, "SUBJECT_PARSE_ERROR="+info.SubjectParseError.Error())
|
||||
} else {
|
||||
env = append(env, "SUBJECT_DN=" + info.Subject.String())
|
||||
env = append(env, "SUBJECT_DN="+info.Subject.String())
|
||||
}
|
||||
|
||||
if info.IssuerParseError != nil {
|
||||
env = append(env, "ISSUER_PARSE_ERROR=" + info.IssuerParseError.Error())
|
||||
env = append(env, "ISSUER_PARSE_ERROR="+info.IssuerParseError.Error())
|
||||
} else {
|
||||
env = append(env, "ISSUER_DN=" + info.Issuer.String())
|
||||
env = append(env, "ISSUER_DN="+info.Issuer.String())
|
||||
}
|
||||
|
||||
// TODO: include SANs in environment
|
||||
|
@ -230,7 +230,7 @@ func (info *CertInfo) Environ () []string {
|
|||
return env
|
||||
}
|
||||
|
||||
func (info *EntryInfo) HasParseErrors () bool {
|
||||
func (info *EntryInfo) HasParseErrors() bool {
|
||||
return info.ParseError != nil ||
|
||||
info.IdentifiersParseError != nil ||
|
||||
info.CertInfo.SubjectParseError != nil ||
|
||||
|
@ -241,7 +241,7 @@ func (info *EntryInfo) HasParseErrors () bool {
|
|||
info.CertInfo.IsCAParseError != nil
|
||||
}
|
||||
|
||||
func (info *EntryInfo) Fingerprint () string {
|
||||
func (info *EntryInfo) Fingerprint() string {
|
||||
if len(info.FullChain) > 0 {
|
||||
return sha256hex(info.FullChain[0])
|
||||
} 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 {
|
||||
return sha256sum(info.FullChain[0])
|
||||
} else {
|
||||
|
@ -257,7 +257,7 @@ func (info *EntryInfo) FingerprintBytes () []byte {
|
|||
}
|
||||
}
|
||||
|
||||
func (info *EntryInfo) typeString () string {
|
||||
func (info *EntryInfo) typeString() string {
|
||||
if info.IsPrecert {
|
||||
return "precert"
|
||||
} else {
|
||||
|
@ -265,7 +265,7 @@ func (info *EntryInfo) typeString () string {
|
|||
}
|
||||
}
|
||||
|
||||
func (info *EntryInfo) typeFriendlyString () string {
|
||||
func (info *EntryInfo) typeFriendlyString() string {
|
||||
if info.IsPrecert {
|
||||
return "Pre-certificate"
|
||||
} else {
|
||||
|
@ -273,7 +273,7 @@ func (info *EntryInfo) typeFriendlyString () string {
|
|||
}
|
||||
}
|
||||
|
||||
func yesnoString (value bool) string {
|
||||
func yesnoString(value bool) string {
|
||||
if value {
|
||||
return "yes"
|
||||
} else {
|
||||
|
@ -281,7 +281,7 @@ func yesnoString (value bool) string {
|
|||
}
|
||||
}
|
||||
|
||||
func (info *EntryInfo) Environ () []string {
|
||||
func (info *EntryInfo) Environ() []string {
|
||||
env := []string{
|
||||
"FINGERPRINT=" + info.Fingerprint(),
|
||||
"CERT_TYPE=" + info.typeString(),
|
||||
|
@ -291,25 +291,25 @@ func (info *EntryInfo) Environ () []string {
|
|||
}
|
||||
|
||||
if info.Filename != "" {
|
||||
env = append(env, "CERT_FILENAME=" + info.Filename)
|
||||
env = append(env, "CERT_FILENAME="+info.Filename)
|
||||
}
|
||||
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 {
|
||||
certEnv := info.CertInfo.Environ()
|
||||
env = append(env, certEnv...)
|
||||
}
|
||||
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 {
|
||||
env = append(env, "DNS_NAMES=" + info.Identifiers.dnsNamesString(","))
|
||||
env = append(env, "IP_ADDRESSES=" + info.Identifiers.ipAddrsString(","))
|
||||
env = append(env, "DNS_NAMES="+info.Identifiers.dnsNamesString(","))
|
||||
env = append(env, "IP_ADDRESSES="+info.Identifiers.ipAddrsString(","))
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Fprintf(out, "\t%13s = %s\n", name, value)
|
||||
} 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()
|
||||
fmt.Fprintf(out, "%s:\n", fingerprint)
|
||||
if info.IdentifiersParseError != nil {
|
||||
|
@ -331,7 +331,7 @@ func (info *EntryInfo) Write (out io.Writer) {
|
|||
}
|
||||
}
|
||||
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 {
|
||||
writeField(out, "Pubkey", info.CertInfo.PubkeyHash(), nil)
|
||||
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, "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 != "" {
|
||||
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.Env = os.Environ()
|
||||
infoEnv := info.Environ()
|
||||
|
@ -362,7 +362,7 @@ func (info *EntryInfo) InvokeHookScript (command string) error {
|
|||
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 {
|
||||
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) {
|
||||
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)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
|
@ -400,7 +400,7 @@ func WriteCertRepository (repoPath string, isPrecert bool, certs [][]byte) (bool
|
|||
return false, path, nil
|
||||
}
|
||||
|
||||
func MatchesWildcard (dnsName string, pattern string) bool {
|
||||
func MatchesWildcard(dnsName string, pattern string) bool {
|
||||
for len(pattern) > 0 {
|
||||
if pattern[0] == '*' {
|
||||
if len(dnsName) > 0 && dnsName[0] != '.' && MatchesWildcard(dnsName[1:], pattern) {
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"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 {
|
||||
t.Errorf("MatchesWildcard(%q, %q) != %v", dnsName, wildcard, expected)
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ package certspotter
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"net"
|
||||
"unicode/utf8"
|
||||
"golang.org/x/net/idna"
|
||||
"net"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const UnparsableDNSLabelPlaceholder = "<unparsable>"
|
||||
|
@ -34,24 +34,24 @@ type UnknownIdentifier struct {
|
|||
*/
|
||||
|
||||
type Identifiers struct {
|
||||
DNSNames []string // stored as ASCII, with IDNs in Punycode
|
||||
IPAddrs []net.IP
|
||||
DNSNames []string // stored as ASCII, with IDNs in Punycode
|
||||
IPAddrs []net.IP
|
||||
//Unknowns []UnknownIdentifier
|
||||
}
|
||||
|
||||
func NewIdentifiers () *Identifiers {
|
||||
func NewIdentifiers() *Identifiers {
|
||||
return &Identifiers{
|
||||
DNSNames: []string{},
|
||||
IPAddrs: []net.IP{},
|
||||
DNSNames: []string{},
|
||||
IPAddrs: []net.IP{},
|
||||
//Unknowns: []UnknownIdentifier{},
|
||||
}
|
||||
}
|
||||
|
||||
func parseIPAddrString (str string) net.IP {
|
||||
func parseIPAddrString(str string) net.IP {
|
||||
return net.ParseIP(str)
|
||||
}
|
||||
|
||||
func isASCIIString (value []byte) bool {
|
||||
func isASCIIString(value []byte) bool {
|
||||
for _, b := range value {
|
||||
if b > 127 {
|
||||
return false
|
||||
|
@ -59,10 +59,10 @@ func isASCIIString (value []byte) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
func isUTF8String (value []byte) bool {
|
||||
func isUTF8String(value []byte) bool {
|
||||
return utf8.Valid(value)
|
||||
}
|
||||
func latin1ToUTF8 (value []byte) string {
|
||||
func latin1ToUTF8(value []byte) string {
|
||||
runes := make([]rune, len(value))
|
||||
for i, b := range value {
|
||||
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
|
||||
// could cause trouble during later processing.
|
||||
func isSaneDNSLabelChar (ch rune) bool {
|
||||
func isSaneDNSLabelChar(ch rune) bool {
|
||||
return ch == '\t' || (ch >= 32 && ch <= 126)
|
||||
}
|
||||
func isSaneDNSLabel (label string) bool {
|
||||
func isSaneDNSLabel(label string) bool {
|
||||
for _, ch := range label {
|
||||
if !isSaneDNSLabelChar(ch) {
|
||||
return false
|
||||
|
@ -84,7 +84,7 @@ func isSaneDNSLabel (label string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func trimHttpPrefixString (value string) string {
|
||||
func trimHttpPrefixString(value string) string {
|
||||
if strings.HasPrefix(value, "http://") {
|
||||
return value[7:]
|
||||
} 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://")) {
|
||||
return value[7:]
|
||||
} 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)
|
||||
for length > 0 && value[length - 1] == '.' {
|
||||
for length > 0 && value[length-1] == '.' {
|
||||
length--
|
||||
}
|
||||
return value[0:length]
|
||||
|
@ -117,7 +117,7 @@ func trimTrailingDots (value string) string {
|
|||
// 2. Trim trailing dots
|
||||
// 3. Convert to lower case
|
||||
// 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)))
|
||||
labels := strings.Split(value, ".")
|
||||
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.
|
||||
func sanitizeUnicodeDNSName (value string) string {
|
||||
func sanitizeUnicodeDNSName(value string) string {
|
||||
value = strings.ToLower(trimTrailingDots(strings.TrimSpace(value)))
|
||||
labels := strings.Split(value, ".")
|
||||
for i, label := range labels {
|
||||
|
@ -142,13 +142,13 @@ func sanitizeUnicodeDNSName (value string) string {
|
|||
return strings.Join(labels, ".")
|
||||
}
|
||||
|
||||
func (ids *Identifiers) appendDNSName (dnsName string) {
|
||||
func (ids *Identifiers) appendDNSName(dnsName string) {
|
||||
if 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 {
|
||||
// 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
|
||||
|
@ -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 the value contains a slash, then this might be a URL,
|
||||
// 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)
|
||||
}
|
||||
|
||||
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,
|
||||
// so http://example.com becomes just example.com. Even though clients
|
||||
// should never successfully validate a DNS name like http://example.com,
|
||||
|
@ -198,7 +198,7 @@ func (ids *Identifiers) AddDnsSAN (value []byte) {
|
|||
ids.addDnsSANnonull(value)
|
||||
}
|
||||
|
||||
func (ids *Identifiers) addCNfinal (value string) {
|
||||
func (ids *Identifiers) addCNfinal(value string) {
|
||||
if ipaddr := parseIPAddrString(value); ipaddr != nil {
|
||||
ids.IPAddrs = append(ids.IPAddrs, ipaddr)
|
||||
} 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 the value contains a slash, then this might be a URL,
|
||||
// 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)
|
||||
}
|
||||
|
||||
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,
|
||||
// so http://example.com becomes just example.com. Even though clients
|
||||
// should never successfully validate a DNS name like http://example.com,
|
||||
|
@ -236,15 +236,15 @@ func (ids *Identifiers) AddCN (value string) {
|
|||
ids.addCNnonull(value)
|
||||
}
|
||||
|
||||
func (ids *Identifiers) AddIPAddress (value net.IP) {
|
||||
func (ids *Identifiers) AddIPAddress(value net.IP) {
|
||||
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)
|
||||
}
|
||||
|
||||
func (ids *Identifiers) ipAddrsString (sep string) string {
|
||||
func (ids *Identifiers) ipAddrsString(sep string) string {
|
||||
str := ""
|
||||
for _, ipAddr := range ids.IPAddrs {
|
||||
if str != "" {
|
||||
|
@ -255,7 +255,7 @@ func (ids *Identifiers) ipAddrsString (sep string) string {
|
|||
return str
|
||||
}
|
||||
|
||||
func (cert *CertInfo) ParseIdentifiers () (*Identifiers, error) {
|
||||
func (cert *CertInfo) ParseIdentifiers() (*Identifiers, error) {
|
||||
ids := NewIdentifiers()
|
||||
|
||||
if cert.SubjectParseError != nil {
|
||||
|
|
24
logs.go
24
logs.go
|
@ -10,26 +10,26 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
type LogInfoFile struct {
|
||||
Logs []LogInfo `json:"logs"`
|
||||
Logs []LogInfo `json:"logs"`
|
||||
}
|
||||
type LogInfo struct {
|
||||
Description string `json:"description"`
|
||||
Key []byte `json:"key"`
|
||||
Url string `json:"url"`
|
||||
MMD int `json:"maximum_merge_delay"`
|
||||
Description string `json:"description"`
|
||||
Key []byte `json:"key"`
|
||||
Url string `json:"url"`
|
||||
MMD int `json:"maximum_merge_delay"`
|
||||
}
|
||||
|
||||
func (info *LogInfo) FullURI () string {
|
||||
func (info *LogInfo) FullURI() string {
|
||||
return "https://" + info.Url
|
||||
}
|
||||
|
||||
func (info *LogInfo) ParsedPublicKey () (crypto.PublicKey, error) {
|
||||
func (info *LogInfo) ParsedPublicKey() (crypto.PublicKey, error) {
|
||||
if info.Key != nil {
|
||||
return x509.ParsePKIXPublicKey(info.Key)
|
||||
} else {
|
||||
|
@ -92,13 +92,13 @@ var DefaultLogs = []LogInfo{
|
|||
var UnderwaterLogs = []LogInfo{
|
||||
{
|
||||
Description: "Google 'Submariner' log",
|
||||
Key: mustDecodeBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOfifIGLUV1Voou9JLfA5LZreRLSUMOCeeic8q3Dw0fpRkGMWV0Gtq20fgHQweQJeLVmEByQj9p81uIW4QkWkTw=="),
|
||||
Url: "ct.googleapis.com/submariner",
|
||||
MMD: 86400,
|
||||
Key: mustDecodeBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOfifIGLUV1Voou9JLfA5LZreRLSUMOCeeic8q3Dw0fpRkGMWV0Gtq20fgHQweQJeLVmEByQj9p81uIW4QkWkTw=="),
|
||||
Url: "ct.googleapis.com/submariner",
|
||||
MMD: 86400,
|
||||
},
|
||||
}
|
||||
|
||||
func mustDecodeBase64 (str string) []byte {
|
||||
func mustDecodeBase64(str string) []byte {
|
||||
bytes, err := base64.StdEncoding.DecodeString(str)
|
||||
if err != nil {
|
||||
panic("MustDecodeBase64: " + err.Error())
|
||||
|
|
37
precerts.go
37
precerts.go
|
@ -10,22 +10,23 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"bytes"
|
||||
"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)
|
||||
}
|
||||
|
||||
var (
|
||||
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
|
||||
oidExtensionSCT = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
|
||||
oidExtensionCTPoison = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
|
||||
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
|
||||
oidExtensionSCT = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.New("failed to parse pre-certificate: " + err.Error())
|
||||
|
@ -116,18 +117,18 @@ func ValidatePrecert (precertBytes []byte, tbsBytes []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
func ReconstructPrecertTBS (tbs *TBSCertificate) (*TBSCertificate, error) {
|
||||
func ReconstructPrecertTBS(tbs *TBSCertificate) (*TBSCertificate, error) {
|
||||
precertTBS := TBSCertificate{
|
||||
Version: tbs.Version,
|
||||
SerialNumber: tbs.SerialNumber,
|
||||
SignatureAlgorithm: tbs.SignatureAlgorithm,
|
||||
Issuer: tbs.Issuer,
|
||||
Validity: tbs.Validity,
|
||||
Subject: tbs.Subject,
|
||||
PublicKey: tbs.PublicKey,
|
||||
UniqueId: tbs.UniqueId,
|
||||
SubjectUniqueId: tbs.SubjectUniqueId,
|
||||
Extensions: make([]Extension, 0, len(tbs.Extensions)),
|
||||
Version: tbs.Version,
|
||||
SerialNumber: tbs.SerialNumber,
|
||||
SignatureAlgorithm: tbs.SignatureAlgorithm,
|
||||
Issuer: tbs.Issuer,
|
||||
Validity: tbs.Validity,
|
||||
Subject: tbs.Subject,
|
||||
PublicKey: tbs.PublicKey,
|
||||
UniqueId: tbs.UniqueId,
|
||||
SubjectUniqueId: tbs.SubjectUniqueId,
|
||||
Extensions: make([]Extension, 0, len(tbs.Extensions)),
|
||||
}
|
||||
|
||||
for _, ext := range tbs.Extensions {
|
||||
|
|
26
scanner.go
26
scanner.go
|
@ -13,14 +13,14 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
// "container/list"
|
||||
// "container/list"
|
||||
"crypto"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"crypto"
|
||||
"errors"
|
||||
|
||||
"software.sslmate.com/src/certspotter/ct"
|
||||
"software.sslmate.com/src/certspotter/ct/client"
|
||||
|
@ -29,7 +29,7 @@ import (
|
|||
type ProcessCallback func(*Scanner, *ct.LogEntry)
|
||||
|
||||
const (
|
||||
FETCH_RETRIES = 10
|
||||
FETCH_RETRIES = 10
|
||||
FETCH_RETRY_WAIT = 1
|
||||
)
|
||||
|
||||
|
@ -48,28 +48,28 @@ type ScannerOptions struct {
|
|||
// Creates a new ScannerOptions struct with sensible defaults
|
||||
func DefaultScannerOptions() *ScannerOptions {
|
||||
return &ScannerOptions{
|
||||
BatchSize: 1000,
|
||||
NumWorkers: 1,
|
||||
Quiet: false,
|
||||
BatchSize: 1000,
|
||||
NumWorkers: 1,
|
||||
Quiet: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Scanner is a tool to scan all the entries in a CT Log.
|
||||
type Scanner struct {
|
||||
// Base URI of CT log
|
||||
LogUri string
|
||||
LogUri string
|
||||
|
||||
// Public key of the log
|
||||
publicKey crypto.PublicKey
|
||||
publicKey crypto.PublicKey
|
||||
|
||||
// Client used to talk to the CT log instance
|
||||
logClient *client.LogClient
|
||||
logClient *client.LogClient
|
||||
|
||||
// Configuration options for this Scanner instance
|
||||
opts ScannerOptions
|
||||
opts ScannerOptions
|
||||
|
||||
// Stats
|
||||
certsProcessed int64
|
||||
certsProcessed int64
|
||||
}
|
||||
|
||||
// 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 {
|
||||
s.Log("Starting scan...");
|
||||
s.Log("Starting scan...")
|
||||
|
||||
s.certsProcessed = 0
|
||||
startTime := time.Now()
|
||||
|
|
173
x509.go
173
x509.go
|
@ -10,93 +10,93 @@
|
|||
package certspotter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"errors"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
"net"
|
||||
"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}
|
||||
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
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
}
|
||||
|
||||
type basicConstraints struct {
|
||||
IsCA bool `asn1:"optional"`
|
||||
MaxPathLen int `asn1:"optional,default:-1"`
|
||||
IsCA bool `asn1:"optional"`
|
||||
MaxPathLen int `asn1:"optional,default:-1"`
|
||||
}
|
||||
|
||||
type Extension struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
Critical bool `asn1:"optional"`
|
||||
Value []byte
|
||||
Id asn1.ObjectIdentifier
|
||||
Critical bool `asn1:"optional"`
|
||||
Value []byte
|
||||
}
|
||||
|
||||
const (
|
||||
sanOtherName = 0
|
||||
sanRfc822Name = 1
|
||||
sanDNSName = 2
|
||||
sanX400Address = 3
|
||||
sanDirectoryName = 4
|
||||
sanEdiPartyName = 5
|
||||
sanURI = 6
|
||||
sanIPAddress = 7
|
||||
sanRegisteredID = 8
|
||||
sanOtherName = 0
|
||||
sanRfc822Name = 1
|
||||
sanDNSName = 2
|
||||
sanX400Address = 3
|
||||
sanDirectoryName = 4
|
||||
sanEdiPartyName = 5
|
||||
sanURI = 6
|
||||
sanIPAddress = 7
|
||||
sanRegisteredID = 8
|
||||
)
|
||||
|
||||
type SubjectAltName struct {
|
||||
Type int
|
||||
Value []byte
|
||||
Type int
|
||||
Value []byte
|
||||
}
|
||||
|
||||
type RDNSequence []RelativeDistinguishedNameSET
|
||||
type RelativeDistinguishedNameSET []AttributeTypeAndValue
|
||||
type AttributeTypeAndValue struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value asn1.RawValue
|
||||
Type asn1.ObjectIdentifier
|
||||
Value asn1.RawValue
|
||||
}
|
||||
|
||||
type TBSCertificate struct {
|
||||
Raw asn1.RawContent
|
||||
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"`
|
||||
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
|
||||
Raw asn1.RawContent
|
||||
|
||||
TBSCertificate asn1.RawValue
|
||||
SignatureAlgorithm asn1.RawValue
|
||||
SignatureValue asn1.RawValue
|
||||
TBSCertificate asn1.RawValue
|
||||
SignatureAlgorithm asn1.RawValue
|
||||
SignatureValue asn1.RawValue
|
||||
}
|
||||
|
||||
|
||||
func (rdns RDNSequence) ParseCNs () ([]string, error) {
|
||||
func (rdns RDNSequence) ParseCNs() ([]string, error) {
|
||||
var cns []string
|
||||
|
||||
for _, rdn := range rdns {
|
||||
|
@ -116,22 +116,31 @@ func (rdns RDNSequence) ParseCNs () ([]string, error) {
|
|||
return cns, nil
|
||||
}
|
||||
|
||||
func rdnLabel (oid asn1.ObjectIdentifier) string {
|
||||
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"
|
||||
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 {
|
||||
func (rdns RDNSequence) String() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, rdn := range rdns {
|
||||
|
@ -156,7 +165,7 @@ func (rdns RDNSequence) String () string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
func (san SubjectAltName) String () string {
|
||||
func (san SubjectAltName) String() string {
|
||||
switch san.Type {
|
||||
case sanDNSName:
|
||||
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
|
||||
if rest, err := asn1.Unmarshal(tbsBytes, &tbs); err != nil {
|
||||
return nil, errors.New("failed to parse TBS: " + err.Error())
|
||||
|
@ -182,10 +191,10 @@ func ParseTBSCertificate (tbsBytes []byte) (*TBSCertificate, error) {
|
|||
return &tbs, nil
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) ParseValidity () (*CertValidity, error) {
|
||||
func (tbs *TBSCertificate) ParseValidity() (*CertValidity, error) {
|
||||
var rawValidity struct {
|
||||
NotBefore asn1.RawValue
|
||||
NotAfter asn1.RawValue
|
||||
NotBefore asn1.RawValue
|
||||
NotAfter asn1.RawValue
|
||||
}
|
||||
if rest, err := asn1.Unmarshal(tbs.Validity.FullBytes, &rawValidity); err != nil {
|
||||
return nil, errors.New("failed to parse validity: " + err.Error())
|
||||
|
@ -205,7 +214,7 @@ func (tbs *TBSCertificate) ParseValidity () (*CertValidity, error) {
|
|||
return &validity, nil
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) ParseBasicConstraints () (*bool, error) {
|
||||
func (tbs *TBSCertificate) ParseBasicConstraints() (*bool, error) {
|
||||
isCA := 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)
|
||||
if rest, err := asn1.Unmarshal(tbs.SerialNumber.FullBytes, &serialNumber); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) GetRawPublicKey () []byte {
|
||||
func (tbs *TBSCertificate) GetRawPublicKey() []byte {
|
||||
return tbs.PublicKey.FullBytes
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) GetRawSubject () []byte {
|
||||
func (tbs *TBSCertificate) GetRawSubject() []byte {
|
||||
return tbs.Subject.FullBytes
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) GetRawIssuer () []byte {
|
||||
func (tbs *TBSCertificate) GetRawIssuer() []byte {
|
||||
return tbs.Issuer.FullBytes
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) ParseSubject () (RDNSequence, error) {
|
||||
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())
|
||||
|
@ -272,7 +281,7 @@ func (tbs *TBSCertificate) ParseSubject () (RDNSequence, error) {
|
|||
return subject, nil
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) ParseIssuer () (RDNSequence, error) {
|
||||
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())
|
||||
|
@ -282,7 +291,7 @@ func (tbs *TBSCertificate) ParseIssuer () (RDNSequence, error) {
|
|||
return issuer, nil
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) ParseSubjectCommonNames () ([]string, error) {
|
||||
func (tbs *TBSCertificate) ParseSubjectCommonNames() ([]string, error) {
|
||||
subject, err := tbs.ParseSubject()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -295,7 +304,7 @@ func (tbs *TBSCertificate) ParseSubjectCommonNames () ([]string, error) {
|
|||
return cns, nil
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) ParseSubjectAltNames () ([]SubjectAltName, error) {
|
||||
func (tbs *TBSCertificate) ParseSubjectAltNames() ([]SubjectAltName, error) {
|
||||
sans := []SubjectAltName{}
|
||||
|
||||
for _, sanExt := range tbs.GetExtension(oidExtensionSubjectAltName) {
|
||||
|
@ -309,7 +318,7 @@ func (tbs *TBSCertificate) ParseSubjectAltNames () ([]SubjectAltName, error) {
|
|||
return sans, nil
|
||||
}
|
||||
|
||||
func (tbs *TBSCertificate) GetExtension (id asn1.ObjectIdentifier) []Extension {
|
||||
func (tbs *TBSCertificate) GetExtension(id asn1.ObjectIdentifier) []Extension {
|
||||
var exts []Extension
|
||||
for _, ext := range tbs.Extensions {
|
||||
if ext.Id.Equal(id) {
|
||||
|
@ -319,8 +328,7 @@ func (tbs *TBSCertificate) GetExtension (id asn1.ObjectIdentifier) []Extension {
|
|||
return exts
|
||||
}
|
||||
|
||||
|
||||
func ParseCertificate (certBytes []byte) (*Certificate, error) {
|
||||
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())
|
||||
|
@ -330,15 +338,15 @@ func ParseCertificate (certBytes []byte) (*Certificate, error) {
|
|||
return &cert, nil
|
||||
}
|
||||
|
||||
func (cert *Certificate) GetRawTBSCertificate () []byte {
|
||||
func (cert *Certificate) GetRawTBSCertificate() []byte {
|
||||
return cert.TBSCertificate.FullBytes
|
||||
}
|
||||
|
||||
func (cert *Certificate) ParseTBSCertificate () (*TBSCertificate, error) {
|
||||
func (cert *Certificate) ParseTBSCertificate() (*TBSCertificate, error) {
|
||||
return ParseTBSCertificate(cert.GetRawTBSCertificate())
|
||||
}
|
||||
|
||||
func parseSANExtension (sans []SubjectAltName, value []byte) ([]SubjectAltName, error) {
|
||||
func parseSANExtension(sans []SubjectAltName, value []byte) ([]SubjectAltName, error) {
|
||||
var seq asn1.RawValue
|
||||
if rest, err := asn1.Unmarshal(value, &seq); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue