Skip to content

Commit 013349b

Browse files
authored
Fix: Disallow timestamp requests where digest length is inconsistent with hash algorithm (#1066)
* Validate hash algorithm availability and digest length in timestamp requests Signed-off-by: Aaron Lew <[email protected]> * Add timestamp request verification to JSON request parser Signed-off-by: Aaron Lew <[email protected]> * Create VerifyTimestampRequest and verify hash algorithm supported Signed-off-by: Aaron Lew <[email protected]> * Fix typo in comment Signed-off-by: Aaron Lew <[email protected]> * Fix case of verifyTimestampRequest function name Signed-off-by: Aaron Lew <[email protected]> --------- Signed-off-by: Aaron Lew <[email protected]>
1 parent 725b9bd commit 013349b

File tree

4 files changed

+91
-20
lines changed

4 files changed

+91
-20
lines changed

pkg/api/error.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ import (
2828
)
2929

3030
const (
31-
failedToGenerateTimestampResponse = "Error generating timestamp response"
32-
WeakHashAlgorithmTimestampRequest = "Weak hash algorithm in timestamp request"
31+
failedToGenerateTimestampResponse = "Error generating timestamp response"
32+
WeakHashAlgorithmTimestampRequest = "Weak hash algorithm in timestamp request"
33+
InconsistentDigestLengthTimestampRequest = "Message digest has incorrect length for specified algorithm"
3334
)
3435

3536
func errorMsg(message string, code int) *models.Error {

pkg/api/timestamp.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func ParseJSONRequest(reqBytes []byte) (*timestamp.Request, string, error) {
9999
TSAPolicyOID: oidInts,
100100
}
101101

102-
return &tsReq, "", nil
102+
return verifyTimestampRequest(&tsReq)
103103
}
104104

105105
func parseDERRequest(reqBytes []byte) (*timestamp.Request, string, error) {
@@ -108,12 +108,7 @@ func parseDERRequest(reqBytes []byte) (*timestamp.Request, string, error) {
108108
return nil, failedToGenerateTimestampResponse, err
109109
}
110110

111-
// verify that the request's hash algorithm is supported
112-
if err := verification.VerifyRequest(parsed); err != nil {
113-
return nil, WeakHashAlgorithmTimestampRequest, err
114-
}
115-
116-
return parsed, "", nil
111+
return verifyTimestampRequest(parsed)
117112
}
118113

119114
func getContentType(r *http.Request) (string, error) {
@@ -188,3 +183,23 @@ func TimestampResponseHandler(params ts.GetTimestampResponseParams) middleware.R
188183
func GetTimestampCertChainHandler(_ ts.GetTimestampCertChainParams) middleware.Responder {
189184
return ts.NewGetTimestampCertChainOK().WithPayload(api.certChainPem)
190185
}
186+
187+
func verifyTimestampRequest(tsReq *timestamp.Request) (*timestamp.Request, string, error) {
188+
if err := verification.VerifyRequest(tsReq); err != nil {
189+
// verify that the request's hash algorithm is not weak
190+
if errors.Is(err, verification.ErrWeakHashAlg) {
191+
return nil, WeakHashAlgorithmTimestampRequest, err
192+
}
193+
// verify that the request's hash algorithm is supported
194+
if errors.Is(err, verification.ErrUnsupportedHashAlg) {
195+
return nil, failedToGenerateTimestampResponse, err
196+
}
197+
// verify that the request's digest length is consistent with the request's hash algorithm
198+
if errors.Is(err, verification.ErrInconsistentDigestLength) {
199+
return nil, InconsistentDigestLengthTimestampRequest, err
200+
}
201+
return nil, failedToGenerateTimestampResponse, err
202+
}
203+
204+
return tsReq, "", nil
205+
}

pkg/verification/verify_request.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,32 @@ package verification
1616

1717
import (
1818
"crypto"
19+
"fmt"
1920

2021
"github.com/digitorus/timestamp"
2122
"github.com/pkg/errors"
2223
)
2324

2425
var ErrWeakHashAlg = errors.New("weak hash algorithm: must be SHA-256, SHA-384, or SHA-512")
26+
var ErrUnsupportedHashAlg = errors.New("unsupported hash algorithm")
27+
var ErrInconsistentDigestLength = errors.New("digest length inconsistent with specified hash algorithm")
2528

2629
func VerifyRequest(ts *timestamp.Request) error {
2730
// only SHA-1, SHA-256, SHA-384, and SHA-512 are supported by the underlying library
28-
if ts.HashAlgorithm == crypto.SHA1 {
31+
switch ts.HashAlgorithm {
32+
case crypto.SHA1:
2933
return ErrWeakHashAlg
34+
case crypto.SHA256, crypto.SHA384, crypto.SHA512:
35+
default:
36+
return ErrUnsupportedHashAlg
3037
}
38+
39+
expectedDigestLength := ts.HashAlgorithm.Size()
40+
actualDigestLength := len(ts.HashedMessage)
41+
42+
if actualDigestLength != expectedDigestLength {
43+
return fmt.Errorf("%w: expected %d bytes, got %d bytes", ErrInconsistentDigestLength, expectedDigestLength, actualDigestLength)
44+
}
45+
3146
return nil
3247
}

pkg/verification/verify_request_test.go

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,63 @@ package verification
1616

1717
import (
1818
"crypto"
19+
"errors"
1920
"testing"
2021

2122
"github.com/digitorus/timestamp"
2223
)
2324

2425
func TestVerifyRequest(t *testing.T) {
25-
tsReq := &timestamp.Request{}
26-
27-
for _, alg := range []crypto.Hash{crypto.SHA256, crypto.SHA384, crypto.SHA512} {
28-
tsReq.HashAlgorithm = alg
29-
if err := VerifyRequest(tsReq); err != nil {
30-
t.Fatalf("unexpected error verifying request, got %v", err)
31-
}
26+
tests := []struct {
27+
name string
28+
tsReq *timestamp.Request
29+
expectedError error
30+
}{
31+
{
32+
name: "Valid SHA256",
33+
tsReq: &timestamp.Request{HashAlgorithm: crypto.SHA256, HashedMessage: make([]byte, crypto.SHA256.Size())},
34+
expectedError: nil,
35+
},
36+
{
37+
name: "Valid SHA384",
38+
tsReq: &timestamp.Request{HashAlgorithm: crypto.SHA384, HashedMessage: make([]byte, crypto.SHA384.Size())},
39+
expectedError: nil,
40+
},
41+
{
42+
name: "Valid SHA512",
43+
tsReq: &timestamp.Request{HashAlgorithm: crypto.SHA512, HashedMessage: make([]byte, crypto.SHA512.Size())},
44+
expectedError: nil,
45+
},
46+
{
47+
name: "Weak Hash SHA1",
48+
tsReq: &timestamp.Request{HashAlgorithm: crypto.SHA1, HashedMessage: make([]byte, crypto.SHA1.Size())},
49+
expectedError: ErrWeakHashAlg,
50+
},
51+
{
52+
name: "Unsupported Hash Algorithm",
53+
tsReq: &timestamp.Request{HashAlgorithm: crypto.SHA224, HashedMessage: make([]byte, crypto.SHA224.Size())},
54+
expectedError: ErrUnsupportedHashAlg,
55+
},
56+
{
57+
name: "Inconsistent Digest Length",
58+
tsReq: &timestamp.Request{HashAlgorithm: crypto.SHA256, HashedMessage: make([]byte, 31)}, // SHA256 size is 32
59+
expectedError: ErrInconsistentDigestLength,
60+
},
3261
}
3362

34-
tsReq.HashAlgorithm = crypto.SHA1
35-
if err := VerifyRequest(tsReq); err != ErrWeakHashAlg {
36-
t.Fatalf("expected error with weak hash algorithm, got %v", err)
63+
for _, tc := range tests {
64+
t.Run(tc.name, func(t *testing.T) {
65+
err := VerifyRequest(tc.tsReq)
66+
if tc.expectedError != nil {
67+
if err == nil {
68+
t.Fatalf("expected error %v, got nil", tc.expectedError)
69+
}
70+
if !errors.Is(err, tc.expectedError) {
71+
t.Fatalf("expected error to be or wrap %v, but got %v (error message: %q)", tc.expectedError, err, err.Error())
72+
}
73+
} else if err != nil {
74+
t.Fatalf("expected no error, but got %v", err)
75+
}
76+
})
3777
}
3878
}

0 commit comments

Comments
 (0)