break timestamp stuff into its own package
This commit is contained in:
Родитель
c546472472
Коммит
84daa31972
|
@ -8,7 +8,7 @@ import (
|
||||||
var encodeIndent = 0
|
var encodeIndent = 0
|
||||||
|
|
||||||
type asn1Object interface {
|
type asn1Object interface {
|
||||||
EncodeTo(writer *bytes.Buffer) error
|
encodeTo(writer *bytes.Buffer) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type asn1Structured struct {
|
type asn1Structured struct {
|
||||||
|
@ -16,12 +16,12 @@ type asn1Structured struct {
|
||||||
content []asn1Object
|
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)
|
//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
|
||||||
encodeIndent++
|
encodeIndent++
|
||||||
inner := new(bytes.Buffer)
|
inner := new(bytes.Buffer)
|
||||||
for _, obj := range s.content {
|
for _, obj := range s.content {
|
||||||
err := obj.EncodeTo(inner)
|
err := obj.encodeTo(inner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ type asn1Primitive struct {
|
||||||
content []byte
|
content []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
|
func (p asn1Primitive) encodeTo(out *bytes.Buffer) error {
|
||||||
_, err := out.Write(p.tagBytes)
|
_, err := out.Write(p.tagBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -54,7 +54,8 @@ func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
|
||||||
return nil
|
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 {
|
if len(ber) == 0 {
|
||||||
return nil, errors.New("ber2der: input ber is empty")
|
return nil, errors.New("ber2der: input ber is empty")
|
||||||
}
|
}
|
||||||
|
@ -65,7 +66,7 @@ func ber2der(ber []byte) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
obj.EncodeTo(out)
|
obj.encodeTo(out)
|
||||||
|
|
||||||
// if offset < len(ber) {
|
// if offset < len(ber) {
|
||||||
// return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", 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
|
// indefinite length fixture
|
||||||
ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00}
|
ber := []byte{0x30, 0x80, 0x02, 0x01, 0x01, 0x00, 0x00}
|
||||||
expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01}
|
expected := []byte{0x30, 0x03, 0x02, 0x01, 0x01}
|
||||||
der, err := ber2der(ber)
|
der, err := BER2DER(ber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ber2der failed with error: %v", err)
|
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)
|
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)
|
t.Errorf("ber2der on DER bytes failed with error: %v", err)
|
||||||
} else {
|
} else {
|
||||||
if !bytes.Equal(der, der2) {
|
if !bytes.Equal(der, der2) {
|
||||||
|
@ -50,7 +50,7 @@ func TestBer2Der_Negatives(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fixture := range fixtures {
|
for _, fixture := range fixtures {
|
||||||
_, err := ber2der(fixture.Input)
|
_, err := BER2DER(fixture.Input)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("No error thrown. Expected: %s", fixture.ErrorContains)
|
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}
|
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}
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("ber2der failed with error: %v", err)
|
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)
|
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)
|
t.Errorf("ber2der on DER bytes failed with error: %v", err)
|
||||||
} else {
|
} else {
|
||||||
if !bytes.Equal(der, der2) {
|
if !bytes.Equal(der, der2) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ type ContentInfo struct {
|
||||||
// ParseContentInfo parses a top-level ContentInfo type from BER encoded data.
|
// ParseContentInfo parses a top-level ContentInfo type from BER encoded data.
|
||||||
func ParseContentInfo(ber []byte) (ci ContentInfo, err error) {
|
func ParseContentInfo(ber []byte) (ci ContentInfo, err error) {
|
||||||
var der []byte
|
var der []byte
|
||||||
if der, err = ber2der(ber); err != nil {
|
if der, err = BER2DER(ber); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,17 +92,6 @@ func NewDataEncapsulatedContentInfo(data []byte) (EncapsulatedContentInfo, error
|
||||||
return NewEncapsulatedContentInfo(data, oid.Data)
|
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.
|
// NewEncapsulatedContentInfo creates a new EncapsulatedContentInfo.
|
||||||
func NewEncapsulatedContentInfo(content []byte, contentType asn1.ObjectIdentifier) (EncapsulatedContentInfo, error) {
|
func NewEncapsulatedContentInfo(content []byte, contentType asn1.ObjectIdentifier) (EncapsulatedContentInfo, error) {
|
||||||
octets, err := asn1.Marshal(asn1.RawValue{
|
octets, err := asn1.Marshal(asn1.RawValue{
|
||||||
|
@ -191,35 +180,6 @@ func (eci EncapsulatedContentInfo) DataEContent() ([]byte, error) {
|
||||||
return eci.EContentValue()
|
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 {
|
// Attribute ::= SEQUENCE {
|
||||||
// attrType OBJECT IDENTIFIER,
|
// attrType OBJECT IDENTIFIER,
|
||||||
// attrValues SET OF AttributeValue }
|
// attrValues SET OF AttributeValue }
|
||||||
|
|
|
@ -2,13 +2,11 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -261,26 +259,6 @@ func TestParseSignautreOutlookDetached(t *testing.T) {
|
||||||
testParseContentInfo(t, fixtureSignatureOutlookDetached)
|
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) {
|
func testParseContentInfo(t *testing.T, ber []byte) {
|
||||||
ci, err := ParseContentInfo(ber)
|
ci, err := ParseContentInfo(ber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -297,48 +275,20 @@ func testParseContentInfo(t *testing.T, ber []byte) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sd.EncapContentInfo.IsTypeData() {
|
if !sd.EncapContentInfo.IsTypeData() {
|
||||||
if !sd.EncapContentInfo.EContentType.Equal(oid.Data) {
|
t.Fatal("expected id-data econtent")
|
||||||
t.Fatalf("expected %s content, got %s", oid.Data.String(), sd.EncapContentInfo.EContentType.String())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
data, err := sd.EncapContentInfo.DataEContent()
|
if !sd.EncapContentInfo.EContentType.Equal(oid.Data) {
|
||||||
if err != nil {
|
t.Fatalf("expected %s content, got %s", oid.Data.String(), sd.EncapContentInfo.EContentType.String())
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
tsti, err := sd.EncapContentInfo.TSTInfoEContent()
|
data, err := sd.EncapContentInfo.DataEContent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if data != nil && len(data) == 0 {
|
||||||
hash, err := tsti.MessageImprint.Hash()
|
t.Fatal("attached signature with zero length data")
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, si := range sd.SignerInfos {
|
for _, si := range sd.SignerInfos {
|
||||||
|
@ -375,7 +325,7 @@ func testParseContentInfo(t *testing.T, ber []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// round trip contentInfo
|
// round trip contentInfo
|
||||||
der, err := ber2der(ber)
|
der, err := BER2DER(ber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -676,193 +626,6 @@ var fixturePFX = mustBase64Decode("" +
|
||||||
"AwIaBQAEFDPX7JM9l8ZnTwGGaDQQvlp7RiBKBAg2WsoFwawSzwICCAA=",
|
"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 {
|
func mustBase64Decode(b64 string) []byte {
|
||||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(b64))
|
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(b64))
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
@ -873,19 +636,3 @@ func mustBase64Decode(b64 string) []byte {
|
||||||
|
|
||||||
return buf.Bytes()
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -10,21 +10,24 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mastahyeti/cms/oid"
|
"github.com/mastahyeti/cms/oid"
|
||||||
|
"github.com/mastahyeti/cms/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TimeStampReq ::= SEQUENCE {
|
// Request is a TimeStampReq
|
||||||
// version INTEGER { v1(1) },
|
// TimeStampReq ::= SEQUENCE {
|
||||||
// messageImprint MessageImprint,
|
// version INTEGER { v1(1) },
|
||||||
// --a hash algorithm OID and the hash value of the data to be
|
// messageImprint MessageImprint,
|
||||||
// --time-stamped
|
// --a hash algorithm OID and the hash value of the data to be
|
||||||
// reqPolicy TSAPolicyId OPTIONAL,
|
// --time-stamped
|
||||||
// nonce INTEGER OPTIONAL,
|
// reqPolicy TSAPolicyId OPTIONAL,
|
||||||
// certReq BOOLEAN DEFAULT FALSE,
|
// nonce INTEGER OPTIONAL,
|
||||||
// extensions [0] IMPLICIT Extensions OPTIONAL }
|
// certReq BOOLEAN DEFAULT FALSE,
|
||||||
type TimeStampReq struct {
|
// extensions [0] IMPLICIT Extensions OPTIONAL }
|
||||||
|
type Request struct {
|
||||||
Version int
|
Version int
|
||||||
MessageImprint MessageImprint
|
MessageImprint MessageImprint
|
||||||
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
|
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
|
||||||
|
@ -36,34 +39,35 @@ type TimeStampReq struct {
|
||||||
const nonceBytes = 16
|
const nonceBytes = 16
|
||||||
|
|
||||||
// GenerateNonce generates a new nonce for this TSR.
|
// GenerateNonce generates a new nonce for this TSR.
|
||||||
func (tsr *TimeStampReq) GenerateNonce() {
|
func (r *Request) GenerateNonce() {
|
||||||
buf := make([]byte, nonceBytes)
|
buf := make([]byte, nonceBytes)
|
||||||
if _, err := rand.Read(buf); err != nil {
|
if _, err := rand.Read(buf); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tsr.Nonce == nil {
|
if r.Nonce == nil {
|
||||||
tsr.Nonce = new(big.Int)
|
r.Nonce = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
tsr.Nonce.SetBytes(buf[:])
|
r.Nonce.SetBytes(buf[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimeStampResp ::= SEQUENCE {
|
// Response is a TimeStampResp
|
||||||
// status PKIStatusInfo,
|
// TimeStampResp ::= SEQUENCE {
|
||||||
// timeStampToken TimeStampToken OPTIONAL }
|
// status PKIStatusInfo,
|
||||||
|
// timeStampToken TimeStampToken OPTIONAL }
|
||||||
//
|
//
|
||||||
// TimeStampToken ::= ContentInfo
|
// TimeStampToken ::= ContentInfo
|
||||||
type TimeStampResp struct {
|
type Response struct {
|
||||||
Status PKIStatusInfo
|
Status PKIStatusInfo
|
||||||
TimeStampToken ContentInfo `asn1:"optional"`
|
TimeStampToken protocol.ContentInfo `asn1:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseTimeStampResp parses a BER encoded TimeStampResp.
|
// ParseResponse parses a BER encoded TimeStampResp.
|
||||||
func ParseTimeStampResp(ber []byte) (TimeStampResp, error) {
|
func ParseResponse(ber []byte) (Response, error) {
|
||||||
var resp TimeStampResp
|
var resp Response
|
||||||
|
|
||||||
der, err := ber2der(ber)
|
der, err := protocol.BER2DER(ber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
@ -128,6 +132,35 @@ type PKIStatusInfo struct {
|
||||||
FailInfo asn1.BitString `asn1:"optional"`
|
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
|
// PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
|
||||||
type PKIFreeText []asn1.RawValue
|
type PKIFreeText []asn1.RawValue
|
||||||
|
|
||||||
|
@ -163,26 +196,27 @@ func (ft PKIFreeText) Strings() ([]string, error) {
|
||||||
return strs, nil
|
return strs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TSTInfo ::= SEQUENCE {
|
// Info is a Info
|
||||||
// version INTEGER { v1(1) },
|
// Info ::= SEQUENCE {
|
||||||
// policy TSAPolicyId,
|
// version INTEGER { v1(1) },
|
||||||
// messageImprint MessageImprint,
|
// policy TSAPolicyId,
|
||||||
// -- MUST have the same value as the similar field in
|
// messageImprint MessageImprint,
|
||||||
// -- TimeStampReq
|
// -- MUST have the same value as the similar field in
|
||||||
// serialNumber INTEGER,
|
// -- TimeStampReq
|
||||||
// -- Time-Stamping users MUST be ready to accommodate integers
|
// serialNumber INTEGER,
|
||||||
// -- up to 160 bits.
|
// -- Time-Stamping users MUST be ready to accommodate integers
|
||||||
// genTime GeneralizedTime,
|
// -- up to 160 bits.
|
||||||
// accuracy Accuracy OPTIONAL,
|
// genTime GeneralizedTime,
|
||||||
// ordering BOOLEAN DEFAULT FALSE,
|
// accuracy Accuracy OPTIONAL,
|
||||||
// nonce INTEGER OPTIONAL,
|
// ordering BOOLEAN DEFAULT FALSE,
|
||||||
// -- MUST be present if the similar field was present
|
// nonce INTEGER OPTIONAL,
|
||||||
// -- in TimeStampReq. In that case it MUST have the same value.
|
// -- MUST be present if the similar field was present
|
||||||
// tsa [0] GeneralName OPTIONAL,
|
// -- in TimeStampReq. In that case it MUST have the same value.
|
||||||
// extensions [1] IMPLICIT Extensions OPTIONAL }
|
// tsa [0] GeneralName OPTIONAL,
|
||||||
|
// extensions [1] IMPLICIT Extensions OPTIONAL }
|
||||||
//
|
//
|
||||||
// TSAPolicyId ::= OBJECT IDENTIFIER
|
// TSAPolicyId ::= OBJECT IDENTIFIER
|
||||||
type TSTInfo struct {
|
type Info struct {
|
||||||
Version int
|
Version int
|
||||||
Policy asn1.ObjectIdentifier
|
Policy asn1.ObjectIdentifier
|
||||||
MessageImprint MessageImprint
|
MessageImprint MessageImprint
|
||||||
|
@ -195,16 +229,41 @@ type TSTInfo struct {
|
||||||
Extensions []pkix.Extension `asn1:"tag:1,optional"`
|
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
|
// GenTimeMax is the latest time at which the token could have been generated
|
||||||
// based on the included GenTime and Accuracy attributes.
|
// based on the included GenTime and Accuracy attributes.
|
||||||
func (tsti *TSTInfo) GenTimeMax() time.Time {
|
func (i *Info) GenTimeMax() time.Time {
|
||||||
return tsti.GenTime.Add(tsti.Accuracy.Duration())
|
return i.GenTime.Add(i.Accuracy.Duration())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenTimeMin is the earliest time at which the token could have been generated
|
// GenTimeMin is the earliest time at which the token could have been generated
|
||||||
// based on the included GenTime and Accuracy attributes.
|
// based on the included GenTime and Accuracy attributes.
|
||||||
func (tsti *TSTInfo) GenTimeMin() time.Time {
|
func (i *Info) GenTimeMin() time.Time {
|
||||||
return tsti.GenTime.Add(-tsti.Accuracy.Duration())
|
return i.GenTime.Add(-i.Accuracy.Duration())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageImprint ::= SEQUENCE {
|
// MessageImprint ::= SEQUENCE {
|
|
@ -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
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче