fleet/third_party/httpsig-go/digest_test.go
Victor Lyuboslavsky c25fed2492
Added a vendored version of httpsig-go. (#30820)
For #30473

This change adds a vendored `httpsig-go` library to our repo. We cannot
use the upstream library because it has not merged the change we need:
https://github.com/remitly-oss/httpsig-go/pull/25

Thus, we need our own copy at this point.

The instructions for keeping this library up to date (if needed) are in
`UPDATE_INSTRUCTIONS`.

None of the coderabbitai review comments are relevant to the
code/features we are going to use for HTTP message signatures.

We will use this library in subsequent PRs for the TPM-backed HTTP
message signature feature.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Introduced a Go library for HTTP message signing and verification,
supporting multiple cryptographic algorithms (RSA, ECDSA, Ed25519,
HMAC).
* Added utilities for key management, including JWK and PEM key
handling.
* Provided HTTP client and server helpers for automatic request signing
and signature verification.
* Implemented structured error handling and metadata extraction for
signatures.

* **Documentation**
  * Added comprehensive README, usage examples, and update instructions.
* Included license and configuration files for third-party and testing
tools.

* **Tests**
* Added extensive unit, integration, and fuzz tests covering signing,
verification, and key handling.
* Included official RFC test vectors and various test data files for
robust validation.

* **Chores**
* Integrated continuous integration workflows and ignore files for code
quality and security analysis.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-14 20:26:50 +02:00

127 lines
3.6 KiB
Go

package httpsig
import (
"encoding/base64"
"errors"
"io"
"testing"
"github.com/remitly-oss/httpsig-go/sigtest"
)
func TestDigestCreate(t *testing.T) {
testcases := []struct {
Name string
Algo Digest
Body io.ReadCloser
ExpectedDigest string // base64 encoded digest
ExpectedHeader string
ExpectedErrCode ErrCode
}{
{
Name: "sha-256",
Algo: DigestSHA256,
Body: sigtest.MakeBody("hello world"),
ExpectedDigest: "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=",
ExpectedHeader: "sha-256=:uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=:",
},
{
Name: "sha-512",
Algo: DigestSHA512,
Body: sigtest.MakeBody("hello world"),
ExpectedDigest: "MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==",
ExpectedHeader: "sha-512=:MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==:",
},
{
Name: "UnsupportedAlgorithm",
Algo: Digest("nope"),
Body: sigtest.MakeBody("hello world"),
ExpectedErrCode: ErrNoSigUnsupportedDigest,
},
}
for _, tc := range testcases {
t.Run(tc.Name, func(t *testing.T) {
actual, err := digestBody(tc.Algo, tc.Body)
if err != nil {
if tc.ExpectedErrCode != "" {
diffErrorCode(t, err, tc.ExpectedErrCode)
return
}
t.Fatal(err)
}
actualEncoded := base64.StdEncoding.EncodeToString(actual.Digest)
sigtest.Diff(t, tc.ExpectedDigest, actualEncoded, "Wrong digest")
actualHeader, err := createDigestHeader(tc.Algo, actual.Digest)
if err != nil {
t.Fatal(err)
}
sigtest.Diff(t, tc.ExpectedHeader, actualHeader, "Wrong digest header")
})
}
}
func TestDigestParse(t *testing.T) {
testcases := []struct {
Name string
Header []string
ExcepctedAlgo Digest
ExpectedDigest string // base64 encoded digest
ExpectedErrCode ErrCode
}{
{
Name: "sha-256",
Header: []string{"sha-256=:uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=:"},
ExcepctedAlgo: DigestSHA256,
ExpectedDigest: "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=",
},
{
Name: "sha-512",
Header: []string{"sha-512=:MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==:"},
ExcepctedAlgo: DigestSHA512,
ExpectedDigest: "MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==",
},
{
Name: "Empty",
Header: []string{},
ExpectedDigest: "",
},
{
Name: "BadHeader",
Header: []string{"bl===ah"},
ExpectedErrCode: ErrNoSigInvalidHeader,
},
{
Name: "Unsupported",
Header: []string{"md5=:blah:"},
ExpectedDigest: "",
},
}
for _, tc := range testcases {
t.Run(tc.Name, func(t *testing.T) {
actualAlgo, actualDigest, err := getSupportedDigestFromHeader(tc.Header)
if err != nil {
if tc.ExpectedErrCode != "" {
diffErrorCode(t, err, tc.ExpectedErrCode)
return
}
t.Fatal(err)
} else if tc.ExpectedErrCode != "" {
t.Fatal("Expected an err")
}
digestEncoded := base64.StdEncoding.EncodeToString(actualDigest)
sigtest.Diff(t, tc.ExcepctedAlgo, actualAlgo, "Wrong digest algo")
sigtest.Diff(t, tc.ExpectedDigest, digestEncoded, "Wrong digest")
})
}
}
func diffErrorCode(t *testing.T, err error, code ErrCode) bool {
var sigerr *SignatureError
if errors.As(err, &sigerr) {
return sigtest.Diff(t, code, sigerr.Code, "Wrong error code")
}
return false
}