Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
|
6c337f1e30 | |
|
4f55b14d9f | |
|
543cd58803 | |
|
63a40da507 | |
|
90a141e752 | |
|
718b4b8a7c | |
|
a614afb557 |
|
@ -14,5 +14,8 @@
|
|||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 80%
|
||||
patch:
|
||||
default:
|
||||
target: 80%
|
|
@ -1,3 +1,3 @@
|
|||
# Repo-Level Owners (in alphabetical order)
|
||||
# Note: This is only for the notaryproject/tspclient-go repo
|
||||
* @gokarnm @JeyJeyGao @niazfk @priteshbandi @shizhMSFT @toddysm @Two-Hearts @vaninrao10 @yizha1
|
||||
* @gokarnm @niazfk @priteshbandi @shizhMSFT @toddysm @Two-Hearts @vaninrao10 @yizha1
|
||||
|
|
|
@ -10,10 +10,12 @@ Yi Zha <yizha1@microsoft.com> (@yizha1)
|
|||
# Repo-Level Maintainers (in alphabetical order)
|
||||
# Pattern: [First Name] [Last Name] <[Email Address]> ([GitHub Handle])
|
||||
# Note: This is for the notaryproject/tspclient-go repo
|
||||
Junjie Gao <junjiegao@microsoft.com> (@JeyJeyGao)
|
||||
Patrick Zheng <patrickzheng@microsoft.com> (@Two-Hearts)
|
||||
Shiwei Zhang <shizh@microsoft.com> (@shizhMSFT)
|
||||
|
||||
# Emeritus Org Maintainers (in alphabetical order)
|
||||
Justin Cormack <justin.cormack@docker.com> (@justincormack)
|
||||
Steve Lasker <StevenLasker@hotmail.com> (@stevelasker)
|
||||
Steve Lasker <StevenLasker@hotmail.com> (@stevelasker)
|
||||
|
||||
# Emeritus Repo-Level Maintainers (in alphabetical order)
|
||||
Junjie Gao <junjiegao@microsoft.com> (@JeyJeyGao)
|
|
@ -73,14 +73,9 @@ func TestTSATimestampGranted(t *testing.T) {
|
|||
|
||||
// do timestamp
|
||||
message := []byte("notation")
|
||||
nonce, err := generateNonce()
|
||||
if err != nil {
|
||||
t.Fatal("failed to create nonce:", err)
|
||||
}
|
||||
requestOpts := RequestOptions{
|
||||
Content: message,
|
||||
HashAlgorithm: crypto.SHA256,
|
||||
Nonce: nonce,
|
||||
}
|
||||
req, err := NewRequest(requestOpts)
|
||||
if err != nil {
|
||||
|
|
9
http.go
9
http.go
|
@ -53,9 +53,16 @@ func NewHTTPTimestamper(httpClient *http.Client, endpoint string) (Timestamper,
|
|||
if httpClient == nil {
|
||||
httpClient = &http.Client{Timeout: 5 * time.Second}
|
||||
}
|
||||
if _, err := url.Parse(endpoint); err != nil {
|
||||
tsaURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tsaURL.Scheme != "http" && tsaURL.Scheme != "https" {
|
||||
return nil, fmt.Errorf("endpoint %q: scheme must be http or https, but got %q", endpoint, tsaURL.Scheme)
|
||||
}
|
||||
if tsaURL.Host == "" {
|
||||
return nil, fmt.Errorf("endpoint %q: host cannot be empty", endpoint)
|
||||
}
|
||||
return &httpTimestamper{
|
||||
httpClient: httpClient,
|
||||
endpoint: endpoint,
|
||||
|
|
20
http_test.go
20
http_test.go
|
@ -67,12 +67,12 @@ func TestHTTPTimestampGranted(t *testing.T) {
|
|||
Tag: 5,
|
||||
FullBytes: []byte{5, 0},
|
||||
},
|
||||
NoNonce: true,
|
||||
}
|
||||
req, err := NewRequest(requestOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest() error = %v", err)
|
||||
}
|
||||
req.Nonce = nil
|
||||
ctx := context.Background()
|
||||
resp, err := tsa.Timestamp(ctx, req)
|
||||
if err != nil {
|
||||
|
@ -256,6 +256,24 @@ func TestNewHTTPTimestamper(t *testing.T) {
|
|||
if _, err := NewHTTPTimestamper(nil, malformedURL); err == nil || err.Error() != expectedErrMsg {
|
||||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
}
|
||||
|
||||
malformedURL = "invalid"
|
||||
expectedErrMsg = `endpoint "invalid": scheme must be http or https, but got ""`
|
||||
if _, err := NewHTTPTimestamper(nil, malformedURL); err == nil || err.Error() != expectedErrMsg {
|
||||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
}
|
||||
|
||||
malformedURL = "invalid://"
|
||||
expectedErrMsg = `endpoint "invalid://": scheme must be http or https, but got "invalid"`
|
||||
if _, err := NewHTTPTimestamper(nil, malformedURL); err == nil || err.Error() != expectedErrMsg {
|
||||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
}
|
||||
|
||||
malformedURL = "https://"
|
||||
expectedErrMsg = `endpoint "https://": host cannot be empty`
|
||||
if _, err := NewHTTPTimestamper(nil, malformedURL); err == nil || err.Error() != expectedErrMsg {
|
||||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHttpTimestamperTimestamp(t *testing.T) {
|
||||
|
|
|
@ -248,14 +248,19 @@ func (d *ParsedSignedData) verifySignedAttributes(signerInfo *SignerInfo, chains
|
|||
|
||||
// verify attributes if present
|
||||
if len(signerInfo.SignedAttributes) == 0 {
|
||||
if d.ContentType.Equal(oid.Data) {
|
||||
return nil, nil
|
||||
}
|
||||
// signed attributes MUST be present if the content type of the
|
||||
// EncapsulatedContentInfo value being signed is not id-data.
|
||||
// According to RFC 5652, if the Content Type is id-data, signed
|
||||
// attributes can be empty. However, this cms package is designed for
|
||||
// timestamp (RFC 3161) and the content type must be id-ct-TSTInfo,
|
||||
// so we require signed attributes to be present.
|
||||
return nil, VerificationError{Message: "missing signed attributes"}
|
||||
}
|
||||
|
||||
// this CMS package is designed for timestamping (RFC 3161), so checking the
|
||||
// content type to be id-ct-TSTInfo is an optimization for tspclient to
|
||||
// fail fast.
|
||||
if !oid.TSTInfo.Equal(d.ContentType) {
|
||||
return nil, fmt.Errorf("unexpected content type: %v. Expected to be id-ct-TSTInfo (%v)", d.ContentType, oid.TSTInfo)
|
||||
}
|
||||
var contentType asn1.ObjectIdentifier
|
||||
if err := signerInfo.SignedAttributes.Get(oid.ContentType, &contentType); err != nil {
|
||||
return nil, VerificationError{Message: "invalid content type", Detail: err}
|
||||
|
|
|
@ -160,7 +160,12 @@ func TestVerify(t *testing.T) {
|
|||
{
|
||||
name: "id-data content type without signed attributes",
|
||||
filePath: "testdata/SignedDataWithoutSignedAttributes.p7s",
|
||||
wantErr: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid content type",
|
||||
filePath: "testdata/TimeStampTokenWithInvalidContentType.p7s",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "an invalid and a valid signer info",
|
||||
|
|
Binary file not shown.
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
func TestConvertToDER(t *testing.T) {
|
||||
var testBytes = make([]byte, 0xFFFFFFFF+8)
|
||||
var testBytes = make([]byte, 0x7FFFFFFF)
|
||||
// primitive identifier
|
||||
testBytes[0] = 0x1f
|
||||
testBytes[1] = 0xa0
|
||||
|
|
28
request.go
28
request.go
|
@ -94,18 +94,6 @@ type RequestOptions struct {
|
|||
// Reference: https://datatracker.ietf.org/doc/html/rfc3161#section-2.4.1
|
||||
ReqPolicy asn1.ObjectIdentifier
|
||||
|
||||
// NoNonce disables any Nonce usage. When set to true, the Nonce field is
|
||||
// ignored, and no built-in Nonce will be generated. OPTIONAL.
|
||||
NoNonce bool
|
||||
|
||||
// Nonce is a large random number with a high probability that the client
|
||||
// generates it only once. The same nonce is included and validated in the
|
||||
// response. It is only used when NoNonce is not set to true.
|
||||
//
|
||||
// When this field is nil, a built-in Nonce will be generated and sent to
|
||||
// the TSA. OPTIONAL.
|
||||
Nonce *big.Int
|
||||
|
||||
// NoCert tells the TSA to not include any signing certificate in its
|
||||
// response. By default, TSA signing certificate is included in the response.
|
||||
// OPTIONAL.
|
||||
|
@ -133,17 +121,9 @@ func NewRequest(opts RequestOptions) (*Request, error) {
|
|||
if tspclientasn1.EqualRawValue(hashAlgParameter, asn1.RawValue{}) || tspclientasn1.EqualRawValue(hashAlgParameter, asn1.NullRawValue) {
|
||||
hashAlgParameter = asn1NullRawValue
|
||||
}
|
||||
var nonce *big.Int
|
||||
if !opts.NoNonce {
|
||||
if opts.Nonce != nil { // user provided Nonce, use it
|
||||
nonce = opts.Nonce
|
||||
} else { // user ignored Nonce, use built-in Nonce
|
||||
var err error
|
||||
nonce, err = generateNonce()
|
||||
if err != nil {
|
||||
return nil, &MalformedRequestError{Msg: err.Error()}
|
||||
}
|
||||
}
|
||||
nonce, err := generateNonce()
|
||||
if err != nil {
|
||||
return nil, &MalformedRequestError{Msg: err.Error()}
|
||||
}
|
||||
return &Request{
|
||||
Version: 1,
|
||||
|
@ -206,7 +186,7 @@ func generateNonce() (*big.Int, error) {
|
|||
// Pick a random number from 0 to 2^159
|
||||
nonce, err := rand.Int(rand.Reader, (&big.Int{}).Lsh(big.NewInt(1), 159))
|
||||
if err != nil {
|
||||
return nil, errors.New("error generating nonce")
|
||||
return nil, errors.New("failed to generate nonce")
|
||||
}
|
||||
return nonce, nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ package tspclient
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
@ -71,6 +72,21 @@ func TestNewRequest(t *testing.T) {
|
|||
if !reflect.DeepEqual(req.MessageImprint.HashAlgorithm.Parameters, asn1NullRawValue) {
|
||||
t.Fatalf("expected %v, but got %v", asn1NullRawValue, req.MessageImprint.HashAlgorithm.Parameters)
|
||||
}
|
||||
|
||||
defaultRandReader := rand.Reader
|
||||
rand.Reader = &dummyRandReader{}
|
||||
defer func() {
|
||||
rand.Reader = defaultRandReader
|
||||
}()
|
||||
opts = RequestOptions{
|
||||
Content: message,
|
||||
HashAlgorithm: crypto.SHA256,
|
||||
}
|
||||
expectedErrMsg = "malformed timestamping request: failed to generate nonce"
|
||||
_, err = NewRequest(opts)
|
||||
if err == nil || !errors.As(err, &malformedRequest) || err.Error() != expectedErrMsg {
|
||||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestMarshalBinary(t *testing.T) {
|
||||
|
@ -155,3 +171,9 @@ func TestValidateRequest(t *testing.T) {
|
|||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
type dummyRandReader struct{}
|
||||
|
||||
func (r *dummyRandReader) Read(b []byte) (int, error) {
|
||||
return 0, errors.New("failed to read")
|
||||
}
|
||||
|
|
2
token.go
2
token.go
|
@ -45,7 +45,7 @@ func ParseSignedToken(berData []byte) (*SignedToken, error) {
|
|||
return nil, err
|
||||
}
|
||||
if !oid.TSTInfo.Equal(signed.ContentType) {
|
||||
return nil, fmt.Errorf("unexpected content type: %v", signed.ContentType)
|
||||
return nil, fmt.Errorf("unexpected content type: %v. Expected to be id-ct-TSTInfo (%v)", signed.ContentType, oid.TSTInfo)
|
||||
}
|
||||
return (*SignedToken)(signed), nil
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func TestParseSignedToken(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedErrMsg := fmt.Sprintf("unexpected content type: %v", oid.Data)
|
||||
expectedErrMsg := fmt.Sprintf("unexpected content type: %v. Expected to be id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4)", oid.Data)
|
||||
_, err = ParseSignedToken(timestampToken)
|
||||
if err == nil || err.Error() != expectedErrMsg {
|
||||
t.Fatalf("expected error %s, but got %v", expectedErrMsg, err)
|
||||
|
|
Loading…
Reference in New Issue