github.com/clocklock/go-rfc3161@v0.0.0-20160419203229-5ea544d9dee0/request.go (about) 1 package rfc3161 2 3 import ( 4 "crypto" 5 "crypto/rand" 6 "crypto/x509/pkix" 7 "encoding/asn1" 8 "errors" 9 "io/ioutil" 10 "math/big" 11 12 "github.com/phayes/cryptoid" 13 ) 14 15 // Errors 16 var ( 17 ErrInvalidDigestSize = errors.New("rfc3161: Invalid Message Digest. Invalid size for the given hash algorithm") 18 ErrUnsupportedHash = errors.New("rfc3161: Unsupported Hash Algorithm") 19 ErrUnsupportedExt = errors.New("rfc3161: Unsupported Critical Extension") 20 ) 21 22 // TimeStampReq contains a full Time Stamp Request as defined by RFC 3161 23 // It is also known as a "Time Stamp Query" 24 // When stored into a file it should contain the extension ".tsq" 25 // It has a mime-type of "application/timestamp-query" 26 type TimeStampReq struct { 27 Version int `asn1:"default:1"` 28 MessageImprint MessageImprint // A hash algorithm OID and the hash value of the data to be time-stamped 29 ReqPolicy asn1.ObjectIdentifier `asn1:"optional"` // Identifier for the policy. For many TSA's, often the same as SignedData.DigestAlgorithm 30 Nonce *big.Int `asn1:"optional"` // Nonce could be up to 160 bits 31 CertReq bool `asn1:"optional"` // If set to true, the TSA's certificate MUST be provided in the response. 32 Extensions []pkix.Extension `asn1:"optional,tag:0"` 33 } 34 35 // MessageImprint contains hash algorithm OID and the hash digest of the data to be time-stamped 36 type MessageImprint struct { 37 HashAlgorithm pkix.AlgorithmIdentifier 38 HashedMessage []byte 39 } 40 41 // NewTimeStampReq creates a new Time Stamp Request, given a crypto.Hash algorithm and a message digest 42 func NewTimeStampReq(hash crypto.Hash, digest []byte) (*TimeStampReq, error) { 43 tsr := new(TimeStampReq) 44 tsr.Version = 1 45 46 err := tsr.SetHashDigest(hash, digest) 47 if err != nil { 48 return nil, err 49 } 50 51 return tsr, nil 52 } 53 54 // ReadTSQ reads a .tsq file into a TimeStampReq 55 func ReadTSQ(filename string) (*TimeStampReq, error) { 56 der, err := ioutil.ReadFile(filename) 57 if err != nil { 58 return nil, err 59 } 60 req := new(TimeStampReq) 61 rest, err := asn1.Unmarshal(der, req) 62 if err != nil { 63 return nil, err 64 } 65 if len(rest) != 0 { 66 return req, ErrUnrecognizedData 67 } 68 return req, nil 69 } 70 71 // SetHashDigest sets the Hash Algorithm and the Hash Digest for the Time Stamp Request 72 func (tsr *TimeStampReq) SetHashDigest(hash crypto.Hash, digest []byte) error { 73 if len(digest) != hash.Size() { 74 return ErrInvalidDigestSize 75 } 76 pkixAlgo := pkix.AlgorithmIdentifier{ 77 Algorithm: cryptoid.HashAlgorithmByCrypto(hash).OID, 78 } 79 80 tsr.MessageImprint.HashAlgorithm = pkixAlgo 81 tsr.MessageImprint.HashedMessage = digest 82 83 return nil 84 } 85 86 // GetHash will get the crypto.Hash for the Time Stamp Request 87 // The Hash will be 0 if it is not recognized 88 func (tsr *TimeStampReq) GetHash() crypto.Hash { 89 hashAlgo, err := cryptoid.HashAlgorithmByOID(tsr.MessageImprint.HashAlgorithm.Algorithm.String()) 90 if err != nil { 91 return 0 92 } 93 return hashAlgo.Hash 94 } 95 96 // GenerateNonce generates a 128 bit nonce for the Time Stamp Request 97 // If a different size is required then set manually with tsr.Nonce.SetBytes() 98 func (tsr *TimeStampReq) GenerateNonce() error { 99 // Generate a 128 bit nonce 100 b := make([]byte, 16, 16) 101 102 _, err := rand.Read(b) 103 if err != nil { 104 return err 105 } 106 107 tsr.Nonce = new(big.Int) 108 tsr.Nonce.SetBytes(b) 109 110 return nil 111 } 112 113 // Verify does a basic sanity check of the Time Stamp Request 114 // Checks to make sure the hash is supported, the digest matches the hash, 115 // and no unsupported critical extensions exist. Be sure to add all supported 116 // extentions to rfc3161.SupportedExtensions. 117 func (tsr *TimeStampReq) Verify() error { 118 hash := tsr.GetHash() 119 if hash == 0 { 120 return ErrUnsupportedHash 121 } 122 if len(tsr.MessageImprint.HashedMessage) != hash.Size() { 123 return ErrInvalidDigestSize 124 } 125 126 // Check for any unsupported critical extensions 127 // Critical Extensions should be registered in rfc3161.SupportedExtensions 128 if tsr.Extensions != nil { 129 for _, ext := range tsr.Extensions { 130 if ext.Critical { 131 supported := false 132 if supportedExtensions != nil { 133 for _, se := range supportedExtensions { 134 if se.Equal(ext.Id) { 135 supported = true 136 break 137 } 138 } 139 } 140 if !supported { 141 return ErrUnsupportedExt 142 } 143 } 144 } 145 } 146 147 return nil 148 }