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  }