...

Source file src/github.com/sassoftware/relic/lib/pkcs9/http.go

Documentation: github.com/sassoftware/relic/lib/pkcs9

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package pkcs9
    18  
    19  import (
    20  	"bytes"
    21  	"crypto"
    22  	"crypto/hmac"
    23  	"encoding/asn1"
    24  	"errors"
    25  	"fmt"
    26  	"net/http"
    27  
    28  	"github.com/sassoftware/relic/lib/pkcs7"
    29  	"github.com/sassoftware/relic/lib/x509tools"
    30  )
    31  
    32  // RFC 3161 timestamping
    33  
    34  // Create a HTTP request to request a token from the given URL
    35  func NewRequest(url string, hash crypto.Hash, hashValue []byte) (msg *TimeStampReq, req *http.Request, err error) {
    36  	alg, ok := x509tools.PkixDigestAlgorithm(hash)
    37  	if !ok {
    38  		return nil, nil, errors.New("unknown digest algorithm")
    39  	}
    40  	msg = &TimeStampReq{
    41  		Version: 1,
    42  		MessageImprint: MessageImprint{
    43  			HashAlgorithm: alg,
    44  			HashedMessage: hashValue,
    45  		},
    46  		Nonce:   x509tools.MakeSerial(),
    47  		CertReq: true,
    48  	}
    49  	reqbytes, err := asn1.Marshal(*msg)
    50  	if err != nil {
    51  		return
    52  	}
    53  	req, err = http.NewRequest("POST", url, bytes.NewReader(reqbytes))
    54  	if err != nil {
    55  		return
    56  	}
    57  	req.Header.Set("Content-Type", "application/timestamp-query")
    58  	return
    59  }
    60  
    61  // Parse a timestamp token from a HTTP response, sanity checking it against the original request nonce
    62  func (req *TimeStampReq) ParseResponse(body []byte) (*pkcs7.ContentInfoSignedData, error) {
    63  	respmsg := new(TimeStampResp)
    64  	if rest, err := asn1.Unmarshal(body, respmsg); err != nil {
    65  		return nil, fmt.Errorf("pkcs9: unmarshalling response: %s", err)
    66  	} else if len(rest) != 0 {
    67  		return nil, errors.New("pkcs9: trailing bytes in response")
    68  	} else if respmsg.Status.Status > StatusGrantedWithMods {
    69  		return nil, fmt.Errorf("pkcs9: request denied: status=%d failureInfo=%x", respmsg.Status.Status, respmsg.Status.FailInfo.Bytes)
    70  	}
    71  	if err := req.SanityCheckToken(&respmsg.TimeStampToken); err != nil {
    72  		return nil, fmt.Errorf("pkcs9: token sanity check failed: %s", err)
    73  	}
    74  	return &respmsg.TimeStampToken, nil
    75  }
    76  
    77  // Sanity check a timestamp token against the nonce in the original request
    78  func (req *TimeStampReq) SanityCheckToken(psd *pkcs7.ContentInfoSignedData) error {
    79  	if _, err := psd.Content.Verify(nil, false); err != nil {
    80  		return err
    81  	}
    82  	info, err := unpackTokenInfo(psd)
    83  	if err != nil {
    84  		return err
    85  	}
    86  	if req.Nonce.Cmp(info.Nonce) != 0 {
    87  		return errors.New("request nonce mismatch")
    88  	}
    89  	if !hmac.Equal(info.MessageImprint.HashedMessage, req.MessageImprint.HashedMessage) {
    90  		return errors.New("message imprint mismatch")
    91  	}
    92  	return nil
    93  }
    94  
    95  // Unpack TSTInfo from a timestamp token
    96  func unpackTokenInfo(psd *pkcs7.ContentInfoSignedData) (*TSTInfo, error) {
    97  	infobytes, err := psd.Content.ContentInfo.Bytes()
    98  	if err != nil {
    99  		return nil, fmt.Errorf("unpack TSTInfo: %s", err)
   100  	} else if infobytes[0] == 0x04 {
   101  		// unwrap dummy OCTET STRING
   102  		_, err = asn1.Unmarshal(infobytes, &infobytes)
   103  		if err != nil {
   104  			return nil, fmt.Errorf("unpack TSTInfo: %s", err)
   105  		}
   106  	}
   107  	info := new(TSTInfo)
   108  	if _, err := asn1.Unmarshal(infobytes, info); err != nil {
   109  		return nil, fmt.Errorf("unpack TSTInfo: %s", err)
   110  	}
   111  	return info, nil
   112  }
   113  

View as plain text