go_S-MIME/timestamp/request.go

124 lines
3.2 KiB
Go

package timestamp
import (
"bytes"
"fmt"
"io"
"net/http"
"crypto"
"crypto/rand"
"crypto/x509/pkix"
"encoding/asn1"
"math/big"
cms "git.ma-al.com/goc_marek/go_S-MIME/cms/protocol"
oid "git.ma-al.com/goc_marek/go_S-MIME/oid"
)
// 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 {
Version int
MessageImprint MessageImprint
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
Nonce *big.Int `asn1:"optional"`
CertReq bool `asn1:"optional,default:false"`
Extensions []pkix.Extension `asn1:"tag:1,optional"`
}
func newTSRequest(msg []byte, hash crypto.Hash) (TimeStampReq, error) {
mi, err := NewMessageImprint(hash, msg)
if err != nil {
return TimeStampReq{}, err
}
return TimeStampReq{
Version: 1,
CertReq: true,
Nonce: GenerateNonce(),
MessageImprint: mi,
}, nil
}
// GenerateNonce generates a new nonce for this TSR.
func GenerateNonce() *big.Int {
buf := make([]byte, nonceBytes)
if _, err := rand.Read(buf); err != nil {
panic(err)
}
return new(big.Int).SetBytes(buf[:])
}
// Do sends this timestamp request to the specified timestamp service, returning
// the parsed response.
func (req TimeStampReq) Do(url string) (TimeStampResp, error) {
var nilResp TimeStampResp
reqDER, err := asn1.Marshal(req)
if err != nil {
return nilResp, err
}
httpReq, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(reqDER))
if err != nil {
return nilResp, err
}
httpReq.Header.Add("Content-Type", contentTypeTSQuery)
HTTP := http.DefaultClient
httpResp, err := HTTP.Do(httpReq)
if err != nil {
return nilResp, err
}
if ct := httpResp.Header.Get("Content-Type"); ct != contentTypeTSReply {
return nilResp, fmt.Errorf("Bad content-type: %s", ct)
}
buf := bytes.NewBuffer(make([]byte, 0, httpResp.ContentLength))
if _, err = io.Copy(buf, httpResp.Body); err != nil {
return nilResp, err
}
return ParseResponse(buf.Bytes())
}
// MessageImprint ::= SEQUENCE {
// hashAlgorithm AlgorithmIdentifier,
// hashedMessage OCTET STRING }
type MessageImprint struct {
HashAlgorithm pkix.AlgorithmIdentifier
HashedMessage []byte
}
// NewMessageImprint creates a new MessageImprint, digesting msg using the specified hash.
func NewMessageImprint(hash crypto.Hash, msg []byte) (MessageImprint, error) {
digestAlgorithm := oid.HashToDigestAlgorithm[hash]
if len(digestAlgorithm) == 0 {
return MessageImprint{}, cms.ErrUnsupported
}
if !hash.Available() {
return MessageImprint{}, cms.ErrUnsupported
}
h := hash.New()
if _, err := h.Write(msg); err != nil {
return MessageImprint{}, err
}
return MessageImprint{
HashAlgorithm: pkix.AlgorithmIdentifier{Algorithm: digestAlgorithm},
HashedMessage: h.Sum(nil),
}, nil
}