break timestamp stuff into its own package

This commit is contained in:
Ben Toews 2018-05-10 13:41:02 -06:00
Родитель c546472472
Коммит 84daa31972
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
7 изменённых файлов: 723 добавлений и 612 удалений

Просмотреть файл

@ -8,7 +8,7 @@ import (
var encodeIndent = 0
type asn1Object interface {
EncodeTo(writer *bytes.Buffer) error
encodeTo(writer *bytes.Buffer) error
}
type asn1Structured struct {
@ -16,12 +16,12 @@ type asn1Structured struct {
content []asn1Object
}
func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
func (s asn1Structured) encodeTo(out *bytes.Buffer) error {
//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
encodeIndent++
inner := new(bytes.Buffer)
for _, obj := range s.content {
err := obj.EncodeTo(inner)
err := obj.encodeTo(inner)
if err != nil {
return err
}
@ -39,7 +39,7 @@ type asn1Primitive struct {
content []byte
}
func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
func (p asn1Primitive) encodeTo(out *bytes.Buffer) error {
_, err := out.Write(p.tagBytes)
if err != nil {
return err
@ -54,7 +54,8 @@ func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
return nil
}
func ber2der(ber []byte) ([]byte, error) {
// BER2DER attempts to convert BER encoded data to DER encoding.
func BER2DER(ber []byte) ([]byte, error) {
if len(ber) == 0 {
return nil, errors.New("ber2der: input ber is empty")
}
@ -65,7 +66,7 @@ func ber2der(ber []byte) ([]byte, error) {
if err != nil {
return nil, err
}
obj.EncodeTo(out)
obj.encodeTo(out)
// if offset < len(ber) {
// return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))

Просмотреть файл

@ -11,7 +11,7 @@ func TestBer2Der(t *testing.T) {
// indefinite length fixture
ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00}
expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01}
der, err := ber2der(ber)
der, err := BER2DER(ber)
if err != nil {
t.Fatalf("ber2der failed with error: %v", err)
}
@ -19,7 +19,7 @@ func TestBer2Der(t *testing.T) {
t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der)
}
if der2, err := ber2der(der); err != nil {
if der2, err := BER2DER(der); err != nil {
t.Errorf("ber2der on DER bytes failed with error: %v", err)
} else {
if !bytes.Equal(der, der2) {
@ -50,7 +50,7 @@ func TestBer2Der_Negatives(t *testing.T) {
}
for _, fixture := range fixtures {
_, err := ber2der(fixture.Input)
_, err := BER2DER(fixture.Input)
if err == nil {
t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains)
}
@ -65,7 +65,7 @@ func TestBer2Der_NestedMultipleIndefinite(t *testing.T) {
ber := []byte{0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00}
expected := []byte{0x30, 0x0A, 0x30, 0x03, 0x02, 0x01, 0x01, 0x30, 0x03, 0x02, 0x01, 0x02}
der, err := ber2der(ber)
der, err := BER2DER(ber)
if err != nil {
t.Fatalf("ber2der failed with error: %v", err)
}
@ -73,7 +73,7 @@ func TestBer2Der_NestedMultipleIndefinite(t *testing.T) {
t.Errorf("ber2der result did not match.\n\tExpected: % X\n\tActual: % X", expected, der)
}
if der2, err := ber2der(der); err != nil {
if der2, err := BER2DER(der); err != nil {
t.Errorf("ber2der on DER bytes failed with error: %v", err)
} else {
if !bytes.Equal(der, der2) {

Просмотреть файл

@ -45,7 +45,7 @@ type ContentInfo struct {
// ParseContentInfo parses a top-level ContentInfo type from BER encoded data.
func ParseContentInfo(ber []byte) (ci ContentInfo, err error) {
var der []byte
if der, err = ber2der(ber); err != nil {
if der, err = BER2DER(ber); err != nil {
return
}
@ -92,17 +92,6 @@ func NewDataEncapsulatedContentInfo(data []byte) (EncapsulatedContentInfo, error
return NewEncapsulatedContentInfo(data, oid.Data)
}
// NewTSTInfoEncapsulatedContentInfo creates a new EncapsulatedContentInfo of
// type id-ct-TSTInfo.
func NewTSTInfoEncapsulatedContentInfo(tsti *TSTInfo) (EncapsulatedContentInfo, error) {
content, err := asn1.Marshal(tsti)
if err != nil {
return EncapsulatedContentInfo{}, err
}
return NewEncapsulatedContentInfo(content, oid.TSTInfo)
}
// NewEncapsulatedContentInfo creates a new EncapsulatedContentInfo.
func NewEncapsulatedContentInfo(content []byte, contentType asn1.ObjectIdentifier) (EncapsulatedContentInfo, error) {
octets, err := asn1.Marshal(asn1.RawValue{
@ -191,35 +180,6 @@ func (eci EncapsulatedContentInfo) DataEContent() ([]byte, error) {
return eci.EContentValue()
}
// IsTypeTSTInfo checks if the EContentType is id-ct-TSTInfo.
func (eci EncapsulatedContentInfo) IsTypeTSTInfo() bool {
return eci.EContentType.Equal(oid.TSTInfo)
}
// TSTInfoEContent gets the EContent assuming EContentType is TSTInfo (RFC3161).
func (eci EncapsulatedContentInfo) TSTInfoEContent() (*TSTInfo, error) {
if !eci.EContentType.Equal(oid.TSTInfo) {
return nil, ErrWrongType
}
ecval, err := eci.EContentValue()
if err != nil {
return nil, err
}
if ecval == nil {
return nil, errors.New("missing EContent for non data type")
}
tsti := TSTInfo{}
if rest, err := asn1.Unmarshal(ecval, &tsti); err != nil {
return nil, err
} else if len(rest) > 0 {
return nil, errors.New("unexpected trailing data")
}
return &tsti, nil
}
// Attribute ::= SEQUENCE {
// attrType OBJECT IDENTIFIER,
// attrValues SET OF AttributeValue }

Просмотреть файл

@ -2,13 +2,11 @@ package protocol
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"io"
"math/big"
"strings"
"testing"
"time"
@ -261,26 +259,6 @@ func TestParseSignautreOutlookDetached(t *testing.T) {
testParseContentInfo(t, fixtureSignatureOutlookDetached)
}
func TestParseTimestampSymantec(t *testing.T) {
testParseContentInfo(t, mustExtractTimeStampToken(fixtureTimestampSymantec))
}
func TestParseTimestampSymantecWithCerts(t *testing.T) {
testParseContentInfo(t, mustExtractTimeStampToken(fixtureTimestampSymantecWithCerts))
}
func TestParseTimestampDigicert(t *testing.T) {
testParseContentInfo(t, mustExtractTimeStampToken(fixtureTimestampDigicert))
}
func TestParseTimestampComodo(t *testing.T) {
testParseContentInfo(t, mustExtractTimeStampToken(fixtureTimestampComodo))
}
func TestParseTimestampGlobalSign(t *testing.T) {
testParseContentInfo(t, mustExtractTimeStampToken(fixtureTimestampGlobalSign))
}
func testParseContentInfo(t *testing.T, ber []byte) {
ci, err := ParseContentInfo(ber)
if err != nil {
@ -297,48 +275,20 @@ func testParseContentInfo(t *testing.T, ber []byte) {
t.Fatal(err)
}
if sd.EncapContentInfo.IsTypeData() {
if !sd.EncapContentInfo.EContentType.Equal(oid.Data) {
t.Fatalf("expected %s content, got %s", oid.Data.String(), sd.EncapContentInfo.EContentType.String())
}
if !sd.EncapContentInfo.IsTypeData() {
t.Fatal("expected id-data econtent")
}
data, err := sd.EncapContentInfo.DataEContent()
if err != nil {
t.Fatal(err)
}
if data != nil && len(data) == 0 {
t.Fatal("attached signature with zero length data")
}
} else if sd.EncapContentInfo.IsTypeTSTInfo() {
if !sd.EncapContentInfo.EContentType.Equal(oid.TSTInfo) {
t.Fatalf("expected %s content, got %s", oid.TSTInfo.String(), sd.EncapContentInfo.EContentType.String())
}
if !sd.EncapContentInfo.EContentType.Equal(oid.Data) {
t.Fatalf("expected %s content, got %s", oid.Data.String(), sd.EncapContentInfo.EContentType.String())
}
tsti, err := sd.EncapContentInfo.TSTInfoEContent()
if err != nil {
t.Fatal(err)
}
hash, err := tsti.MessageImprint.Hash()
if err != nil {
t.Fatal(err)
}
if hash != crypto.SHA256 {
t.Fatalf("expected SHA256 hash, found %s", tsti.MessageImprint.HashAlgorithm.Algorithm.String())
}
if tsti.SerialNumber == nil {
t.Fatal("expected non-nill SN")
}
if tsti.SerialNumber.Cmp(big.NewInt(0)) <= 0 {
t.Fatal("expected SN>0")
}
if tsti.Version != 1 {
t.Fatalf("expected tst v1, found %d", tsti.Version)
}
} else {
t.Fatalf("unknown econtenttype: %s", sd.EncapContentInfo.EContentType.String())
data, err := sd.EncapContentInfo.DataEContent()
if err != nil {
t.Fatal(err)
}
if data != nil && len(data) == 0 {
t.Fatal("attached signature with zero length data")
}
for _, si := range sd.SignerInfos {
@ -375,7 +325,7 @@ func testParseContentInfo(t *testing.T, ber []byte) {
}
// round trip contentInfo
der, err := ber2der(ber)
der, err := BER2DER(ber)
if err != nil {
t.Fatal(err)
}
@ -676,193 +626,6 @@ var fixturePFX = mustBase64Decode("" +
"AwIaBQAEFDPX7JM9l8ZnTwGGaDQQvlp7RiBKBAg2WsoFwawSzwICCAA=",
)
var fixtureTimestampSymantec = mustBase64Decode("" +
"MIIDnjADAgEAMIIDlQYJKoZIhvcNAQcCoIIDhjCCA4ICAQMxDTALBglghkgBZQMEAgEwggEOBgsqhkiG" +
"9w0BCRABBKCB/gSB+zCB+AIBAQYLYIZIAYb4RQEHFwMwMTANBglghkgBZQMEAgEFAAQgWJG1tSLV3wht" +
"D/CxEPvZ0hu0/HFjrzTQgoai6Eb2vgMCFHERZNISITpb8tPCqDQtcNGcWhhSGA8yMDE4MDUwOTE0NTQy" +
"MlowAwIBHqCBhqSBgzCBgDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9u" +
"MR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBTSEEyNTYg" +
"VGltZVN0YW1waW5nIFNpZ25lciAtIEcyMYICWjCCAlYCAQEwgYswdzELMAkGA1UEBhMCVVMxHTAbBgNV" +
"BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSgw" +
"JgYDVQQDEx9TeW1hbnRlYyBTSEEyNTYgVGltZVN0YW1waW5nIENBAhBUWPKq10HWRLyEqXugllLmMAsG" +
"CWCGSAFlAwQCAaCBpDAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4" +
"MDUwOTE0NTQyMlowLwYJKoZIhvcNAQkEMSIEIF/3JTU7CB+pzL3Mf+8BKgIRZQlDbovL5WzNhyeTSCn6" +
"MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIM96wXrQR+zV/cNoIgMbEtTvB4tvK0xea6Qfj/LPS61nMAsG" +
"CSqGSIb3DQEBAQSCAQCRxSB9MLAzK4YnNoFqIK9i71b011Q4pcyF6FEffC3ihOHjdmaHf/rFCeuv4roh" +
"yGxW9cRTshE8UohcghMEuSbkSyaFtVt37o31NC1IvW0vouJVQ0j0rg6nQjlsO9rMGW7cJOS2lVnREqk5" +
"+WfBMKJVnuYSXrnUdxcjSG++4eBCEF5L1fdCVjm4s1hagEORimvUoKuStibW0lwE8rdOEBjusZjRPDV6" +
"hudDhI+2SJPCAFhnNaDDT73y+Ux4x5cVdxHV+tME8kUrr6Hm/l6EyPxu/jwrV/EdJFVsJfkemdJz/ACa" +
"EbbTXfP8KuOwEyUwbFbRCXqO+Z6Gg0RqpiAZWCSM",
)
var fixtureTimestampSymantecWithCerts = mustBase64Decode("" +
"MIIOLTADAgEAMIIOJAYJKoZIhvcNAQcCoIIOFTCCDhECAQMxDTALBglghkgBZQMEAgEwggEOBgsqhkiG" +
"9w0BCRABBKCB/gSB+zCB+AIBAQYLYIZIAYb4RQEHFwMwMTANBglghkgBZQMEAgEFAAQgWJG1tSLV3wht" +
"D/CxEPvZ0hu0/HFjrzTQgoai6Eb2vgMCFDSZty7Ob7ZraC01Jcblagd3PcnYGA8yMDE4MDUwOTE4MjUy" +
"MlowAwIBHqCBhqSBgzCBgDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9u" +
"MR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBTSEEyNTYg" +
"VGltZVN0YW1waW5nIFNpZ25lciAtIEczoIIKizCCBTgwggQgoAMCAQICEHsFsdRJaFFE98mJ0pwZnRIw" +
"DQYJKoZIhvcNAQELBQAwgb0xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0G" +
"A1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDIwMDggVmVyaVNpZ24sIElu" +
"Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE4MDYGA1UEAxMvVmVyaVNpZ24gVW5pdmVyc2FsIFJv" +
"b3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTYwMTEyMDAwMDAwWhcNMzEwMTExMjM1OTU5WjB3" +
"MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFu" +
"dGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0Ew" +
"ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7WZ1ZVU+djHJdGoGi61XzsAGtPHGsMo8Fa4aa" +
"JwAyl2pNyWQUSym7wtkpuS7sY7Phzz8LVpD4Yht+66YH4t5/Xm1AONSRBudBfHkcy8utG7/YlZHz8O5s" +
"+K2WOS5/wSe4eDnFhKXt7a+Hjs6Nx23q0pi1Oh8eOZ3D9Jqo9IThxNF8ccYGKbQ/5IMNJsN7CD5N+Qq3" +
"M0n/yjvU9bKbS+GImRr1wOkzFNbfx4Dbke7+vJJXcnf0zajM/gn1kze+lYhqxdz0sUvUzugJkV+1hHk1" +
"inisGTKPI8EyQRtZDqk+scz51ivvt9jk1R1tETqS9pPJnONI7rtTDtQ2l4Z4xaE3AgMBAAGjggF3MIIB" +
"czAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADBmBgNVHSAEXzBdMFsGC2CGSAGG+EUB" +
"BxcDMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0" +
"dHBzOi8vZC5zeW1jYi5jb20vcnBhMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL3Mu" +
"c3ltY2QuY29tMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNiLmNvbS91bml2ZXJzYWwtcm9v" +
"dC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwgwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFt" +
"cC0yMDQ4LTMwHQYDVR0OBBYEFK9j1sqjToVy4Ke8QfMpojh/gHViMB8GA1UdIwQYMBaAFLZ3+mlIR59T" +
"EtXC6gcydgfRlwcZMA0GCSqGSIb3DQEBCwUAA4IBAQB16rAt1TQZXDJF/g7h1E+meMFv1+rd3E/zociB" +
"iPenjxXmQCmt5l30otlWZIRxMCrdHmEXZiBWBpgZjV1x8viXvAn9HJFHyeLojQP7zJAv1gpsTjPs1rST" +
"yEyQY0g5QCHE3dZuiZg8tZiX6KkGtwnJj1NXQZAv4R5NTtzKEHhsQm7wtsX4YVxS9U72a433Snq+8839" +
"A9fZ9gOoD+NT9wp17MZ1LqpmhQSZt/gGV+HGDvbor9rsmxgfqrnjOgC/zoqUywHbnsc4uw9Sq9HjlANg" +
"Ck2g/idtFDL8P5dA4b+ZidvkORS92uTTw+orWrOVWFUEfcea7CMDjYUq0v+uqWGBMIIFSzCCBDOgAwIB" +
"AgIQe9Tlr7rMBz+hASMEIkFNEjANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJVUzEdMBsGA1UEChMU" +
"U3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNV" +
"BAMTH1N5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMTcxMjIzMDAwMDAwWhcNMjkwMzIy" +
"MjM1OTU5WjCBgDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD" +
"VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBTSEEyNTYgVGltZVN0" +
"YW1waW5nIFNpZ25lciAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArw6Kqvjcv2l7" +
"VBdxRwm9jTyB+HQVd2eQnP3eTgKeS3b25TY+ZdUkIG0w+d0dg+k/J0ozTm0WiuSNQI0iqr6nCxvSB7Y8" +
"tRokKPgbclE9yAmIJgg6+fpDI3VHcAyzX1uPCB1ySFdlTa8CPED39N0yOJM/5Sym81kjy4DeE035EMmq" +
"ChhsVWFX0fECLMS1q/JsI9KfDQ8ZbK2FYmn9ToXBilIxq1vYyXRS41dsIr9Vf2/KBqs/SrcidmXs7Dby" +
"lpWBJiz9u5iqATjTryVAmwlT8ClXhVhe6oVIQSGH5d600yaye0BTWHmOUjEGTZQDRcTOPAPstwDyOiLF" +
"tG/l77CKmwIDAQABo4IBxzCCAcMwDAYDVR0TAQH/BAIwADBmBgNVHSAEXzBdMFsGC2CGSAGG+EUBBxcD" +
"MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBz" +
"Oi8vZC5zeW1jYi5jb20vcnBhMEAGA1UdHwQ5MDcwNaAzoDGGL2h0dHA6Ly90cy1jcmwud3Muc3ltYW50" +
"ZWMuY29tL3NoYTI1Ni10c3MtY2EuY3JsMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQE" +
"AwIHgDB3BggrBgEFBQcBAQRrMGkwKgYIKwYBBQUHMAGGHmh0dHA6Ly90cy1vY3NwLndzLnN5bWFudGVj" +
"LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL3RzLWFpYS53cy5zeW1hbnRlYy5jb20vc2hhMjU2LXRzcy1j" +
"YS5jZXIwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0yMDQ4LTYwHQYDVR0OBBYEFKUT" +
"AamfhcwbbhYeXzsxqnk2AHsdMB8GA1UdIwQYMBaAFK9j1sqjToVy4Ke8QfMpojh/gHViMA0GCSqGSIb3" +
"DQEBCwUAA4IBAQBGnq/wuKJfoplIz6gnSyHNsrmmcnBjL+NVKXs5Rk7nfmUGWIu8V4qSDQjYELo2JPoK" +
"e/s702K/SpQV5oLbilRt/yj+Z89xP+YzCdmiWRD0Hkr+Zcze1GvjUil1AEorpczLm+ipTfe0F1mSQcO3" +
"P4bm9sB/RDxGXBda46Q71Wkm1SF94YBnfmKst04uFZrlnCOvWxHqcalB+Q15OKmhDc+0sdo+mnrHIsV0" +
"zd9HCYbE/JElshuW6YUI6N3qdGBuYKVWeg3IRFjc5vlIFJ7lv94AvXexmBRyFCTfxxEsHwA/w0sUxmcc" +
"zB4Go5BfXFSLPuMzW4IPxbeGAk5xn+lmRT92MYICWjCCAlYCAQEwgYswdzELMAkGA1UEBhMCVVMxHTAb" +
"BgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3Jr" +
"MSgwJgYDVQQDEx9TeW1hbnRlYyBTSEEyNTYgVGltZVN0YW1waW5nIENBAhB71OWvuswHP6EBIwQiQU0S" +
"MAsGCWCGSAFlAwQCAaCBpDAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8X" +
"DTE4MDUwOTE4MjUyMlowLwYJKoZIhvcNAQkEMSIEIF5EOTCml8PvDOxSGeQnbCv+jXprtZlEut7wcOx/" +
"xjfvMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIMR0znYAfQI5Tg2l5N58FMaA+eKCATz+9lPvXbcf32H4" +
"MAsGCSqGSIb3DQEBAQSCAQBD1SGuMSSNtmwg38x/1d8v+uvX/2aPIJQS//p5Q54Y8moIEeezRhG0tK3N" +
"81tfKdLeYTVE6VL8D7ZaCpbKzNJeD6DQM4S87bzH88H5RQOb2JTCvBPF3C/ytcl7ylezx6xsFNtftbW3" +
"IOXETaWLgIBpeL7jUZQDhgQ4Xb9HeFl4vA6Wk2kR88h+8Tv2ci0AI9hZgHhH9c/OwPvd8TKbhSjK9qXK" +
"DjaJr0BeVuYHPSWxfsxWVCOjNIOg7moWpPLSYQpqM2gdg5ppjQWffWYC4rywmM6XsBKs+EKFb++4GSOc" +
"wc6JJCugxm8Ba1a6nDAAAQYf/pQyBRRlh/qCHZ0rIoFq",
)
var fixtureTimestampDigicert = mustBase64Decode("" +
"MIIOuTADAgEAMIIOsAYJKoZIhvcNAQcCoIIOoTCCDp0CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG" +
"9w0BCRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIFiRtbUi1d8IbQ/wsRD7" +
"2dIbtPxxY6800IKGouhG9r4DAhAvZIfDsFuq0GRqVn9Wu2I8GA8yMDE4MDUwOTE4NDgxOFqgggu7MIIG" +
"gjCCBWqgAwIBAgIQCcD8RsgEQhO1WYuvKE9OQTANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEV" +
"MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhE" +
"aWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTE3MDEwNDAwMDAwMFoXDTI4" +
"MDExODAwMDAwMFowTDELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSowKAYDVQQDEyFEaWdp" +
"Q2VydCBTSEEyIFRpbWVzdGFtcCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" +
"AQCelZhqNDtzG6h+/Me+KWmJx2gmRl89jWJzh4GjoZzwt1skN1qS1PRZ13aJ5NzVJ/DVZrwK7rQrMWes" +
"WMVKkVkrRR4JAdZks1nujWZN+yNezBANC4pn71KuoAiQwlL39ai1bpsse53ntT77eM0yUBi/QLVMjLtX" +
"9KBPEUVsQkK55a/W3/SnfApolg/SXylXzvsdMv/0EaETIvsSy+/XU9Lrl8uirBsdnVghUYLCwt7qKz8s" +
"IoTQQ+w7Oz9HxPZW3EU3mLRrdLVZr3hXacgPCQJ43dhTwZnbYMSd6q6v4H6GSlypWGGoXnSKAShock6n" +
"hp21AlKHcGZI047vgSTM3NhlAgMBAAGjggM4MIIDNDAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw" +
"ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCCAb8GA1UdIASCAbYwggGyMIIBoQYJYIZIAYb9bAcBMIIB" +
"kjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCCAWQGCCsGAQUFBwICMIIB" +
"Vh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUA" +
"IABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUA" +
"IABEAGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkA" +
"bgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBpAHQA" +
"IABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUA" +
"ZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwHwYD" +
"VR0jBBgwFoAU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHQYDVR0OBBYEFOGnMkruASEofVTV8geSbrQHDz2H" +
"MHEGA1UdHwRqMGgwMqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMu" +
"Y3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDCBhQYI" +
"KwYBBQUHAQEEeTB3MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTwYIKwYBBQUH" +
"MAKGQ2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURUaW1lc3Rh" +
"bXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggEBAB7wQYIyru3xtDUT3FDC1ZeuIiKdDg6vM9NM/Xy/" +
"bwERp5RlIlzGIqHIiVJrmoxzXNlePzLeFmBMizb9MZkKvcGEt40d74kmEwVW80fNR1uthLI4r2ojtUXj" +
"HogyRoDSt6aZIv3BeM/1i9gMjAUJ7kTmgNVtcMyfUx4n3SpI3tqTZa1uZaOZp8JADnPMWE+PRSjlvJyI" +
"5ijOYF0tJV2Lcy6lDVtR2ppO/1AFiSja8ni70lh4jUSnrDoAkXhpiWQE012W3yq/+aVMLJP/5ordgqzx" +
"0rOihprBVYlWakc/+tYzlUM1iQV4Wjpp2iK4BEPTb2g1NnoUPkXpmGSGDxMMJkowggUxMIIEGaADAgEC" +
"AhAKoSXW1jIbfkHkBdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE" +
"aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFz" +
"c3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYT" +
"AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV" +
"BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEB" +
"AQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1ofue2oPSNs4jk" +
"l79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX7" +
"86O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8" +
"YGqsLwfM/fDqR9mIUF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfx" +
"FwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAdBgNVHQ4EFgQU9Lbh" +
"IB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/" +
"BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEE" +
"bTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6" +
"Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6" +
"MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j" +
"cmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j" +
"cmwwUAYDVR0gBEkwRzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj" +
"ZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdWac3v3dp8qmN6" +
"s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli" +
"6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSl" +
"gNcSR3CzddWThZN+tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6H" +
"USHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjayS6JKldj1po5S" +
"MYICTTCCAkkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE" +
"CxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVz" +
"dGFtcGluZyBDQQIQCcD8RsgEQhO1WYuvKE9OQTANBglghkgBZQMEAgEFAKCBmDAaBgkqhkiG9w0BCQMx" +
"DQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4MDUwOTE4NDgxOFowLwYJKoZIhvcNAQkEMSIE" +
"IDpdtczqob9pSfKx5ZEHQZSSHM3P+8uGHy1rXmrK9iUjMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFEAB" +
"kUdcmIkd66EEr0cJG1621MvLMA0GCSqGSIb3DQEBAQUABIIBAIlFY+12XT6zvj4/0LVL5//VunTmYTKg" +
"Z6eSrafFT9zOvGbDzm/8XnDLrUQq9Y4kQpE+eKfHWJOBQQZ0ze0wftUml+iRsvqEVlax7G03SzHyPIYH" +
"HzEH/IKRlryHR5LgzzeFqS6IdVg18FBLvrs2fvPJlsj0ZGmAbwn6ntHDromtnkwZV6Cir5gH+wSKuA+Z" +
"3Qj5odgrTQ9gmbmNlFgwp4BwH/vFbBB1eIt7EUD1KfZzThfdFYHnyl8eRcE5p5+MxvyAC78fPzlSlJJP" +
"OES5LDDTx/Qvhet0PjJv70Z7kKgMmAA0BMTRuTnGfiVfEoFm2bzoKmwprU38EPz+PVnrbUA=",
)
var fixtureTimestampComodo = mustBase64Decode("" +
"MIIDuDADAgEAMIIDrwYJKoZIhvcNAQcCoIIDoDCCA5wCAQMxDzANBglghkgBZQMEAgEFADCCAQ8GCyqG" +
"SIb3DQEJEAEEoIH/BIH8MIH5AgEBBgorBgEEAbIxAgEBMDEwDQYJYIZIAWUDBAIBBQAEIFiRtbUi1d8I" +
"bQ/wsRD72dIbtPxxY6800IKGouhG9r4DAhUA4Fc3zQPRFgrg3c8/sksclhBco7QYDzIwMTgwNTA5MTg0" +
"NzQyWqCBjKSBiTCBhjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G" +
"A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxLDAqBgNVBAMTI0NPTU9ETyBT" +
"SEEtMjU2IFRpbWUgU3RhbXBpbmcgU2lnbmVyMYICcTCCAm0CAQEwgaowgZUxCzAJBgNVBAYTAlVTMQsw" +
"CQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1Qg" +
"TmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNF" +
"UkZpcnN0LU9iamVjdAIQTrCHj8wkNTay2Mn3vzlVdzANBglghkgBZQMEAgEFAKCBmDAaBgkqhkiG9w0B" +
"CQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4MDUwOTE4NDc0MlowKwYLKoZIhvcNAQkQ" +
"AgwxHDAaMBgwFgQUNlJ9T6JqaPnrRZbx2Zq7LA6nbfowLwYJKoZIhvcNAQkEMSIEIJeVWgArDRySkAZc" +
"F6na8PZrsUBoQs2jUzy94iOFYfM6MA0GCSqGSIb3DQEBAQUABIIBAKKV56NeTuFn4VdoNv15X0bUWG3p" +
"JSMRVbp1CWktnraj7E5m3BUmFlb4Dwrf3IMmE4QJrGrzDUWtUmpnHR4VuGAUmyajEcmDICc2gpBBG+aV" +
"0Ng/lXQ1xAotKkU7/4wNQY1nOBsquZykYRHWbzJaVxaq8VEc0nVZY2o1TVDgWtLF7BHAd96vw4iVuG3O" +
"Pb8izdFMwQ0t/TMNq0FD0hEFQDSTvVkayeaficblGbhf/p1xuCxSMoFBmnfO56aRX01E3SDNAgo3/hFl" +
"na2g8ESpdWHRMqG3+8ehvgMwljUnhj5+iYT1YF7Rm6KcV2TCIh6QyokN42ji4BMqTlBA7vzSx5A=",
)
var fixtureTimestampGlobalSign = mustBase64Decode("" +
"MIIDoTADAgEAMIIDmAYJKoZIhvcNAQcCoIIDiTCCA4UCAQMxCzAJBgUrDgMCGgUAMIHdBgsqhkiG9w0B" +
"CRABBKCBzQSByjCBxwIBAQYJKwYBBAGgMgICMDEwDQYJYIZIAWUDBAIBBQAEIFiRtbUi1d8IbQ/wsRD7" +
"2dIbtPxxY6800IKGouhG9r4DAhRYZmxGjSg8ojY0mWZG3dUdVW0mAxgPMjAxODA1MDkxODQ2MjRaoF2k" +
"WzBZMQswCQYDVQQGEwJTRzEfMB0GA1UEChMWR01PIEdsb2JhbFNpZ24gUHRlIEx0ZDEpMCcGA1UEAxMg" +
"R2xvYmFsU2lnbiBUU0EgZm9yIFN0YW5kYXJkIC0gRzIxggKRMIICjQIBATBoMFIxCzAJBgNVBAYTAkJF" +
"MRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFRpbWVzdGFtcGlu" +
"ZyBDQSAtIEcyAhIRIbRVNR67GrJPl+8H/iqzC4owCQYFKw4DAhoFAKCB/zAaBgkqhkiG9w0BCQMxDQYL" +
"KoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4MDUwOTE4NDYyNFowIwYJKoZIhvcNAQkEMRYEFOmL" +
"BqSyLEaL7tN+hDwnk6fha6wfMIGdBgsqhkiG9w0BCRACDDGBjTCBijCBhzCBhAQUg/3hunb+9VKRtQ1o" +
"YZBtqkW1jLUwbDBWpFQwUjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExKDAm" +
"BgNVBAMTH0dsb2JhbFNpZ24gVGltZXN0YW1waW5nIENBIC0gRzICEhEhtFU1Hrsask+X7wf+KrMLijAN" +
"BgkqhkiG9w0BAQEFAASCAQBhWhjTagaTyATim1IHw0tF0wb22rlj6qXki86lclB/2uxBC8/3uLVd259z" +
"iz7aaTmxSj3ksMBq9A75beQW5Be9vK00B/mj/p1dLrtgCcYZtV4uhoBkBx0YbriumEnvQoQL1bI1EiXh" +
"TDbdTrGs2wXn3Xzw/qwqc7w+IjW1BjqzLf6BB9jw2raxMuWBA3EGMwGTumRx5x6a7j2Jx/9Uhs+3ce+9" +
"ZRDtiWAFCkTQVvNLrAuHLTFK6lLOqfucrru76adpJMlTJ+VRut0adpwviS1Cb2ifIX1iUHjtGssihk6v" +
"/tt7Yo4J341G5pC4JDXXhJvxHImNew3l0BWM0LROEgLM",
)
func mustBase64Decode(b64 string) []byte {
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(b64))
buf := new(bytes.Buffer)
@ -873,19 +636,3 @@ func mustBase64Decode(b64 string) []byte {
return buf.Bytes()
}
// The fixtures above are complete TimeStampResp's, but most of our tests only
// care about the TimeStampToken (CMS ContentInfo) part of it.
func mustExtractTimeStampToken(ber []byte) []byte {
resp, err := ParseTimeStampResp(ber)
if err != nil {
panic(err)
}
tstDER, err := asn1.Marshal(resp.TimeStampToken)
if err != nil {
panic(err)
}
return tstDER
}

Просмотреть файл

@ -1,246 +0,0 @@
package protocol
import (
"bytes"
"crypto"
"encoding/asn1"
"encoding/hex"
"math/big"
"testing"
"time"
)
func TestTimeStampReq(t *testing.T) {
tsr := new(TimeStampReq)
tsr.GenerateNonce()
if tsr.Nonce == nil {
t.Fatal("expected non-nil nonce")
}
// don't check for exact bitlength match, since leading 0's don't count
// towards length.
if tsr.Nonce.BitLen() < nonceBytes*8/2 {
t.Fatalf("expected %d bit nonce, got %d", nonceBytes*8, tsr.Nonce.BitLen())
}
if tsr.Nonce.Cmp(new(big.Int)) == 0 {
t.Fatal("expected non-zero nonce")
}
}
func TestMessageImprint(t *testing.T) {
m := []byte("hello, world!")
mi1, err := NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
if err != nil {
panic(err)
}
// same
mi2, err := NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
if err != nil {
panic(err)
}
if !mi1.Equal(mi2) {
t.Fatal("expected m1==m2")
}
// round trip
der, err := asn1.Marshal(mi1)
if err != nil {
t.Fatal(err)
}
if _, err = asn1.Unmarshal(der, &mi2); err != nil {
t.Fatal(err)
}
if !mi1.Equal(mi2) {
t.Fatal("expected m1==m2")
}
// null value for hash alrogithm parameters (as opposed to being absent entirely)
mi2, _ = NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
mi2.HashAlgorithm.Parameters = asn1.RawValue{Tag: 5} // go1.10 has asn1.NullRawValue
if !mi1.Equal(mi2) {
t.Fatal("expected m1==m2")
}
// different digest
mi2, err = NewMessageImprint(crypto.SHA1, bytes.NewReader(m))
if err != nil {
panic(err)
}
if mi1.Equal(mi2) {
t.Fatal("expected m1!=m2")
}
// different message
mi2, err = NewMessageImprint(crypto.SHA256, bytes.NewReader([]byte("wrong")))
if err != nil {
panic(err)
}
if mi1.Equal(mi2) {
t.Fatal("expected m1!=m2")
}
// bad digest
mi2, _ = NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
mi2.HashedMessage = mi2.HashedMessage[0 : len(mi2.HashedMessage)-1]
if mi1.Equal(mi2) {
t.Fatal("expected m1!=m2")
}
}
func TestErrorTimeStampResp(t *testing.T) {
// Error response from request with missing message digest.
respDER, _ := ber2der(mustBase64Decode("MDQwMgIBAjApDCd0aGUgZGF0YSBzdWJtaXR0ZWQgaGFzIHRoZSB3cm9uZyBmb3JtYXQDAgIE"))
resp, err := ParseTimeStampResp(respDER)
if err != nil {
t.Fatal(err)
}
rt, err := asn1.Marshal(resp)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(respDER, rt) {
t.Fatal("expected round-tripping error TimeStampResp to equal")
}
expectedStatus := 2
if resp.Status.Status != expectedStatus {
t.Fatalf("expected status %d, got %d", expectedStatus, resp.Status.Status)
}
if numStrings := len(resp.Status.StatusString); numStrings != 1 {
t.Fatalf("expected single status string, got %d", numStrings)
}
expectedString := "the data submitted has the wrong format"
actualStrings, err := resp.Status.StatusString.Strings()
if err != nil {
t.Fatal(err)
}
if actualStrings[0] != expectedString {
t.Fatalf("expected status string %s, got %s", expectedString, actualStrings[0])
}
expectedFailInfoLen := 6
if resp.Status.FailInfo.BitLength != expectedFailInfoLen {
t.Fatalf("expected len(failinfo) %d, got %d", expectedFailInfoLen, resp.Status.FailInfo.BitLength)
}
expectedFailInfo := []int{0, 0, 0, 0, 0, 1}
for i, v := range expectedFailInfo {
if actual := resp.Status.FailInfo.At(i); actual != v {
t.Fatalf("expected failinfo[%d] to be %d, got %d", i, v, actual)
}
}
}
func TestPKIFreeText(t *testing.T) {
der := mustBase64Decode("MBUME0JhZCBtZXNzYWdlIGRpZ2VzdC4=")
var ft PKIFreeText
if _, err := asn1.Unmarshal(der, &ft); err != nil {
t.Fatal(err)
}
rt, err := asn1.Marshal(ft)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(der, rt) {
t.Fatal("expected round-tripped PKIFreeText to match")
}
ft, err = NewPKIFreeText([]string{"Bad message digest."})
if err != nil {
t.Fatal(err)
}
rt, err = asn1.Marshal(ft)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(der, rt) {
t.Fatal("expected newly made PKIFreeText to match original DER")
}
}
func TestTSTInfo(t *testing.T) {
tsr, err := ParseTimeStampResp(fixtureTimestampSymantecWithCerts)
if err != nil {
t.Fatal(err)
}
sd, err := tsr.TimeStampToken.SignedDataContent()
if err != nil {
t.Fatal(err)
}
tsti, err := sd.EncapContentInfo.TSTInfoEContent()
if err != nil {
t.Fatal(err)
}
expectedVersion := 1
if tsti.Version != expectedVersion {
t.Fatalf("expected version %d, got %d", expectedVersion, tsti.Version)
}
expectedPolicy := asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 7, 23, 3}
if !tsti.Policy.Equal(expectedPolicy) {
t.Fatalf("expected policy %s, got %s", expectedPolicy.String(), tsti.Policy.String())
}
expectedHash := crypto.SHA256
if hash, err := tsti.MessageImprint.Hash(); err != nil {
t.Fatal(err)
} else if hash != expectedHash {
t.Fatalf("expected hash %d, got %d", expectedHash, hash)
}
expectedMI, _ := NewMessageImprint(crypto.SHA256, bytes.NewReader([]byte("hello\n")))
if !tsti.MessageImprint.Equal(expectedMI) {
t.Fatalf("expected hash %s, got %s",
hex.EncodeToString(expectedMI.HashedMessage),
hex.EncodeToString(tsti.MessageImprint.HashedMessage))
}
expectedSN := new(big.Int).SetBytes([]byte{0x34, 0x99, 0xB7, 0x2E, 0xCE, 0x6F, 0xB6, 0x6B, 0x68, 0x2D, 0x35, 0x25, 0xC6, 0xE5, 0x6A, 0x07, 0x77, 0x3D, 0xC9, 0xD8})
if tsti.SerialNumber.Cmp(expectedSN) != 0 {
t.Fatalf("expected SN %s, got %s", expectedSN.String(), tsti.SerialNumber.String())
}
timeFmt := "2006-01-02 15:04:05 MST"
expectedGenTime, _ := time.Parse(timeFmt, "2018-05-09 18:25:22 UTC")
if !tsti.GenTime.Equal(expectedGenTime) {
t.Fatalf("expected gentime %s, got %s", expectedGenTime.String(), tsti.GenTime.String())
}
expectedAccuracy := 30 * time.Second
if accuracy := tsti.Accuracy.Duration(); accuracy != expectedAccuracy {
t.Fatalf("expected accurracy %s, got %s", expectedAccuracy.String(), accuracy.String())
}
expectedGenTimeMax := expectedGenTime.Add(expectedAccuracy)
if tsti.GenTimeMax() != expectedGenTimeMax {
t.Fatalf("expected gentimemax %s, got %s", expectedGenTimeMax.String(), tsti.GenTimeMax().String())
}
expectedGenTimeMin := expectedGenTime.Add(-expectedAccuracy)
if tsti.GenTimeMin() != expectedGenTimeMin {
t.Fatalf("expected gentimemax %s, got %s", expectedGenTimeMin.String(), tsti.GenTimeMin().String())
}
expectedOrdering := false
if tsti.Ordering != expectedOrdering {
t.Fatalf("expected ordering %t, got %t", expectedOrdering, tsti.Ordering)
}
if tsti.Nonce != nil {
t.Fatal("expected nil nonce")
}
// don't bother with TSA, since we don't want to mess with parsing GeneralNames.
if tsti.Extensions != nil {
t.Fatal("expected nil extensions")
}
}

Просмотреть файл

@ -1,4 +1,4 @@
package protocol
package timestamp
import (
"bytes"
@ -10,21 +10,24 @@ import (
"fmt"
"io"
"math/big"
"strings"
"time"
"github.com/mastahyeti/cms/oid"
"github.com/mastahyeti/cms/protocol"
)
// TimeStampReq ::= SEQUENCE {
// version INTEGER { v1(1) },
// messageImprint MessageImprint,
// --a hash algorithm OID and the hash value of the data to be
// --time-stamped
// reqPolicy TSAPolicyId OPTIONAL,
// nonce INTEGER OPTIONAL,
// certReq BOOLEAN DEFAULT FALSE,
// extensions [0] IMPLICIT Extensions OPTIONAL }
type TimeStampReq struct {
// Request is a TimeStampReq
// TimeStampReq ::= SEQUENCE {
// version INTEGER { v1(1) },
// messageImprint MessageImprint,
// --a hash algorithm OID and the hash value of the data to be
// --time-stamped
// reqPolicy TSAPolicyId OPTIONAL,
// nonce INTEGER OPTIONAL,
// certReq BOOLEAN DEFAULT FALSE,
// extensions [0] IMPLICIT Extensions OPTIONAL }
type Request struct {
Version int
MessageImprint MessageImprint
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
@ -36,34 +39,35 @@ type TimeStampReq struct {
const nonceBytes = 16
// GenerateNonce generates a new nonce for this TSR.
func (tsr *TimeStampReq) GenerateNonce() {
func (r *Request) GenerateNonce() {
buf := make([]byte, nonceBytes)
if _, err := rand.Read(buf); err != nil {
panic(err)
}
if tsr.Nonce == nil {
tsr.Nonce = new(big.Int)
if r.Nonce == nil {
r.Nonce = new(big.Int)
}
tsr.Nonce.SetBytes(buf[:])
r.Nonce.SetBytes(buf[:])
}
// TimeStampResp ::= SEQUENCE {
// status PKIStatusInfo,
// timeStampToken TimeStampToken OPTIONAL }
// Response is a TimeStampResp
// TimeStampResp ::= SEQUENCE {
// status PKIStatusInfo,
// timeStampToken TimeStampToken OPTIONAL }
//
// TimeStampToken ::= ContentInfo
type TimeStampResp struct {
// TimeStampToken ::= ContentInfo
type Response struct {
Status PKIStatusInfo
TimeStampToken ContentInfo `asn1:"optional"`
TimeStampToken protocol.ContentInfo `asn1:"optional"`
}
// ParseTimeStampResp parses a BER encoded TimeStampResp.
func ParseTimeStampResp(ber []byte) (TimeStampResp, error) {
var resp TimeStampResp
// ParseResponse parses a BER encoded TimeStampResp.
func ParseResponse(ber []byte) (Response, error) {
var resp Response
der, err := ber2der(ber)
der, err := protocol.BER2DER(ber)
if err != nil {
return resp, err
}
@ -128,6 +132,35 @@ type PKIStatusInfo struct {
FailInfo asn1.BitString `asn1:"optional"`
}
// Error represents an unsuccessful PKIStatusInfo as an error.
func (si PKIStatusInfo) Error() error {
if si.Status == 0 {
return nil
}
fiStr := ""
if si.FailInfo.BitLength > 0 {
fibin := make([]byte, si.FailInfo.BitLength)
for i := range fibin {
if si.FailInfo.At(i) == 1 {
fibin[i] = byte('1')
} else {
fibin[i] = byte('0')
}
}
fiStr = fmt.Sprintf(" FailInfo(0b%s)", string(fibin))
}
statusStr := ""
if len(si.StatusString) > 0 {
if strs, err := si.StatusString.Strings(); err == nil {
statusStr = fmt.Sprintf(" StatusString(%s)", strings.Join(strs, ","))
}
}
return fmt.Errorf("Bad TimeStampResp: Status(%d)%s%s", si.Status, statusStr, fiStr)
}
// PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
type PKIFreeText []asn1.RawValue
@ -163,26 +196,27 @@ func (ft PKIFreeText) Strings() ([]string, error) {
return strs, nil
}
// TSTInfo ::= SEQUENCE {
// version INTEGER { v1(1) },
// policy TSAPolicyId,
// messageImprint MessageImprint,
// -- MUST have the same value as the similar field in
// -- TimeStampReq
// serialNumber INTEGER,
// -- Time-Stamping users MUST be ready to accommodate integers
// -- up to 160 bits.
// genTime GeneralizedTime,
// accuracy Accuracy OPTIONAL,
// ordering BOOLEAN DEFAULT FALSE,
// nonce INTEGER OPTIONAL,
// -- MUST be present if the similar field was present
// -- in TimeStampReq. In that case it MUST have the same value.
// tsa [0] GeneralName OPTIONAL,
// extensions [1] IMPLICIT Extensions OPTIONAL }
// Info is a Info
// Info ::= SEQUENCE {
// version INTEGER { v1(1) },
// policy TSAPolicyId,
// messageImprint MessageImprint,
// -- MUST have the same value as the similar field in
// -- TimeStampReq
// serialNumber INTEGER,
// -- Time-Stamping users MUST be ready to accommodate integers
// -- up to 160 bits.
// genTime GeneralizedTime,
// accuracy Accuracy OPTIONAL,
// ordering BOOLEAN DEFAULT FALSE,
// nonce INTEGER OPTIONAL,
// -- MUST be present if the similar field was present
// -- in TimeStampReq. In that case it MUST have the same value.
// tsa [0] GeneralName OPTIONAL,
// extensions [1] IMPLICIT Extensions OPTIONAL }
//
// TSAPolicyId ::= OBJECT IDENTIFIER
type TSTInfo struct {
// TSAPolicyId ::= OBJECT IDENTIFIER
type Info struct {
Version int
Policy asn1.ObjectIdentifier
MessageImprint MessageImprint
@ -195,16 +229,41 @@ type TSTInfo struct {
Extensions []pkix.Extension `asn1:"tag:1,optional"`
}
// ParseInfo parses an Info out of a CMS EncapsulatedContentInfo.
func ParseInfo(eci protocol.EncapsulatedContentInfo) (Info, error) {
i := Info{}
if !eci.EContentType.Equal(oid.TSTInfo) {
return i, protocol.ErrWrongType
}
ecval, err := eci.EContentValue()
if err != nil {
return i, err
}
if ecval == nil {
return i, errors.New("missing EContent for non data type")
}
if rest, err := asn1.Unmarshal(ecval, &i); err != nil {
return i, err
} else if len(rest) > 0 {
return i, errors.New("unexpected trailing data")
}
return i, nil
}
// GenTimeMax is the latest time at which the token could have been generated
// based on the included GenTime and Accuracy attributes.
func (tsti *TSTInfo) GenTimeMax() time.Time {
return tsti.GenTime.Add(tsti.Accuracy.Duration())
func (i *Info) GenTimeMax() time.Time {
return i.GenTime.Add(i.Accuracy.Duration())
}
// GenTimeMin is the earliest time at which the token could have been generated
// based on the included GenTime and Accuracy attributes.
func (tsti *TSTInfo) GenTimeMin() time.Time {
return tsti.GenTime.Add(-tsti.Accuracy.Duration())
func (i *Info) GenTimeMin() time.Time {
return i.GenTime.Add(-i.Accuracy.Duration())
}
// MessageImprint ::= SEQUENCE {

590
timestamp/timestamp_test.go Normal file
Просмотреть файл

@ -0,0 +1,590 @@
package timestamp
import (
"bytes"
"crypto"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"io"
"math/big"
"strings"
"testing"
"time"
"github.com/mastahyeti/cms/protocol"
)
func TestTimeStampReq(t *testing.T) {
tsr := new(Request)
tsr.GenerateNonce()
if tsr.Nonce == nil {
t.Fatal("expected non-nil nonce")
}
// don't check for exact bitlength match, since leading 0's don't count
// towards length.
if tsr.Nonce.BitLen() < nonceBytes*8/2 {
t.Fatalf("expected %d bit nonce, got %d", nonceBytes*8, tsr.Nonce.BitLen())
}
if tsr.Nonce.Cmp(new(big.Int)) == 0 {
t.Fatal("expected non-zero nonce")
}
}
func TestMessageImprint(t *testing.T) {
m := []byte("hello, world!")
mi1, err := NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
if err != nil {
panic(err)
}
// same
mi2, err := NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
if err != nil {
panic(err)
}
if !mi1.Equal(mi2) {
t.Fatal("expected m1==m2")
}
// round trip
der, err := asn1.Marshal(mi1)
if err != nil {
t.Fatal(err)
}
if _, err = asn1.Unmarshal(der, &mi2); err != nil {
t.Fatal(err)
}
if !mi1.Equal(mi2) {
t.Fatal("expected m1==m2")
}
// null value for hash alrogithm parameters (as opposed to being absent entirely)
mi2, _ = NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
mi2.HashAlgorithm.Parameters = asn1.RawValue{Tag: 5} // go1.10 has asn1.NullRawValue
if !mi1.Equal(mi2) {
t.Fatal("expected m1==m2")
}
// different digest
mi2, err = NewMessageImprint(crypto.SHA1, bytes.NewReader(m))
if err != nil {
panic(err)
}
if mi1.Equal(mi2) {
t.Fatal("expected m1!=m2")
}
// different message
mi2, err = NewMessageImprint(crypto.SHA256, bytes.NewReader([]byte("wrong")))
if err != nil {
panic(err)
}
if mi1.Equal(mi2) {
t.Fatal("expected m1!=m2")
}
// bad digest
mi2, _ = NewMessageImprint(crypto.SHA256, bytes.NewReader(m))
mi2.HashedMessage = mi2.HashedMessage[0 : len(mi2.HashedMessage)-1]
if mi1.Equal(mi2) {
t.Fatal("expected m1!=m2")
}
}
func TestErrorTimeStampResp(t *testing.T) {
// Error response from request with missing message digest.
respDER, _ := protocol.BER2DER(mustBase64Decode("MDQwMgIBAjApDCd0aGUgZGF0YSBzdWJtaXR0ZWQgaGFzIHRoZSB3cm9uZyBmb3JtYXQDAgIE"))
resp, err := ParseResponse(respDER)
if err != nil {
t.Fatal(err)
}
rt, err := asn1.Marshal(resp)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(respDER, rt) {
t.Fatal("expected round-tripping error TimeStampResp to equal")
}
expectedStatus := 2
if resp.Status.Status != expectedStatus {
t.Fatalf("expected status %d, got %d", expectedStatus, resp.Status.Status)
}
if numStrings := len(resp.Status.StatusString); numStrings != 1 {
t.Fatalf("expected single status string, got %d", numStrings)
}
expectedString := "the data submitted has the wrong format"
actualStrings, err := resp.Status.StatusString.Strings()
if err != nil {
t.Fatal(err)
}
if actualStrings[0] != expectedString {
t.Fatalf("expected status string %s, got %s", expectedString, actualStrings[0])
}
expectedFailInfoLen := 6
if resp.Status.FailInfo.BitLength != expectedFailInfoLen {
t.Fatalf("expected len(failinfo) %d, got %d", expectedFailInfoLen, resp.Status.FailInfo.BitLength)
}
expectedFailInfo := []int{0, 0, 0, 0, 0, 1}
for i, v := range expectedFailInfo {
if actual := resp.Status.FailInfo.At(i); actual != v {
t.Fatalf("expected failinfo[%d] to be %d, got %d", i, v, actual)
}
}
}
func TestPKIFreeText(t *testing.T) {
der := mustBase64Decode("MBUME0JhZCBtZXNzYWdlIGRpZ2VzdC4=")
var ft PKIFreeText
if _, err := asn1.Unmarshal(der, &ft); err != nil {
t.Fatal(err)
}
rt, err := asn1.Marshal(ft)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(der, rt) {
t.Fatal("expected round-tripped PKIFreeText to match")
}
ft, err = NewPKIFreeText([]string{"Bad message digest."})
if err != nil {
t.Fatal(err)
}
rt, err = asn1.Marshal(ft)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(der, rt) {
t.Fatal("expected newly made PKIFreeText to match original DER")
}
}
func TestTSTInfo(t *testing.T) {
resp, err := ParseResponse(fixtureTimestampSymantecWithCerts)
if err != nil {
t.Fatal(err)
}
sd, err := resp.TimeStampToken.SignedDataContent()
if err != nil {
t.Fatal(err)
}
inf, err := ParseInfo(sd.EncapContentInfo)
if err != nil {
t.Fatal(err)
}
expectedVersion := 1
if inf.Version != expectedVersion {
t.Fatalf("expected version %d, got %d", expectedVersion, inf.Version)
}
expectedPolicy := asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 7, 23, 3}
if !inf.Policy.Equal(expectedPolicy) {
t.Fatalf("expected policy %s, got %s", expectedPolicy.String(), inf.Policy.String())
}
expectedHash := crypto.SHA256
if hash, err := inf.MessageImprint.Hash(); err != nil {
t.Fatal(err)
} else if hash != expectedHash {
t.Fatalf("expected hash %d, got %d", expectedHash, hash)
}
expectedMI, _ := NewMessageImprint(crypto.SHA256, bytes.NewReader([]byte("hello\n")))
if !inf.MessageImprint.Equal(expectedMI) {
t.Fatalf("expected hash %s, got %s",
hex.EncodeToString(expectedMI.HashedMessage),
hex.EncodeToString(inf.MessageImprint.HashedMessage))
}
expectedSN := new(big.Int).SetBytes([]byte{0x34, 0x99, 0xB7, 0x2E, 0xCE, 0x6F, 0xB6, 0x6B, 0x68, 0x2D, 0x35, 0x25, 0xC6, 0xE5, 0x6A, 0x07, 0x77, 0x3D, 0xC9, 0xD8})
if inf.SerialNumber.Cmp(expectedSN) != 0 {
t.Fatalf("expected SN %s, got %s", expectedSN.String(), inf.SerialNumber.String())
}
timeFmt := "2006-01-02 15:04:05 MST"
expectedGenTime, _ := time.Parse(timeFmt, "2018-05-09 18:25:22 UTC")
if !inf.GenTime.Equal(expectedGenTime) {
t.Fatalf("expected gentime %s, got %s", expectedGenTime.String(), inf.GenTime.String())
}
expectedAccuracy := 30 * time.Second
if accuracy := inf.Accuracy.Duration(); accuracy != expectedAccuracy {
t.Fatalf("expected accurracy %s, got %s", expectedAccuracy.String(), accuracy.String())
}
expectedGenTimeMax := expectedGenTime.Add(expectedAccuracy)
if inf.GenTimeMax() != expectedGenTimeMax {
t.Fatalf("expected gentimemax %s, got %s", expectedGenTimeMax.String(), inf.GenTimeMax().String())
}
expectedGenTimeMin := expectedGenTime.Add(-expectedAccuracy)
if inf.GenTimeMin() != expectedGenTimeMin {
t.Fatalf("expected gentimemax %s, got %s", expectedGenTimeMin.String(), inf.GenTimeMin().String())
}
expectedOrdering := false
if inf.Ordering != expectedOrdering {
t.Fatalf("expected ordering %t, got %t", expectedOrdering, inf.Ordering)
}
if inf.Nonce != nil {
t.Fatal("expected nil nonce")
}
// don't bother with TSA, since we don't want to mess with parsing GeneralNames.
if inf.Extensions != nil {
t.Fatal("expected nil extensions")
}
}
func TestParseTimestampSymantec(t *testing.T) {
testParseInfo(t, fixtureTimestampSymantec)
}
func TestParseTimestampSymantecWithCerts(t *testing.T) {
testParseInfo(t, fixtureTimestampSymantecWithCerts)
}
func TestParseTimestampDigicert(t *testing.T) {
testParseInfo(t, fixtureTimestampDigicert)
}
func TestParseTimestampComodo(t *testing.T) {
testParseInfo(t, fixtureTimestampComodo)
}
func TestParseTimestampGlobalSign(t *testing.T) {
testParseInfo(t, fixtureTimestampGlobalSign)
}
func testParseInfo(t *testing.T, ber []byte) {
resp, err := ParseResponse(ber)
if err != nil {
t.Fatal(err)
}
if err = resp.Status.Error(); err != nil {
t.Fatal(err)
}
sd, err := resp.TimeStampToken.SignedDataContent()
if err != nil {
t.Fatal(err)
}
certs, err := sd.X509Certificates()
if err != nil {
t.Fatal(err)
}
inf, err := ParseInfo(sd.EncapContentInfo)
if err != nil {
t.Fatal(err)
}
hash, err := inf.MessageImprint.Hash()
if err != nil {
t.Fatal(err)
}
if hash != crypto.SHA256 {
t.Fatalf("expected SHA256 hash, found %s", inf.MessageImprint.HashAlgorithm.Algorithm.String())
}
if inf.SerialNumber == nil {
t.Fatal("expected non-nill SN")
}
if inf.SerialNumber.Cmp(big.NewInt(0)) <= 0 {
t.Fatal("expected SN>0")
}
if inf.Version != 1 {
t.Fatalf("expected tst v1, found %d", inf.Version)
}
for _, si := range sd.SignerInfos {
if _, err = si.FindCertificate(certs); err != nil && len(certs) > 0 {
t.Fatal(err)
}
if ct, errr := si.GetContentTypeAttribute(); errr != nil {
t.Fatal(errr)
} else {
// signerInfo contentType attribute must match signedData
// encapsulatedContentInfo content type.
if !ct.Equal(sd.EncapContentInfo.EContentType) {
t.Fatalf("expected %s content, got %s", sd.EncapContentInfo.EContentType.String(), ct.String())
}
}
if md, errr := si.GetMessageDigestAttribute(); errr != nil {
t.Fatal(errr)
} else if len(md) == 0 {
t.Fatal("nil/empty message digest attribute")
}
if algo := si.X509SignatureAlgorithm(); algo == x509.UnknownSignatureAlgorithm {
t.Fatalf("unknown signature algorithm")
}
var nilTime time.Time
if st, errr := si.GetSigningTimeAttribute(); errr != nil {
t.Fatal(errr)
} else if st == nilTime {
t.Fatal("0 value signing time")
}
}
// round trip resp
der, err := protocol.BER2DER(ber)
if err != nil {
t.Fatal(err)
}
der2, err := asn1.Marshal(resp)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(der, der2) {
t.Fatal("re-encoded contentInfo doesn't match original")
}
// round trip signedData
der = resp.TimeStampToken.Content.Bytes
der2, err = asn1.Marshal(*sd)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(der, der2) {
t.Fatal("re-encoded signedData doesn't match original")
}
}
var fixtureTimestampSymantec = mustBase64Decode("" +
"MIIDnjADAgEAMIIDlQYJKoZIhvcNAQcCoIIDhjCCA4ICAQMxDTALBglghkgBZQMEAgEwggEOBgsqhkiG" +
"9w0BCRABBKCB/gSB+zCB+AIBAQYLYIZIAYb4RQEHFwMwMTANBglghkgBZQMEAgEFAAQgWJG1tSLV3wht" +
"D/CxEPvZ0hu0/HFjrzTQgoai6Eb2vgMCFHERZNISITpb8tPCqDQtcNGcWhhSGA8yMDE4MDUwOTE0NTQy" +
"MlowAwIBHqCBhqSBgzCBgDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9u" +
"MR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBTSEEyNTYg" +
"VGltZVN0YW1waW5nIFNpZ25lciAtIEcyMYICWjCCAlYCAQEwgYswdzELMAkGA1UEBhMCVVMxHTAbBgNV" +
"BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMSgw" +
"JgYDVQQDEx9TeW1hbnRlYyBTSEEyNTYgVGltZVN0YW1waW5nIENBAhBUWPKq10HWRLyEqXugllLmMAsG" +
"CWCGSAFlAwQCAaCBpDAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4" +
"MDUwOTE0NTQyMlowLwYJKoZIhvcNAQkEMSIEIF/3JTU7CB+pzL3Mf+8BKgIRZQlDbovL5WzNhyeTSCn6" +
"MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIM96wXrQR+zV/cNoIgMbEtTvB4tvK0xea6Qfj/LPS61nMAsG" +
"CSqGSIb3DQEBAQSCAQCRxSB9MLAzK4YnNoFqIK9i71b011Q4pcyF6FEffC3ihOHjdmaHf/rFCeuv4roh" +
"yGxW9cRTshE8UohcghMEuSbkSyaFtVt37o31NC1IvW0vouJVQ0j0rg6nQjlsO9rMGW7cJOS2lVnREqk5" +
"+WfBMKJVnuYSXrnUdxcjSG++4eBCEF5L1fdCVjm4s1hagEORimvUoKuStibW0lwE8rdOEBjusZjRPDV6" +
"hudDhI+2SJPCAFhnNaDDT73y+Ux4x5cVdxHV+tME8kUrr6Hm/l6EyPxu/jwrV/EdJFVsJfkemdJz/ACa" +
"EbbTXfP8KuOwEyUwbFbRCXqO+Z6Gg0RqpiAZWCSM",
)
var fixtureTimestampSymantecWithCerts = mustBase64Decode("" +
"MIIOLTADAgEAMIIOJAYJKoZIhvcNAQcCoIIOFTCCDhECAQMxDTALBglghkgBZQMEAgEwggEOBgsqhkiG" +
"9w0BCRABBKCB/gSB+zCB+AIBAQYLYIZIAYb4RQEHFwMwMTANBglghkgBZQMEAgEFAAQgWJG1tSLV3wht" +
"D/CxEPvZ0hu0/HFjrzTQgoai6Eb2vgMCFDSZty7Ob7ZraC01Jcblagd3PcnYGA8yMDE4MDUwOTE4MjUy" +
"MlowAwIBHqCBhqSBgzCBgDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9u" +
"MR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBTSEEyNTYg" +
"VGltZVN0YW1waW5nIFNpZ25lciAtIEczoIIKizCCBTgwggQgoAMCAQICEHsFsdRJaFFE98mJ0pwZnRIw" +
"DQYJKoZIhvcNAQELBQAwgb0xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0G" +
"A1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDIwMDggVmVyaVNpZ24sIElu" +
"Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE4MDYGA1UEAxMvVmVyaVNpZ24gVW5pdmVyc2FsIFJv" +
"b3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTYwMTEyMDAwMDAwWhcNMzEwMTExMjM1OTU5WjB3" +
"MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFu" +
"dGVjIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMTH1N5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0Ew" +
"ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7WZ1ZVU+djHJdGoGi61XzsAGtPHGsMo8Fa4aa" +
"JwAyl2pNyWQUSym7wtkpuS7sY7Phzz8LVpD4Yht+66YH4t5/Xm1AONSRBudBfHkcy8utG7/YlZHz8O5s" +
"+K2WOS5/wSe4eDnFhKXt7a+Hjs6Nx23q0pi1Oh8eOZ3D9Jqo9IThxNF8ccYGKbQ/5IMNJsN7CD5N+Qq3" +
"M0n/yjvU9bKbS+GImRr1wOkzFNbfx4Dbke7+vJJXcnf0zajM/gn1kze+lYhqxdz0sUvUzugJkV+1hHk1" +
"inisGTKPI8EyQRtZDqk+scz51ivvt9jk1R1tETqS9pPJnONI7rtTDtQ2l4Z4xaE3AgMBAAGjggF3MIIB" +
"czAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADBmBgNVHSAEXzBdMFsGC2CGSAGG+EUB" +
"BxcDMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0" +
"dHBzOi8vZC5zeW1jYi5jb20vcnBhMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL3Mu" +
"c3ltY2QuY29tMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNiLmNvbS91bml2ZXJzYWwtcm9v" +
"dC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwgwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFt" +
"cC0yMDQ4LTMwHQYDVR0OBBYEFK9j1sqjToVy4Ke8QfMpojh/gHViMB8GA1UdIwQYMBaAFLZ3+mlIR59T" +
"EtXC6gcydgfRlwcZMA0GCSqGSIb3DQEBCwUAA4IBAQB16rAt1TQZXDJF/g7h1E+meMFv1+rd3E/zociB" +
"iPenjxXmQCmt5l30otlWZIRxMCrdHmEXZiBWBpgZjV1x8viXvAn9HJFHyeLojQP7zJAv1gpsTjPs1rST" +
"yEyQY0g5QCHE3dZuiZg8tZiX6KkGtwnJj1NXQZAv4R5NTtzKEHhsQm7wtsX4YVxS9U72a433Snq+8839" +
"A9fZ9gOoD+NT9wp17MZ1LqpmhQSZt/gGV+HGDvbor9rsmxgfqrnjOgC/zoqUywHbnsc4uw9Sq9HjlANg" +
"Ck2g/idtFDL8P5dA4b+ZidvkORS92uTTw+orWrOVWFUEfcea7CMDjYUq0v+uqWGBMIIFSzCCBDOgAwIB" +
"AgIQe9Tlr7rMBz+hASMEIkFNEjANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJVUzEdMBsGA1UEChMU" +
"U3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxKDAmBgNV" +
"BAMTH1N5bWFudGVjIFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMTcxMjIzMDAwMDAwWhcNMjkwMzIy" +
"MjM1OTU5WjCBgDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD" +
"VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTEwLwYDVQQDEyhTeW1hbnRlYyBTSEEyNTYgVGltZVN0" +
"YW1waW5nIFNpZ25lciAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArw6Kqvjcv2l7" +
"VBdxRwm9jTyB+HQVd2eQnP3eTgKeS3b25TY+ZdUkIG0w+d0dg+k/J0ozTm0WiuSNQI0iqr6nCxvSB7Y8" +
"tRokKPgbclE9yAmIJgg6+fpDI3VHcAyzX1uPCB1ySFdlTa8CPED39N0yOJM/5Sym81kjy4DeE035EMmq" +
"ChhsVWFX0fECLMS1q/JsI9KfDQ8ZbK2FYmn9ToXBilIxq1vYyXRS41dsIr9Vf2/KBqs/SrcidmXs7Dby" +
"lpWBJiz9u5iqATjTryVAmwlT8ClXhVhe6oVIQSGH5d600yaye0BTWHmOUjEGTZQDRcTOPAPstwDyOiLF" +
"tG/l77CKmwIDAQABo4IBxzCCAcMwDAYDVR0TAQH/BAIwADBmBgNVHSAEXzBdMFsGC2CGSAGG+EUBBxcD" +
"MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBz" +
"Oi8vZC5zeW1jYi5jb20vcnBhMEAGA1UdHwQ5MDcwNaAzoDGGL2h0dHA6Ly90cy1jcmwud3Muc3ltYW50" +
"ZWMuY29tL3NoYTI1Ni10c3MtY2EuY3JsMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQE" +
"AwIHgDB3BggrBgEFBQcBAQRrMGkwKgYIKwYBBQUHMAGGHmh0dHA6Ly90cy1vY3NwLndzLnN5bWFudGVj" +
"LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL3RzLWFpYS53cy5zeW1hbnRlYy5jb20vc2hhMjU2LXRzcy1j" +
"YS5jZXIwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0yMDQ4LTYwHQYDVR0OBBYEFKUT" +
"AamfhcwbbhYeXzsxqnk2AHsdMB8GA1UdIwQYMBaAFK9j1sqjToVy4Ke8QfMpojh/gHViMA0GCSqGSIb3" +
"DQEBCwUAA4IBAQBGnq/wuKJfoplIz6gnSyHNsrmmcnBjL+NVKXs5Rk7nfmUGWIu8V4qSDQjYELo2JPoK" +
"e/s702K/SpQV5oLbilRt/yj+Z89xP+YzCdmiWRD0Hkr+Zcze1GvjUil1AEorpczLm+ipTfe0F1mSQcO3" +
"P4bm9sB/RDxGXBda46Q71Wkm1SF94YBnfmKst04uFZrlnCOvWxHqcalB+Q15OKmhDc+0sdo+mnrHIsV0" +
"zd9HCYbE/JElshuW6YUI6N3qdGBuYKVWeg3IRFjc5vlIFJ7lv94AvXexmBRyFCTfxxEsHwA/w0sUxmcc" +
"zB4Go5BfXFSLPuMzW4IPxbeGAk5xn+lmRT92MYICWjCCAlYCAQEwgYswdzELMAkGA1UEBhMCVVMxHTAb" +
"BgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3Jr" +
"MSgwJgYDVQQDEx9TeW1hbnRlYyBTSEEyNTYgVGltZVN0YW1waW5nIENBAhB71OWvuswHP6EBIwQiQU0S" +
"MAsGCWCGSAFlAwQCAaCBpDAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8X" +
"DTE4MDUwOTE4MjUyMlowLwYJKoZIhvcNAQkEMSIEIF5EOTCml8PvDOxSGeQnbCv+jXprtZlEut7wcOx/" +
"xjfvMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIMR0znYAfQI5Tg2l5N58FMaA+eKCATz+9lPvXbcf32H4" +
"MAsGCSqGSIb3DQEBAQSCAQBD1SGuMSSNtmwg38x/1d8v+uvX/2aPIJQS//p5Q54Y8moIEeezRhG0tK3N" +
"81tfKdLeYTVE6VL8D7ZaCpbKzNJeD6DQM4S87bzH88H5RQOb2JTCvBPF3C/ytcl7ylezx6xsFNtftbW3" +
"IOXETaWLgIBpeL7jUZQDhgQ4Xb9HeFl4vA6Wk2kR88h+8Tv2ci0AI9hZgHhH9c/OwPvd8TKbhSjK9qXK" +
"DjaJr0BeVuYHPSWxfsxWVCOjNIOg7moWpPLSYQpqM2gdg5ppjQWffWYC4rywmM6XsBKs+EKFb++4GSOc" +
"wc6JJCugxm8Ba1a6nDAAAQYf/pQyBRRlh/qCHZ0rIoFq",
)
var fixtureTimestampDigicert = mustBase64Decode("" +
"MIIOuTADAgEAMIIOsAYJKoZIhvcNAQcCoIIOoTCCDp0CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG" +
"9w0BCRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIFiRtbUi1d8IbQ/wsRD7" +
"2dIbtPxxY6800IKGouhG9r4DAhAvZIfDsFuq0GRqVn9Wu2I8GA8yMDE4MDUwOTE4NDgxOFqgggu7MIIG" +
"gjCCBWqgAwIBAgIQCcD8RsgEQhO1WYuvKE9OQTANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEV" +
"MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhE" +
"aWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTE3MDEwNDAwMDAwMFoXDTI4" +
"MDExODAwMDAwMFowTDELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSowKAYDVQQDEyFEaWdp" +
"Q2VydCBTSEEyIFRpbWVzdGFtcCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" +
"AQCelZhqNDtzG6h+/Me+KWmJx2gmRl89jWJzh4GjoZzwt1skN1qS1PRZ13aJ5NzVJ/DVZrwK7rQrMWes" +
"WMVKkVkrRR4JAdZks1nujWZN+yNezBANC4pn71KuoAiQwlL39ai1bpsse53ntT77eM0yUBi/QLVMjLtX" +
"9KBPEUVsQkK55a/W3/SnfApolg/SXylXzvsdMv/0EaETIvsSy+/XU9Lrl8uirBsdnVghUYLCwt7qKz8s" +
"IoTQQ+w7Oz9HxPZW3EU3mLRrdLVZr3hXacgPCQJ43dhTwZnbYMSd6q6v4H6GSlypWGGoXnSKAShock6n" +
"hp21AlKHcGZI047vgSTM3NhlAgMBAAGjggM4MIIDNDAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw" +
"ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDCCAb8GA1UdIASCAbYwggGyMIIBoQYJYIZIAYb9bAcBMIIB" +
"kjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCCAWQGCCsGAQUFBwICMIIB" +
"Vh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUA" +
"IABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUA" +
"IABEAGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5AGkA" +
"bgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBpAHQA" +
"IABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUA" +
"ZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwHwYD" +
"VR0jBBgwFoAU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHQYDVR0OBBYEFOGnMkruASEofVTV8geSbrQHDz2H" +
"MHEGA1UdHwRqMGgwMqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMu" +
"Y3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDCBhQYI" +
"KwYBBQUHAQEEeTB3MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTwYIKwYBBQUH" +
"MAKGQ2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURUaW1lc3Rh" +
"bXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggEBAB7wQYIyru3xtDUT3FDC1ZeuIiKdDg6vM9NM/Xy/" +
"bwERp5RlIlzGIqHIiVJrmoxzXNlePzLeFmBMizb9MZkKvcGEt40d74kmEwVW80fNR1uthLI4r2ojtUXj" +
"HogyRoDSt6aZIv3BeM/1i9gMjAUJ7kTmgNVtcMyfUx4n3SpI3tqTZa1uZaOZp8JADnPMWE+PRSjlvJyI" +
"5ijOYF0tJV2Lcy6lDVtR2ppO/1AFiSja8ni70lh4jUSnrDoAkXhpiWQE012W3yq/+aVMLJP/5ordgqzx" +
"0rOihprBVYlWakc/+tYzlUM1iQV4Wjpp2iK4BEPTb2g1NnoUPkXpmGSGDxMMJkowggUxMIIEGaADAgEC" +
"AhAKoSXW1jIbfkHkBdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE" +
"aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFz" +
"c3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAwMDBaFw0zMTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYT" +
"AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV" +
"BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqGSIb3DQEB" +
"AQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdFM1EQfdD5fU1ofue2oPSNs4jk" +
"l79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ/l9lP+Cb6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX7" +
"86O6Ij4YrBHk8JkDbTuFfAnT7l3ImgtU46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8" +
"YGqsLwfM/fDqR9mIUF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F0IQZchfx" +
"FwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHOMIIByjAdBgNVHQ4EFgQU9Lbh" +
"IB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/" +
"BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEE" +
"bTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6" +
"Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6" +
"MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j" +
"cmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j" +
"cmwwUAYDVR0gBEkwRzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2lj" +
"ZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IBAQBxlRLpUYdWac3v3dp8qmN6" +
"s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwhWiq3BTQdaq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli" +
"6F6CJR7Euhx7LCHi1lssFDVDBGiy23UC4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSl" +
"gNcSR3CzddWThZN+tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UBJrZspe6H" +
"USHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0e/VWMyIvIjayS6JKldj1po5S" +
"MYICTTCCAkkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE" +
"CxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVz" +
"dGFtcGluZyBDQQIQCcD8RsgEQhO1WYuvKE9OQTANBglghkgBZQMEAgEFAKCBmDAaBgkqhkiG9w0BCQMx" +
"DQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4MDUwOTE4NDgxOFowLwYJKoZIhvcNAQkEMSIE" +
"IDpdtczqob9pSfKx5ZEHQZSSHM3P+8uGHy1rXmrK9iUjMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFEAB" +
"kUdcmIkd66EEr0cJG1621MvLMA0GCSqGSIb3DQEBAQUABIIBAIlFY+12XT6zvj4/0LVL5//VunTmYTKg" +
"Z6eSrafFT9zOvGbDzm/8XnDLrUQq9Y4kQpE+eKfHWJOBQQZ0ze0wftUml+iRsvqEVlax7G03SzHyPIYH" +
"HzEH/IKRlryHR5LgzzeFqS6IdVg18FBLvrs2fvPJlsj0ZGmAbwn6ntHDromtnkwZV6Cir5gH+wSKuA+Z" +
"3Qj5odgrTQ9gmbmNlFgwp4BwH/vFbBB1eIt7EUD1KfZzThfdFYHnyl8eRcE5p5+MxvyAC78fPzlSlJJP" +
"OES5LDDTx/Qvhet0PjJv70Z7kKgMmAA0BMTRuTnGfiVfEoFm2bzoKmwprU38EPz+PVnrbUA=",
)
var fixtureTimestampComodo = mustBase64Decode("" +
"MIIDuDADAgEAMIIDrwYJKoZIhvcNAQcCoIIDoDCCA5wCAQMxDzANBglghkgBZQMEAgEFADCCAQ8GCyqG" +
"SIb3DQEJEAEEoIH/BIH8MIH5AgEBBgorBgEEAbIxAgEBMDEwDQYJYIZIAWUDBAIBBQAEIFiRtbUi1d8I" +
"bQ/wsRD72dIbtPxxY6800IKGouhG9r4DAhUA4Fc3zQPRFgrg3c8/sksclhBco7QYDzIwMTgwNTA5MTg0" +
"NzQyWqCBjKSBiTCBhjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G" +
"A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxLDAqBgNVBAMTI0NPTU9ETyBT" +
"SEEtMjU2IFRpbWUgU3RhbXBpbmcgU2lnbmVyMYICcTCCAm0CAQEwgaowgZUxCzAJBgNVBAYTAlVTMQsw" +
"CQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1Qg" +
"TmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNF" +
"UkZpcnN0LU9iamVjdAIQTrCHj8wkNTay2Mn3vzlVdzANBglghkgBZQMEAgEFAKCBmDAaBgkqhkiG9w0B" +
"CQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4MDUwOTE4NDc0MlowKwYLKoZIhvcNAQkQ" +
"AgwxHDAaMBgwFgQUNlJ9T6JqaPnrRZbx2Zq7LA6nbfowLwYJKoZIhvcNAQkEMSIEIJeVWgArDRySkAZc" +
"F6na8PZrsUBoQs2jUzy94iOFYfM6MA0GCSqGSIb3DQEBAQUABIIBAKKV56NeTuFn4VdoNv15X0bUWG3p" +
"JSMRVbp1CWktnraj7E5m3BUmFlb4Dwrf3IMmE4QJrGrzDUWtUmpnHR4VuGAUmyajEcmDICc2gpBBG+aV" +
"0Ng/lXQ1xAotKkU7/4wNQY1nOBsquZykYRHWbzJaVxaq8VEc0nVZY2o1TVDgWtLF7BHAd96vw4iVuG3O" +
"Pb8izdFMwQ0t/TMNq0FD0hEFQDSTvVkayeaficblGbhf/p1xuCxSMoFBmnfO56aRX01E3SDNAgo3/hFl" +
"na2g8ESpdWHRMqG3+8ehvgMwljUnhj5+iYT1YF7Rm6KcV2TCIh6QyokN42ji4BMqTlBA7vzSx5A=",
)
var fixtureTimestampGlobalSign = mustBase64Decode("" +
"MIIDoTADAgEAMIIDmAYJKoZIhvcNAQcCoIIDiTCCA4UCAQMxCzAJBgUrDgMCGgUAMIHdBgsqhkiG9w0B" +
"CRABBKCBzQSByjCBxwIBAQYJKwYBBAGgMgICMDEwDQYJYIZIAWUDBAIBBQAEIFiRtbUi1d8IbQ/wsRD7" +
"2dIbtPxxY6800IKGouhG9r4DAhRYZmxGjSg8ojY0mWZG3dUdVW0mAxgPMjAxODA1MDkxODQ2MjRaoF2k" +
"WzBZMQswCQYDVQQGEwJTRzEfMB0GA1UEChMWR01PIEdsb2JhbFNpZ24gUHRlIEx0ZDEpMCcGA1UEAxMg" +
"R2xvYmFsU2lnbiBUU0EgZm9yIFN0YW5kYXJkIC0gRzIxggKRMIICjQIBATBoMFIxCzAJBgNVBAYTAkJF" +
"MRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSgwJgYDVQQDEx9HbG9iYWxTaWduIFRpbWVzdGFtcGlu" +
"ZyBDQSAtIEcyAhIRIbRVNR67GrJPl+8H/iqzC4owCQYFKw4DAhoFAKCB/zAaBgkqhkiG9w0BCQMxDQYL" +
"KoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTE4MDUwOTE4NDYyNFowIwYJKoZIhvcNAQkEMRYEFOmL" +
"BqSyLEaL7tN+hDwnk6fha6wfMIGdBgsqhkiG9w0BCRACDDGBjTCBijCBhzCBhAQUg/3hunb+9VKRtQ1o" +
"YZBtqkW1jLUwbDBWpFQwUjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExKDAm" +
"BgNVBAMTH0dsb2JhbFNpZ24gVGltZXN0YW1waW5nIENBIC0gRzICEhEhtFU1Hrsask+X7wf+KrMLijAN" +
"BgkqhkiG9w0BAQEFAASCAQBhWhjTagaTyATim1IHw0tF0wb22rlj6qXki86lclB/2uxBC8/3uLVd259z" +
"iz7aaTmxSj3ksMBq9A75beQW5Be9vK00B/mj/p1dLrtgCcYZtV4uhoBkBx0YbriumEnvQoQL1bI1EiXh" +
"TDbdTrGs2wXn3Xzw/qwqc7w+IjW1BjqzLf6BB9jw2raxMuWBA3EGMwGTumRx5x6a7j2Jx/9Uhs+3ce+9" +
"ZRDtiWAFCkTQVvNLrAuHLTFK6lLOqfucrru76adpJMlTJ+VRut0adpwviS1Cb2ifIX1iUHjtGssihk6v" +
"/tt7Yo4J341G5pC4JDXXhJvxHImNew3l0BWM0LROEgLM",
)
func mustBase64Decode(b64 string) []byte {
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(b64))
buf := new(bytes.Buffer)
if _, err := io.Copy(buf, decoder); err != nil {
panic(err)
}
return buf.Bytes()
}
// The fixtures above are complete TimeStampResp's, but most of our tests only
// care about the TimeStampToken (CMS ContentInfo) part of it.
func mustExtractTimeStampToken(ber []byte) []byte {
resp, err := ParseResponse(ber)
if err != nil {
panic(err)
}
tstDER, err := asn1.Marshal(resp.TimeStampToken)
if err != nil {
panic(err)
}
return tstDER
}