diff --git a/go.mod b/go.mod index 507d1f83ec..86036ad76a 100644 --- a/go.mod +++ b/go.mod @@ -110,7 +110,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/client_golang v1.21.1 github.com/quasilyte/go-ruleguard/dsl v0.3.22 - github.com/remitly-oss/httpsig-go v1.1.3 + github.com/remitly-oss/httpsig-go v1.2.0 github.com/rs/zerolog v1.32.0 github.com/russellhaering/goxmldsig v1.4.0 github.com/saferwall/pe v1.5.5 @@ -349,5 +349,3 @@ tool ( github.com/kevinburke/go-bindata github.com/quasilyte/go-ruleguard/dsl ) - -replace github.com/remitly-oss/httpsig-go => ./third_party/httpsig-go diff --git a/go.sum b/go.sum index 9c673d7b3a..ef4b80378a 100644 --- a/go.sum +++ b/go.sum @@ -838,6 +838,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remitly-oss/httpsig-go v1.2.0 h1:rI634TJkh+US3qkWQfkJ7VDJgCvlIbyEepsEw+37W50= +github.com/remitly-oss/httpsig-go v1.2.0/go.mod h1:HYfozYlK9Zv9GYyw+eIuXugk1OV2kjowVrvdv0KQ4XU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/orbit/changes/32393-httpsig-go b/orbit/changes/32393-httpsig-go new file mode 100644 index 0000000000..428bce227c --- /dev/null +++ b/orbit/changes/32393-httpsig-go @@ -0,0 +1 @@ +* Updated httpsig-go library to 1.2.0 (for host identity certificates and HTTP message signatures). diff --git a/pkg/fleethttpsig/fleethttpsig.go b/pkg/fleethttpsig/fleethttpsig.go index c391dfbaec..a11496ba99 100644 --- a/pkg/fleethttpsig/fleethttpsig.go +++ b/pkg/fleethttpsig/fleethttpsig.go @@ -3,6 +3,7 @@ package fleethttpsig import ( "crypto" + "crypto/ecdsa" "github.com/remitly-oss/httpsig-go" ) @@ -28,13 +29,22 @@ func Verifier(kf httpsig.KeyFetcher) (*httpsig.Verifier, error) { } // Signer returns a *httpsig.Signer to sign HTTP requests to a Fleet server. +// It handles both regular ECDSA keys and TPM-backed signers. func Signer(metaKeyID string, signer crypto.Signer, signingAlgorithm httpsig.Algorithm) (*httpsig.Signer, error) { + signingKey := httpsig.SigningKey{ + MetaKeyID: metaKeyID, + } + if _, ok := signer.(*ecdsa.PrivateKey); ok { + signingKey.Key = signer + } else { + // TPM or other hardware-backed signer + signingKey.Opts = httpsig.SigningKeyOpts{ + Signer: signer, + } + } return httpsig.NewSigner(httpsig.SigningProfile{ Algorithm: signingAlgorithm, Fields: requiredFields, Metadata: requiredMetadata, - }, httpsig.SigningKey{ - Key: signer, - MetaKeyID: metaKeyID, - }) + }, signingKey) } diff --git a/third_party/README.md b/third_party/README.md new file mode 100644 index 0000000000..4a8b3a9547 --- /dev/null +++ b/third_party/README.md @@ -0,0 +1,2 @@ +This directory is for vendored third party libraries. +See: https://github.com/fleetdm/fleet/blob/main/docs/Contributing/adr/0004-third-party-vendoring.md diff --git a/third_party/httpsig-go/.github/workflows/codeql.yml b/third_party/httpsig-go/.github/workflows/codeql.yml deleted file mode 100644 index 41b7e1fb93..0000000000 --- a/third_party/httpsig-go/.github/workflows/codeql.yml +++ /dev/null @@ -1,84 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - schedule: - - cron: '42 20 * * 0' - -jobs: - analyze: - name: Analyze - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners - # Consider using larger runners for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} - permissions: - # required for all workflows - security-events: write - - # only required for workflows in private repositories - actions: read - contents: read - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] - # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both - # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/third_party/httpsig-go/.github/workflows/wf.yml b/third_party/httpsig-go/.github/workflows/wf.yml deleted file mode 100644 index 189ed15758..0000000000 --- a/third_party/httpsig-go/.github/workflows/wf.yml +++ /dev/null @@ -1,14 +0,0 @@ -on: [push, pull_request] -name: Test -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.21' - - name: Checkout code - uses: actions/checkout@v3 - - name: Test - run: go test -v ./... diff --git a/third_party/httpsig-go/.semgrepignore b/third_party/httpsig-go/.semgrepignore deleted file mode 100644 index e3842490eb..0000000000 --- a/third_party/httpsig-go/.semgrepignore +++ /dev/null @@ -1 +0,0 @@ -testdata/*.key \ No newline at end of file diff --git a/third_party/httpsig-go/.sideignore b/third_party/httpsig-go/.sideignore deleted file mode 100644 index e5ed11ee18..0000000000 --- a/third_party/httpsig-go/.sideignore +++ /dev/null @@ -1,49 +0,0 @@ -# To avoid giving your LLM irrelevant context, this .sideignore file can be used -# to exclude any code that isn't part of what is normally edited. The format is -# the same as .gitignore. Note that any patterns listed here are ignored *in -# addition* to to what is ignored via .sideignore. -# -# The following is a list of common vendored dependencies or known paths that -# should not be edited, in case they are not already in a .gitignore file. -# Adjust by: -# -# 1. Removing paths for languages/frameworks not relevant to you -# 2. Add any paths specific to your project that get auto-generated but not -# normally imported, such as mocks etc. - -# General vendored dependencies -vendor/ -third_party/ -extern/ -deps/ - -# Node -node_modules/ - -# Python virtual environments (less common names) -.venv/ -env/ - -# Go vendored dependencies -vendor/ - -# Ruby -.bundle/ -vendor/bundle/ - -# PHP -vendor/ - -# Java / Kotlin -.gradle/ - -# C / C++ -deps/ - -# Swift / Dart / Flutter -.dart_tool/ -Carthage/ - -# Elixir / Erlang -_build/ -deps/ \ No newline at end of file diff --git a/third_party/httpsig-go/LICENSE b/third_party/httpsig-go/LICENSE deleted file mode 100644 index 95501867bd..0000000000 --- a/third_party/httpsig-go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 remitly-oss - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/third_party/httpsig-go/README.md b/third_party/httpsig-go/README.md deleted file mode 100644 index 2604f883e7..0000000000 --- a/third_party/httpsig-go/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# HTTP Message Signatures - -[![Go Reference](https://pkg.go.dev/badge/github.com/remitly-oss/httpsig-go.svg)](https://pkg.go.dev/github.com/remitly-oss/httpsig-go) -[![Go Report Card](https://goreportcard.com/badge/github.com/remitly-oss/httpsig-go)](https://goreportcard.com/report/github.com/remitly-oss/httpsig-go) - -An implementation of HTTP Message Signatures from [RFC 9421](https://datatracker.ietf.org/doc/rfc9421/). - -HTTP signatures are a mechanism for signing and verifying HTTP requests and responses. - -HTTP signatures can be (or will be able to) used for demonstrating proof-of-posession ([DPoP](https://www.rfc-editor.org/rfc/rfc9449.html)) for [OAuth](https://oauth.net/2/dpop/) bearer tokens. - -## Supported Features -The full specification is supported with the exception of the following. File a ticket or PR and support will be added -Planned but not currently supported features: -- JWS algorithms -- Header parameters including trailers - -## net/http integration -Create net/http clients that sign requests and/or verifies repsonses. -```go - params := httpsig.SigningOptions{ - PrivateKey: nil, // Fill in your private key - Algorithm: httpsig.Algo_ECDSA_P256_SHA256, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaKeyID}, - MetaKeyID: "key123", - } - - // Create the signature signer - signer, _ := httpsig.NewSigner(params) - - // Create a net/http Client that signs all requests - signingClient := httpsig.NewHTTPClient(nil, signer, nil) -``` - -Create net/http Handlers that verify incoming requests to the server. -```go - myhandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Lookup the results of verification - if veriftyResult, ok := httpsig.GetVerifyResult(r.Context()); ok { - keyid, _ := veriftyResult.KeyID() - fmt.Fprintf(w, "Hello, %s", keyid) - } else { - fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) - } - }) - - // Create a verifier - verifier, _ := httpsig.NewVerifier(nil, httpsig.DefaultVerifyProfile) - - mux := http.NewServeMux() - // Wrap the handler with the a signature verification handler. - mux.Handle("/", httpsig.NewHandler(myhandler, verifier)) -``` - -## Stability -The v1.1+ release is stable and production ready. - -Please file issues and bugs in the github projects issue tracker. - -## References - -- [RFC 9421](https://datatracker.ietf.org/doc/rfc9421/) -- [OAuth support](https://oauth.net/http-signatures/) -- [Interactive UI](https://httpsig.org/) diff --git a/third_party/httpsig-go/UPDATE_INSTRUCTIONS b/third_party/httpsig-go/UPDATE_INSTRUCTIONS deleted file mode 100644 index ec1acca1c0..0000000000 --- a/third_party/httpsig-go/UPDATE_INSTRUCTIONS +++ /dev/null @@ -1,42 +0,0 @@ -These are instructions for pulling in the latest changes from the upstream version of this library. -The `UPSTREAM_COMMIT` file tracks the upstream version that we last synced with. - -_Notes:_ -- Update `/path/to/your/monorepo` below to your fleet repo location -- These instructions have not been fully tested. - -```bash -export FLEET_REPO=/path/to/your/monorepo -# Clone upstream -git clone https://github.com/remitly-oss/httpsig-go.git ~/httpsig-go-merge -cd ~/httpsig-go-merge - -# Check out the last upstream commit we vendored -git checkout $(cat "$FLEET_REPO"/third_party/httpsig-go/UPSTREAM_COMMIT) - -# Create a branch for our downstream changes -git checkout -b internal-changes - -# Copy current vendored version into this working repo -rsync -a --delete "$FLEET_REPO"/third_party/httpsig-go/ ./ --exclude .git -git add . -git commit -m "Apply downstream changes" - -# Fetch upstream updates and merge them -git fetch origin -git checkout main -git merge origin/main -git checkout internal-changes -git merge main # resolve conflicts - -# Copy merged result back into monorepo -rsync -a --delete ./ "$FLEET_REPO"/third_party/httpsig-go/ --exclude .git - -# Record the new upstream commit. Manually double check that it matches the upstream commit. -git rev-parse origin/main > "$FLEET_REPO"/third_party/httpsig-go/UPSTREAM_COMMIT - -# Commit to monorepo -cd "$FLEET_REPO" -git add third_party/httpsig-go -git commit -m "Update httpsig-go with latest upstream changes" -``` diff --git a/third_party/httpsig-go/UPSTREAM_COMMIT b/third_party/httpsig-go/UPSTREAM_COMMIT deleted file mode 100644 index f02329f8b1..0000000000 --- a/third_party/httpsig-go/UPSTREAM_COMMIT +++ /dev/null @@ -1 +0,0 @@ -d68e2e99a37987076d8588fafe2aa34147abcc24 diff --git a/third_party/httpsig-go/accept.go b/third_party/httpsig-go/accept.go deleted file mode 100644 index eb9f6180ac..0000000000 --- a/third_party/httpsig-go/accept.go +++ /dev/null @@ -1,68 +0,0 @@ -package httpsig - -import ( - "fmt" - - sfv "github.com/dunglas/httpsfv" -) - -type AcceptSignature struct { - Profile SigningProfile - MetaNonce string // 'nonce' - MetaKeyID string // 'keyid' - MetaTag string // 'tag' - No default. A value must be provided if the parameter is in Metadata. -} - -func ParseAcceptSignature(acceptHeader string) (AcceptSignature, error) { - as := AcceptSignature{} - acceptDict, err := sfv.UnmarshalDictionary([]string{acceptHeader}) - if err != nil { - return as, newError(ErrInvalidAcceptSignature, "Unable to parse Accept-Signature value", err) - } - profiles := acceptDict.Names() - if len(profiles) == 0 { - return as, newError(ErrMissingAcceptSignature, "No Accept-Signature value") - } - - label := profiles[0] - profileItems, _ := acceptDict.Get(label) - profileList, isList := profileItems.(sfv.InnerList) - if !isList { - return as, newError(ErrInvalidAcceptSignature, "Unable to parse Accept-Signature value. Accept-Signature must be a dictionary.") - } - - fields := []string{} - for _, componentItem := range profileList.Items { - field, ok := componentItem.Value.(string) - if !ok { - return as, newError(ErrInvalidAcceptSignature, fmt.Sprintf("Invalid signature component '%v', Components must be strings", componentItem.Value)) - - } - fields = append(fields, field) - } - as.Profile = SigningProfile{ - Fields: Fields(fields...), - Label: label, - Metadata: []Metadata{}, - } - - md := metadataProviderFromParams{profileList.Params} - for _, meta := range profileList.Params.Names() { - as.Profile.Metadata = append(as.Profile.Metadata, Metadata(meta)) - switch Metadata(meta) { - case MetaNonce: - as.MetaNonce, _ = md.Nonce() - case MetaAlgorithm: - alg, _ := md.Alg() - as.Profile.Algorithm = Algorithm(alg) - case MetaKeyID: - as.MetaKeyID, _ = md.KeyID() - case MetaTag: - as.MetaTag, _ = md.Tag() - - } - } - - return as, nil - -} diff --git a/third_party/httpsig-go/accept_test.go b/third_party/httpsig-go/accept_test.go deleted file mode 100644 index 30eb974bd0..0000000000 --- a/third_party/httpsig-go/accept_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package httpsig - -import ( - "testing" - - "github.com/remitly-oss/httpsig-go/sigtest" -) - -func TestAcceptParseSignature(t *testing.T) { - testcases := []struct { - Name string - Desc string - AcceptHeader string - Expected AcceptSignature - ExpectedErrCode ErrCode - }{ - { - Name: "FromSpecification", - Desc: "Accept header used in the RFC", - AcceptHeader: `sig1=("@method" "@target-uri" "@authority" "content-digest" "cache-control");keyid="test-key-rsa-pss";created;tag="app-123"`, - Expected: AcceptSignature{ - MetaKeyID: "test-key-rsa-pss", - MetaTag: "app-123", - Profile: SigningProfile{ - Fields: Fields("@method", "@target-uri", "@authority", "content-digest", "cache-control"), - Metadata: []Metadata{"keyid", "created", "tag"}, - Label: "sig1", - }, - }, - }, - { - Name: "InvalidAcceptSig", - AcceptHeader: `("@method" "@target-uri" "@authority" "content-digest" "cache-control");keyid="test-key-rsa-pss";created;tag="app-123"`, - - ExpectedErrCode: ErrInvalidAcceptSignature, - }, - { - Name: "NoAcceptSig", - AcceptHeader: "", - ExpectedErrCode: ErrMissingAcceptSignature, - }, - { - Name: "NotAList", - AcceptHeader: `sig1="@method"`, - ExpectedErrCode: ErrInvalidAcceptSignature, - }, - { - Name: "BadComponent", - AcceptHeader: `sig1=("@method" 1 "@authority" "content-digest" "cache-control");keyid="test-key-rsa-pss";created;tag="app-123"`, - ExpectedErrCode: ErrInvalidAcceptSignature, - }, - } - - for _, tc := range testcases { - t.Run(tc.Name, func(t *testing.T) { - actual, err := ParseAcceptSignature(tc.AcceptHeader) - if sigtest.Diff(t, tc.ExpectedErrCode, errCode(err), "Wrong error code") { - t.Logf("%+v\n", err) - return - } - sigtest.Diff(t, tc.Expected, actual, "Wrong signature options") - }) - } -} diff --git a/third_party/httpsig-go/base.go b/third_party/httpsig-go/base.go deleted file mode 100644 index f9e4780e83..0000000000 --- a/third_party/httpsig-go/base.go +++ /dev/null @@ -1,248 +0,0 @@ -package httpsig - -import ( - "context" - "fmt" - "io" - "net/http" - "slices" - "strconv" - "strings" - - sfv "github.com/dunglas/httpsfv" -) - -// sigBaseInput is the required input to calculate the signature base -type sigBaseInput struct { - Components []componentID - MetadataParams []Metadata // metadata parameters to add to the signature and their values - MetadataValues MetadataProvider -} - -type httpMessage struct { - IsResponse bool - Req *http.Request - Resp *http.Response -} - -func (hrr httpMessage) Headers() http.Header { - if hrr.IsResponse { - return hrr.Resp.Header - } - return hrr.Req.Header -} - -func (hrr httpMessage) Body() io.ReadCloser { - if hrr.IsResponse { - return hrr.Resp.Body - } - return hrr.Req.Body -} - -func (hrr httpMessage) SetBody(body io.ReadCloser) { - if hrr.IsResponse { - hrr.Resp.Body = body - return - } - hrr.Req.Body = body -} - -func (hrr httpMessage) Context() context.Context { - if hrr.IsResponse { - return context.Background() - } - return hrr.Req.Context() -} - -func (hrr httpMessage) isDebug() bool { - if dbgval, ok := hrr.Context().Value(ctxKeyAddDebug).(bool); ok { - return dbgval - } - return false -} - -/* -calculateSignatureBase calculates the 'signature base' - the data used as the input to signing or verifying -The signature base is an ASCII string containing the canonicalized HTTP message components covered by the signature. -*/ -func calculateSignatureBase(msg httpMessage, bp sigBaseInput) (signatureBase, error) { - signatureParams := sfv.InnerList{ - Items: []sfv.Item{}, - Params: sfv.NewParams(), - } - componentNames := []string{} - var base strings.Builder - - // Add all the required components - for _, component := range bp.Components { - name, err := component.signatureName() - if err != nil { - return signatureBase{}, err - } - if slices.Contains(componentNames, name) { - return signatureBase{}, newError(ErrInvalidSignatureOptions, fmt.Sprintf("Repeated component name not allowed: '%s'", name)) - } - componentNames = append(componentNames, name) - signatureParams.Items = append(signatureParams.Items, component.Item) - - value, err := component.signatureValue(msg) - if err != nil { - return signatureBase{}, err - } - - base.WriteString(fmt.Sprintf("%s: %s\n", name, value)) - } - - // Add signature metadata parameters - for _, meta := range bp.MetadataParams { - switch meta { - case MetaCreated: - created, err := bp.MetadataValues.Created() - if err != nil { - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Failed to get value for %s metadata parameter", meta), err) - } - signatureParams.Params.Add(string(MetaCreated), created) - case MetaExpires: - expires, err := bp.MetadataValues.Expires() - if err != nil { - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Failed to get value for %s metadata parameter", meta), err) - } - signatureParams.Params.Add(string(MetaExpires), expires) - case MetaNonce: - nonce, err := bp.MetadataValues.Nonce() - if err != nil { - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Failed to get value for %s metadata parameter", meta), err) - } - signatureParams.Params.Add(string(MetaNonce), nonce) - case MetaAlgorithm: - alg, err := bp.MetadataValues.Alg() - if err != nil { - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Failed to get value for %s metadata parameter", meta), err) - } - signatureParams.Params.Add(string(MetaAlgorithm), alg) - case MetaKeyID: - keyID, err := bp.MetadataValues.KeyID() - if err != nil { - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Failed to get value for %s metadata parameter", meta), err) - } - signatureParams.Params.Add(string(MetaKeyID), keyID) - case MetaTag: - tag, err := bp.MetadataValues.Tag() - if err != nil { - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Failed to get value for %s metadata parameter", meta), err) - } - signatureParams.Params.Add(string(MetaTag), tag) - default: - return signatureBase{}, newError(ErrInvalidMetadata, fmt.Sprintf("Invalid metadata field '%s'", meta)) - } - } - - paramsOut, err := sfv.Marshal(signatureParams) - if err != nil { - return signatureBase{}, fmt.Errorf("Failed to marshal params: %w", err) - } - - base.WriteString(fmt.Sprintf("\"%s\": %s", sigparams, paramsOut)) - return signatureBase{ - base: []byte(base.String()), - signatureInput: paramsOut, - }, nil -} - -// componentID is the signature 'component identifier' as detailed in the specification. -type componentID struct { - Name string // canonical, lower case component name. The name is also the value of the Item. - Item sfv.Item // The sfv representation of the component identifier. This contains the name and parameters. -} - -// SignatureName is the components serialized name required by the signature. -func (cID componentID) signatureName() (string, error) { - signame, err := sfv.Marshal(cID.Item) - if err != nil { - return "", newError(ErrInvalidComponent, fmt.Sprintf("Unable to serialize component identifier '%s'", cID.Name), err) - } - return signame, nil -} - -// signatureValue is the components value required by the signature. -func (cID componentID) signatureValue(msg httpMessage) (string, error) { - val := "" - var err error - if strings.HasPrefix(cID.Name, "@") { - val, err = deriveComponentValue(msg, cID) - if err != nil { - return "", err - } - } else { - values := msg.Headers().Values(cID.Name) - if len(values) == 0 { - return "", newError(ErrInvalidComponent, fmt.Sprintf("Message is missing required component '%s'", cID.Name)) - } - // TODO Handle multi value - if len(values) > 1 { - return "", newError(ErrUnsupported, fmt.Sprintf("This library does yet support signatures for components/headers with multiple values: %s", cID.Name)) - } - val = msg.Headers().Get(cID.Name) - } - return val, nil -} -func deriveComponentValue(r httpMessage, component componentID) (string, error) { - if r.IsResponse { - return deriveComponentValueResponse(r.Resp, component) - } - return deriveComponentValueRequest(r.Req, component) -} - -func deriveComponentValueResponse(resp *http.Response, component componentID) (string, error) { - switch component.Name { - case "@status": - return strconv.Itoa(resp.StatusCode), nil - } - return "", nil -} - -func deriveComponentValueRequest(req *http.Request, component componentID) (string, error) { - switch component.Name { - case "@method": - return req.Method, nil - case "@target-uri": - return deriveTargetURI(req), nil - case "@authority": - return req.Host, nil - case "@scheme": - case "@request-target": - case "@path": - return req.URL.Path, nil - case "@query": - return fmt.Sprintf("?%s", req.URL.RawQuery), nil - case "@query-param": - paramKey, found := component.Item.Params.Get("name") - if !found { - return "", newError(ErrInvalidSignatureOptions, fmt.Sprintf("@query-param specified but missing 'name' parameter to indicate which parameter.")) - } - paramName, ok := paramKey.(string) - if !ok { - return "", newError(ErrInvalidSignatureOptions, fmt.Sprintf("@query-param specified but the 'name' parameter must be a string to indicate which parameter.")) - } - paramValue := req.URL.Query().Get(paramName) - // TODO support empty - is this still a string with a space in it? - if paramValue == "" { - return "", newError(ErrInvalidSignatureOptions, fmt.Sprintf("@query-param '%s' specified but that query param is not in the request", paramName)) - } - return paramValue, nil - default: - return "", newError(ErrInvalidSignatureOptions, fmt.Sprintf("Unsupported derived component identifier for a request '%s'", component.Name)) - } - return "", nil -} - -// deriveTargetURI resolves to an absolute form as required by RFC 9110 and referenced by the http signatures spec. -// The target URI excludes the reference's fragment component, if any, since fragment identifiers are reserved for client-side processing -func deriveTargetURI(req *http.Request) string { - scheme := "https" - if req.TLS == nil { - scheme = "http" - } - - return fmt.Sprintf("%s://%s%s%s", scheme, req.Host, req.URL.RawPath, req.URL.RawQuery) -} diff --git a/third_party/httpsig-go/digest.go b/third_party/httpsig-go/digest.go deleted file mode 100644 index 63960ba18b..0000000000 --- a/third_party/httpsig-go/digest.go +++ /dev/null @@ -1,123 +0,0 @@ -package httpsig - -import ( - "bytes" - "crypto/sha256" - "crypto/sha512" - "fmt" - "io" - "net/http" - - sfv "github.com/dunglas/httpsfv" -) - -const ( - digestAlgoSHA256 = "sha-256" - digestAlgoSHA512 = "sha-512" -) - -var ( - emptySHA256 = sha256.Sum256([]byte{}) - emptySHA512 = sha512.Sum512([]byte{}) -) - -// digestBody reads the entire body to calculate the digest and returns a new io.ReaderCloser which can be set as the new request body. -type digestInfo struct { - Digest []byte - NewBody io.ReadCloser // NewBody is intended as the http.Request Body replacement. Calculating the digest requires reading the body. -} - -func digestBody(digAlgo Digest, body io.ReadCloser) (digestInfo, error) { - var digest []byte - // client GET requests have a nil body - // received/server GET requests have a body but its NoBody - if body == nil || body == http.NoBody { - switch digAlgo { - case DigestSHA256: - digest = emptySHA256[:] - case DigestSHA512: - digest = emptySHA512[:] - default: - return digestInfo{}, newError(ErrNoSigUnsupportedDigest, fmt.Sprintf("Unsupported digest algorithm '%s'", digAlgo)) - } - return digestInfo{ - Digest: digest, - NewBody: body, - }, nil - } - var buf bytes.Buffer - if _, err := buf.ReadFrom(body); err != nil { - return digestInfo{}, newError(ErrNoSigMessageBody, "Failed to read message body to calculate digest", err) - } - if err := body.Close(); err != nil { - return digestInfo{}, newError(ErrNoSigMessageBody, "Failed to close message body to calculate digest", err) - } - - switch digAlgo { - case DigestSHA256: - d := sha256.Sum256(buf.Bytes()) - digest = d[:] - case DigestSHA512: - d := sha512.Sum512(buf.Bytes()) - digest = d[:] - default: - return digestInfo{}, newError(ErrNoSigUnsupportedDigest, fmt.Sprintf("Unsupported digest algorithm '%s'", digAlgo)) - } - - return digestInfo{ - Digest: digest, - NewBody: io.NopCloser(bytes.NewReader(buf.Bytes())), - }, nil -} - -func createDigestHeader(algo Digest, digest []byte) (string, error) { - sfValue := sfv.NewItem(digest) - header := sfv.NewDictionary() - switch algo { - case DigestSHA256: - header.Add(digestAlgoSHA256, sfValue) - case DigestSHA512: - header.Add(digestAlgoSHA512, sfValue) - default: - return "", newError(ErrNoSigUnsupportedDigest, fmt.Sprintf("Unsupported digest algorithm '%s'", algo)) - } - value, err := sfv.Marshal(header) - if err != nil { - return "", newError(ErrInternal, "Failed to marshal digest", err) - } - return value, nil - -} - -// getSupportedDigestFromHeader returns the first supported digest from the supplied header. If no supported header is found a nil digest is returned. -func getSupportedDigestFromHeader(contentDigestHeader []string) (algo Digest, digest []byte, err error) { - digestDict, err := sfv.UnmarshalDictionary(contentDigestHeader) - if err != nil { - return "", nil, newError(ErrNoSigInvalidHeader, "Could not parse Content-Digest header", err) - } - - for _, algo := range digestDict.Names() { - switch Digest(algo) { - case DigestSHA256: - fallthrough - case DigestSHA512: - member, ok := digestDict.Get(algo) - if !ok { - continue - } - item, ok := member.(sfv.Item) - if !ok { - // If not a an Item it's not a valid header value. Skip - continue - } - if digest, ok := item.Value.([]byte); ok { - return Digest(algo), digest, nil - } - default: - // Unsupported - continue - } - } - - return "", nil, nil -} diff --git a/third_party/httpsig-go/digest_test.go b/third_party/httpsig-go/digest_test.go deleted file mode 100644 index f549aa59c7..0000000000 --- a/third_party/httpsig-go/digest_test.go +++ /dev/null @@ -1,127 +0,0 @@ -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 -} diff --git a/third_party/httpsig-go/examples_test.go b/third_party/httpsig-go/examples_test.go deleted file mode 100644 index 37b8e9e191..0000000000 --- a/third_party/httpsig-go/examples_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package httpsig_test - -import ( - "crypto" - "fmt" - "html" - "net/http" - "net/http/httptest" - - "github.com/remitly-oss/httpsig-go" - "github.com/remitly-oss/httpsig-go/keyman" - "github.com/remitly-oss/httpsig-go/keyutil" -) - -func ExampleSign() { - pkeyEncoded := `-----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgNTK6255ubaaj1i/c -ppuLouTgjAVyHGSxI0pYX8z1e2GhRANCAASkbVuWv1KXXs2H8b0ruFLyv2lKJWtT -BznPJ5sSI1Jn+srosJB/GbEZ3Kg6PcEi+jODF9fdpNEaHGbbGdaVhJi1 ------END PRIVATE KEY-----` - - pkey, _ := keyutil.ReadPrivateKey([]byte(pkeyEncoded)) - req := httptest.NewRequest("GET", "https://example.com/data", nil) - - profile := httpsig.SigningProfile{ - Algorithm: httpsig.Algo_ECDSA_P256_SHA256, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaKeyID}, - } - skey := httpsig.SigningKey{ - Key: pkey, - MetaKeyID: "key123", - } - - signer, _ := httpsig.NewSigner(profile, skey) - signer.Sign(req) -} - -func ExampleVerify() { - pubkeyEncoded := `-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIUctKvU5L/eEYxua5Zlz0HIQJRQq -MTQ7eYQXwqpTvTJkuTffGXKLilT75wY2YZWfybv9flu5d6bCfw+4UB9+cg== ------END PUBLIC KEY-----` - - pubkey, _ := keyutil.ReadPublicKey([]byte(pubkeyEncoded)) - req := httptest.NewRequest("GET", "https://example.com/data", nil) - - kf := keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "key123": { - KeyID: "key123", - Algo: httpsig.Algo_ECDSA_P256_SHA256, - PubKey: pubkey, - }, - }) - - httpsig.Verify(req, kf, httpsig.DefaultVerifyProfile) -} - -func ExampleNewHandler() { - myhandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Lookup the results of verification - if veriftyResult, ok := httpsig.GetVerifyResult(r.Context()); ok { - keyid, _ := veriftyResult.KeyID() - fmt.Fprintf(w, "Hello, %s", html.EscapeString(keyid)) - } else { - fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) - } - }) - - // Create a verifier - verifier, _ := httpsig.NewVerifier(nil, httpsig.DefaultVerifyProfile) - - mux := http.NewServeMux() - // Wrap the handler with the a signature verification handler. - mux.Handle("/", httpsig.NewHandler(myhandler, verifier)) -} - -func ExampleClient() { - profile := httpsig.SigningProfile{ - Algorithm: httpsig.Algo_ECDSA_P256_SHA256, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaKeyID}, - } - var privateKey crypto.PrivateKey // Get your private key - - sk := httpsig.SigningKey{ - Key: privateKey, - MetaKeyID: "key123", - } - // Create the signature signer - signer, _ := httpsig.NewSigner(profile, sk) - - // Create a net/http Client that signs all requests - signingClient := httpsig.NewHTTPClient(nil, signer, nil) - - // This call will be signed. - signingClient.Get("https://example.com") - - verifier, _ := httpsig.NewVerifier(nil, httpsig.DefaultVerifyProfile) - // Create a net/http Client that signs and verifies all requests - signVerifyClient := httpsig.NewHTTPClient(nil, signer, verifier) - - signVerifyClient.Get("https://example.com") -} diff --git a/third_party/httpsig-go/fz_test.go b/third_party/httpsig-go/fz_test.go deleted file mode 100644 index ed69fcb32f..0000000000 --- a/third_party/httpsig-go/fz_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package httpsig - -import ( - "bufio" - "bytes" - "net/http" - "os" - "testing" - - "github.com/remitly-oss/httpsig-go/sigtest" -) - -// FuzzSigningOptions fuzzes the basic user input to SigningOptions -func FuzzSigningOptions1(f *testing.F) { - testcases := [][]string{ - {"", "", "", ""}, - {"", "0", "0", "\xde"}, - {"", "\n", "0", "0"}, - {"", "", "0", "@"}, - {"", "@query-param", "0", "0"}, - {string(Algo_ECDSA_P256_SHA256), "@query", "0", "0"}, - {"any", "@query", "0", "0"}, - {string(Algo_ED25519), "@query", "0", "0"}, - } - - for _, tc := range testcases { - f.Add(tc[0], tc[1], tc[2], tc[3]) - } - - reqtxt, err := os.ReadFile("testdata/rfc-test-request.txt") - if err != nil { - f.Fatal(err) - } - - f.Fuzz(func(t *testing.T, algo, label, keyID, tag string) { - t.Logf("Label: %s\n", label) - t.Logf("keyid: %s\n", keyID) - t.Logf("tag: %s\n", tag) - - fields := Fields(label, keyID, tag) - fields = append(fields, SignedField{ - Name: label, - Parameters: map[string]any{ - keyID: tag, - }, - }) - privKey := sigtest.ReadTestPrivateKey(t, "test-key-ed25519.key") - so := SigningProfile{ - Algorithm: Algo_ED25519, - Fields: Fields(label, keyID, tag), - Metadata: []Metadata{MetaKeyID, MetaTag}, - Label: label, - } - sk := SigningKey{ - Key: privKey, - MetaKeyID: keyID, - MetaTag: tag, - } - if so.validate(sk) != nil { - // Catching invalidate signing options is good. - return - } - - req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(reqtxt))) - if err != nil { - t.Fatal(err) - } - - err = Sign(req, so, sk) - if err != nil { - if _, ok := err.(*SignatureError); ok { - // Handled error - return - } - // Unhandled error - t.Error(err) - } - }) -} - -func FuzzSigningOptionsFields(f *testing.F) { - testcases := [][]string{ - {"", "", ""}, - {"0", "0", "\xde"}, - {"\n", "0", "0"}, - {"", "0", "@"}, - {"@query-param", "name", "0"}, - {"@query", "0", "0"}, - {"@method", "", ""}, - {"@status", "", ""}, - } - - for _, tc := range testcases { - f.Add(tc[0], tc[1], tc[2]) - } - - reqtxt, err := os.ReadFile("testdata/rfc-test-request.txt") - if err != nil { - f.Fatal(err) - } - - f.Fuzz(func(t *testing.T, field, tagName, tagValue string) { - t.Logf("field: %s\n", field) - t.Logf("tag: %s:%s\n", tagName, tagValue) - fields := []SignedField{} - if tagName == "" { - fields = append(fields, SignedField{ - Name: field, - }) - } else { - fields = append(fields, SignedField{ - Name: field, - Parameters: map[string]any{ - tagName: tagValue, - }, - }) - } - - so := SigningProfile{ - Algorithm: Algo_ED25519, - Fields: fields, - } - sk := SigningKey{ - Key: sigtest.ReadTestPrivateKey(t, "test-key-ed25519.key"), - } - if so.validate(sk) != nil { - // Catching invalidate signing options is good. - return - } - - req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(reqtxt))) - if err != nil { - t.Fatal(err) - } - - err = Sign(req, so, sk) - if err != nil { - if _, ok := err.(*SignatureError); ok { - // Handled error - return - } - // Unhandled error - t.Error(err) - } - }) -} - -func FuzzExtractSignatures(f *testing.F) { - testcases := []struct { - SignatureHeader string - SignatureInputHeader string - }{ - { - SignatureHeader: "", - SignatureInputHeader: "", - }, - { - SignatureHeader: "sig-b24=(\"@status\" \"content-type\" \"content-digest\" \"content-length\");created=1618884473;keyid=\"test-key-ecc-p256\"", - SignatureInputHeader: "sig-b24=:wNmSUAhwb5LxtOtOpNa6W5xj067m5hFrj0XQ4fvpaCLx0NKocgPquLgyahnzDnDAUy5eCdlYUEkLIj+32oiasw==:", - }, - } - - for _, tc := range testcases { - f.Add(tc.SignatureHeader, tc.SignatureInputHeader) - } - - reqtxt, err := os.ReadFile("testdata/rfc-test-request.txt") - if err != nil { - f.Fatal(err) - } - - f.Fuzz(func(t *testing.T, sigHeader, sigInputHeader string) { - t.Logf("signature header: %s\n", sigHeader) - t.Logf("signature input header: %s\n", sigInputHeader) - - req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(reqtxt))) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("signature", sigHeader) - req.Header.Set("signature-input", sigInputHeader) - - sigSFV, err := parseSignaturesFromRequest(req.Header) - if err != nil { - return - } - for _, label := range sigSFV.Sigs.Names() { - _, err = unmarshalSignature(sigSFV, label) - if err != nil { - if _, ok := err.(*SignatureError); ok { - // Handled error - return - } - // Unhandled error - t.Error(err) - } - } - }) -} diff --git a/third_party/httpsig-go/go.mod b/third_party/httpsig-go/go.mod deleted file mode 100644 index ca025e3a94..0000000000 --- a/third_party/httpsig-go/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/remitly-oss/httpsig-go - -go 1.21.4 - -require ( - github.com/dunglas/httpsfv v1.0.2 - github.com/google/go-cmp v0.7.0 -) diff --git a/third_party/httpsig-go/go.sum b/third_party/httpsig-go/go.sum deleted file mode 100644 index 239a929214..0000000000 --- a/third_party/httpsig-go/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0= -github.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= diff --git a/third_party/httpsig-go/http.go b/third_party/httpsig-go/http.go deleted file mode 100644 index 54a60eb857..0000000000 --- a/third_party/httpsig-go/http.go +++ /dev/null @@ -1,102 +0,0 @@ -package httpsig - -import ( - "context" - "net/http" -) - -type contextKey int - -const verifyKey contextKey = 0 - -// NewSigningHTTPClient creates an *http.Client that signs requests before sending. If hc is nil a new *http.Client is created. If signer is not nil all requests will be signed. If verifier is not nil all requests will be verified. -func NewHTTPClient(hc *http.Client, signer *Signer, verifier *Verifier) *http.Client { - if hc == nil { - hc = &http.Client{} - } - - hc.Transport = NewTransport(hc.Transport, signer, verifier) - return hc -} - -// NewTransport returns an http.RoundTripper implementation that signs requests and verifies responses if signer and verifier are not nil. If rt is nil http.DefaultTransport is used. -func NewTransport(rt http.RoundTripper, signer *Signer, verifier *Verifier) http.RoundTripper { - if rt == nil { - rt = http.DefaultTransport - } - - return &transport{ - sign: signer != nil, - signer: signer, - verify: verifier != nil, - verifier: verifier, - rt: rt, - } -} - -// VerifyHandler verifies the http signature of each request. If not verified it returns a 401 Unauthorized HTTP error. If verified it puts the verification result in the requests context. Use GetVerifyResult to read the context. -type VerifyHandler struct { - handler http.Handler - verifier *Verifier -} - -// NewHandler wraps an http.Handler with a an http.Handler that verifies each request. verifier cannot be nil. -func NewHandler(h http.Handler, verifier *Verifier) http.Handler { - if verifier == nil { - panic("verifier cannot be nil") - } - return &VerifyHandler{ - handler: h, - verifier: verifier, - } -} - -func (vh VerifyHandler) ServeHTTP(rw http.ResponseWriter, inReq *http.Request) { - vr, err := vh.verifier.Verify(inReq) - if err != nil { - // Failed to verify - rw.Write([]byte("Unauthorized")) - rw.WriteHeader(http.StatusUnauthorized) - return - } - - req := inReq.WithContext(context.WithValue(inReq.Context(), verifyKey, &vr)) - vh.handler.ServeHTTP(rw, req) -} - -// GetVerifyResult returns the results of a successful request signature verification. -func GetVerifyResult(ctx context.Context) (v VerifyResult, found bool) { - if vr, ok := ctx.Value(verifyKey).(*VerifyResult); ok && vr != nil { - return *vr, true - } - return VerifyResult{}, false -} - -// transport implements http.RoundTripper interface. It signs the request before calling the underlying RoundTripper -type transport struct { - sign bool - verify bool - signer *Signer - verifier *Verifier - rt http.RoundTripper -} - -func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { - if t.sign { - // Signing does not read or close the body - err := t.signer.Sign(req) - if err != nil { - return nil, err - } - } - - resp, err := t.rt.RoundTrip(req) - if err != nil { - return resp, err - } - if t.verify { - // Verifying does not read or close the response body - _, err = t.verifier.VerifyResponse(resp) - } - return resp, err -} diff --git a/third_party/httpsig-go/keyman/keyman.go b/third_party/httpsig-go/keyman/keyman.go deleted file mode 100644 index 9161cc3a5c..0000000000 --- a/third_party/httpsig-go/keyman/keyman.go +++ /dev/null @@ -1,34 +0,0 @@ -// keyman provides key management functionality -package keyman - -import ( - "context" - "fmt" - "net/http" - - "github.com/remitly-oss/httpsig-go" -) - -// KeyFetchInMemory implements KeyFetcher for public keys stored in memory. -type KeyFetchInMemory struct { - pubkeys map[string]httpsig.KeySpec -} - -func NewKeyFetchInMemory(pubkeys map[string]httpsig.KeySpec) *KeyFetchInMemory { - if pubkeys == nil { - pubkeys = map[string]httpsig.KeySpec{} - } - return &KeyFetchInMemory{pubkeys} -} - -func (kf *KeyFetchInMemory) FetchByKeyID(ctx context.Context, rh http.Header, keyID string) (httpsig.KeySpecer, error) { - ks, found := kf.pubkeys[keyID] - if !found { - return nil, fmt.Errorf("Key for keyid '%s' not found", keyID) - } - return ks, nil -} - -func (kf *KeyFetchInMemory) Fetch(context.Context, http.Header, httpsig.MetadataProvider) (httpsig.KeySpecer, error) { - return nil, fmt.Errorf("Fetch without keyid not supported") -} diff --git a/third_party/httpsig-go/keyutil/jwk.go b/third_party/httpsig-go/keyutil/jwk.go deleted file mode 100644 index 6a251912c7..0000000000 --- a/third_party/httpsig-go/keyutil/jwk.go +++ /dev/null @@ -1,284 +0,0 @@ -package keyutil - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "encoding/base64" - "encoding/json" - "fmt" - "math/big" - "os" -) - -const ( - KeyTypeEC = "EC" - KeyTypeOct = "oct" -) - -func ReadJWKFile(jwkFile string) (JWK, error) { - keyBytes, err := os.ReadFile(jwkFile) - if err != nil { - return JWK{}, fmt.Errorf("Failed to read jwk key file '%s': %w", jwkFile, err) - } - return ReadJWK(keyBytes) -} - -func ReadJWK(jwkBytes []byte) (JWK, error) { - base := jwk{} - err := json.Unmarshal(jwkBytes, &base) - if err != nil { - return JWK{}, fmt.Errorf("Failed to json parse JWK: %w", err) - } - jwk := JWK{ - KeyType: base.KeyType, - Algorithm: base.Algo, - KeyID: base.KeyID, - } - switch base.KeyType { - case KeyTypeEC: - jec := jwkEC{} - err := json.Unmarshal(jwkBytes, &jec) - if err != nil { - return JWK{}, fmt.Errorf("Failed to json parse JWK: %w", err) - } - jwk.jwtImpl = jec - case KeyTypeOct: - jsym := jwkSymmetric{} - err := json.Unmarshal(jwkBytes, &jsym) - if err != nil { - return JWK{}, fmt.Errorf("Failed to json parse JWK: %w", err) - } - jwk.jwtImpl = jsym - default: - return JWK{}, fmt.Errorf("Unsupported key type/kty - '%s'", base.KeyType) - } - - return jwk, nil -} - -// ReadJWKFromPEM converts a PEM encoded private key to JWK. 'kty' is set based on the passed in PrivateKey type. -func ReadJWKFromPEM(pkeyBytes []byte) (JWK, error) { - pkey, err := ReadPrivateKey(pkeyBytes) - if err != nil { - return JWK{}, err - } - return FromPrivateKey(pkey) -} - -// FromPrivateKey creates a JWK from a crypto.PrivateKey. 'kty' is set based on the passed in PrivateKey. -func FromPrivateKey(pkey crypto.PrivateKey) (JWK, error) { - switch key := pkey.(type) { - case *ecdsa.PrivateKey: - jec := jwkEC{ - jwk: jwk{ - KeyType: KeyTypeEC, - }, - Curve: key.Curve.Params().Name, - X: &octet{*key.X}, - Y: &octet{*key.Y}, - D: &octet{*key.D}, - } - - return JWK{ - KeyType: KeyTypeEC, - jwtImpl: jec, - }, nil - default: - return JWK{}, fmt.Errorf("Unsupported private key type '%T'", pkey) - } -} - -// JWK provides basic data and usage for a JWK. -type JWK struct { - // Common fields are duplicated as struct members for better usability. - KeyType string // 'kty' - "EC", "RSA", "oct" - Algorithm string // 'alg' - KeyID string // 'kid' - jwtImpl any // the specific implementation of JWK based on KeyType. -} - -func (ji *JWK) PublicKey() (crypto.PublicKey, error) { - switch ji.KeyType { - case ji.KeyType: // ECC - if jec, ok := ji.jwtImpl.(jwkEC); ok { - return jec.PublicKey() - } - } - - return nil, fmt.Errorf("Unsupported key type for PublicKey - '%s'", ji.KeyType) -} - -func (ji *JWK) PublicKeyJWK() (JWK, error) { - switch ji.KeyType { - case KeyTypeEC: - if jec, ok := ji.jwtImpl.(jwkEC); ok { - jec.jwk.Algo = ji.Algorithm - jec.jwk.KeyID = ji.KeyID - return jec.PublicKeyJWK() - } - } - - return JWK{}, fmt.Errorf("Unsupported key type for PublicKey'%s'", ji.KeyType) -} - -func (ji *JWK) PrivateKey() (crypto.PrivateKey, error) { - switch ji.KeyType { - case KeyTypeEC: - if jec, ok := ji.jwtImpl.(jwkEC); ok { - return jec.PrivateKey() - } - } - return nil, fmt.Errorf("Unsupported key type PrivateKey - '%s'", ji.KeyType) -} - -func (ji *JWK) SecretKey() ([]byte, error) { - switch ji.KeyType { - case KeyTypeOct: - if jsym, ok := ji.jwtImpl.(jwkSymmetric); ok { - return jsym.Key(), nil - } - - } - return nil, fmt.Errorf("Unsupported key type for Secret '%s'", ji.KeyType) -} - -// octet represents the data for base64 URL encoded data as specified by JWKs. -type octet struct { - big.Int -} - -func (ob octet) MarshalJSON() ([]byte, error) { - out := fmt.Sprintf("\"%s\"", base64.RawURLEncoding.EncodeToString(ob.Bytes())) - return []byte(out), nil -} - -func (ob *octet) UnmarshalJSON(data []byte) error { - // data is the json value and must be unmarshaled into a go string first - encoded := "" - err := json.Unmarshal(data, &encoded) - if err != nil { - return err - } - - rawBytes, err := base64.RawURLEncoding.DecodeString(encoded) - if err != nil { - return fmt.Errorf("Failed to base64 decode: %w", err) - } - - x := new(big.Int) - x.SetBytes(rawBytes) - *ob = octet{*x} - - return nil -} - -type jwk struct { - KeyType string `json:"kty"` // kty algorithm family used with the key such as "RSA" or "EC". - Algo string `json:"alg,omitempty"` // alg identifies the algorithm intended for use with the key. - KeyID string `json:"kid,omitempty"` // Used to match a specific key -} - -type jwkEC struct { - jwk - Curve string `json:"crv"` // The curve used with the key e.g. P-256 - X *octet `json:"x"` // x coordinate of the curve. - Y *octet `json:"y"` // y coordinate of the curve. - D *octet `json:"d,omitempty"` // For private keys. -} - -func (ec *jwkEC) params() (crv elliptic.Curve, byteLen int, e error) { - switch ec.Curve { - case "P-256": - crv = elliptic.P256() - case "P-384": - crv = elliptic.P384() - case "P-521": - crv = elliptic.P521() - default: - return nil, 0, fmt.Errorf("Unsupported ECC curve '%s'", ec.Curve) - } - return crv, crv.Params().BitSize / 8, nil -} - -func (ec *jwkEC) PublicKey() (*ecdsa.PublicKey, error) { - crv, byteLen, err := ec.params() - if err != nil { - return nil, err - } - - if len(ec.X.Bytes()) != byteLen { - return nil, fmt.Errorf("X coordinate must be %d byte length for curve '%s'. Got '%d'", byteLen, ec.Curve, len(ec.X.Bytes())) - } - if len(ec.Y.Bytes()) != byteLen { - return nil, fmt.Errorf("Y coordinate must be %d byte length for curve '%s'. Got '%d'", byteLen, ec.Curve, len(ec.Y.Bytes())) - } - - return &ecdsa.PublicKey{ - Curve: crv, - X: &ec.X.Int, - Y: &ec.Y.Int, - }, nil -} - -func (ec *jwkEC) PublicKeyJWK() (JWK, error) { - return JWK{ - KeyType: ec.KeyType, - Algorithm: ec.Algo, - KeyID: ec.KeyID, - jwtImpl: jwkEC{ - jwk: ec.jwk, - Curve: ec.Curve, - X: ec.X, - Y: ec.Y, - }, - }, nil -} - -func (ec *jwkEC) PrivateKey() (*ecdsa.PrivateKey, error) { - if ec.D == nil { - return nil, fmt.Errorf("JWK does not contain a private key") - } - pubkey, err := ec.PublicKey() - if err != nil { - return nil, err - } - _, byteLen, err := ec.params() - if err != nil { - return nil, err - } - - if len(ec.D.Bytes()) != byteLen { - return nil, fmt.Errorf("D coordinate must be %d byte length for curve '%s'. Got '%d'", byteLen, ec.Curve, len(ec.D.Bytes())) - } - - return &ecdsa.PrivateKey{ - PublicKey: *pubkey, - D: &ec.D.Int, - }, nil -} - -type jwkSymmetric struct { - jwk - K *octet `json:"k" ` // Symmetric key -} - -func (js *jwkSymmetric) Key() []byte { - return js.K.Bytes() -} - -func (j JWK) MarshalJSON() ([]byte, error) { - // Set the Algo and KeyID in case the JWK fields have changed - switch jt := j.jwtImpl.(type) { - case jwkEC: - jt.jwk.Algo = j.Algorithm - jt.jwk.KeyID = j.KeyID - return json.Marshal(jt) - case jwkSymmetric: - jt.jwk.Algo = j.Algorithm - jt.jwk.KeyID = j.KeyID - return json.Marshal(jt) - } - - return json.Marshal(j.jwtImpl) -} diff --git a/third_party/httpsig-go/keyutil/jwk_test.go b/third_party/httpsig-go/keyutil/jwk_test.go deleted file mode 100644 index a01312115c..0000000000 --- a/third_party/httpsig-go/keyutil/jwk_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package keyutil - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "encoding/json" - "strings" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func TestParseJWK(t *testing.T) { - tests := []struct { - Name string - InputFile string // one of InputFile or Input is used - Input string - Expected JWK - ExpectedErrContains string - }{ - { - Name: "Valid EC JWK", - InputFile: "testdata/test-jwk-ec.json", - Expected: JWK{ - KeyType: "EC", - KeyID: "test-key-ecc-p256", - }, - }, - { - - Name: "Valid symmetric JWK", - InputFile: "testdata/test-jwk-symmetric.json", - Expected: JWK{ - - KeyType: "oct", - KeyID: "test-symmetric-key", - }, - }, - { - Name: "Invalid JSON", - Input: `{"kty": malformed`, - ExpectedErrContains: "parse", - }, - { - Name: "Empty input", - Input: "", - ExpectedErrContains: "parse", - }, - } - - for _, tc := range tests { - t.Run(tc.Name, func(t *testing.T) { - var actual JWK - var actualErr error - if tc.InputFile != "" { - actual, actualErr = ReadJWKFile(tc.InputFile) - } else { - actual, actualErr = ReadJWK([]byte(tc.Input)) - } - - if actualErr != nil { - if !strings.Contains(actualErr.Error(), tc.ExpectedErrContains) { - Diff(t, tc.ExpectedErrContains, actualErr.Error(), "Wrong error") - } - return - } - - Diff(t, tc.Expected, actual, "Wrong JWK", cmpopts.IgnoreUnexported(JWK{})) - }) - } -} - -func TestJWKMarshalRoundTrip(t *testing.T) { - tests := []struct { - name string - inputType string - expectedErrContains string - keyid string - algorithm string - }{ - { - name: "EC Key Round Trip", - inputType: "EC", - keyid: "mykey_123", - algorithm: "myalgo", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var pk crypto.PrivateKey - switch tc.inputType { - case "EC": - var err error - pk, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatal(err) - } - } - - original, err := FromPrivateKey(pk) - original.KeyID = tc.keyid - original.Algorithm = tc.algorithm - if err != nil { - if tc.expectedErrContains != "" { - if !strings.Contains(err.Error(), tc.expectedErrContains) { - t.Errorf("Expected error containing %q, got: %v", tc.expectedErrContains, err) - } - return - } - t.Fatalf("Failed to generate create JWK from private key: %v", err) - } - - // Marshal JWK to JSON - jsonBytes, err := json.Marshal(original) - if err != nil { - t.Fatalf("Failed to marshal JWK: %v", err) - } - - // Unmarshal back to new JWK - roundTripped, err := ReadJWK(jsonBytes) - if err != nil { - t.Fatalf("Failed to unmarshal round-tripped JWK: %v", err) - } - - // Compare original and round-tripped JWKs - Diff(t, original, roundTripped, "Round-tripped JWK differs from original", cmpopts.IgnoreUnexported(JWK{})) - Diff(t, tc.keyid, roundTripped.KeyID, "Round-tripped JWK differs from original", cmpopts.IgnoreUnexported(JWK{})) - }) - } -} - -func Diff(t *testing.T, expected, actual any, msg string, opts ...cmp.Option) bool { - if diff := cmp.Diff(expected, actual, opts...); diff != "" { - t.Errorf("%s (-want +got):\n%s", msg, diff) - return true - } - return false -} diff --git a/third_party/httpsig-go/keyutil/keyutil.go b/third_party/httpsig-go/keyutil/keyutil.go deleted file mode 100644 index 4018dbf8a8..0000000000 --- a/third_party/httpsig-go/keyutil/keyutil.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -Package keyutil provides some basic PEM and JWK key handling without dependencies. It is not meant as a replacement for a full key handling library. -*/ -package keyutil - -import ( - "crypto" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/pem" - "fmt" - "os" -) - -var ( - oidPublicKeyRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} -) - -// MustReadPublicKeyFile reads a PEM encoded public key file or panics -func MustReadPublicKeyFile(pubkeyFile string) crypto.PublicKey { - pk, err := ReadPublicKeyFile(pubkeyFile) - if err != nil { - panic(err) - } - return pk -} - -// ReadPublicKeyFile reads a PEM encdoded public key file and parses into crypto.PublicKey -func ReadPublicKeyFile(pubkeyFile string) (crypto.PublicKey, error) { - keyBytes, err := os.ReadFile(pubkeyFile) - if err != nil { - return nil, fmt.Errorf("Failed to read public key file '%s': %w", pubkeyFile, err) - } - return ReadPublicKey(keyBytes) -} - -// ReadPublicKey decodes a PEM encoded public key and parses into crypto.PublicKey -func ReadPublicKey(encodedPubkey []byte) (crypto.PublicKey, error) { - block, _ := pem.Decode(encodedPubkey) - if block == nil { - return nil, fmt.Errorf("Failed to PEM decode public key") - } - var key crypto.PublicKey - var err error - - switch block.Type { - case "PUBLIC KEY": - key, err = x509.ParsePKIXPublicKey(block.Bytes) - case "RSA PUBLIC KEY": - key, err = x509.ParsePKCS1PublicKey(block.Bytes) - default: - return nil, fmt.Errorf("Unsupported pubkey format '%s'", block.Type) - } - if err != nil { - return nil, fmt.Errorf("Failed to parse public key with format '%s': %w", block.Type, err) - } - - return key, nil -} - -// MustReadPrivateKeyFile decodes a PEM encoded private key file and parses into a crypto.PrivateKey or panics. -func MustReadPrivateKeyFile(pkFile string) crypto.PrivateKey { - pk, err := ReadPrivateKeyFile(pkFile) - if err != nil { - panic(err) - } - return pk -} - -func MustReadPrivateKey(encodedPrivateKey []byte) crypto.PrivateKey { - pkey, err := ReadPrivateKey(encodedPrivateKey) - if err != nil { - panic(err) - } - return pkey -} - -// ReadPrivateKeyFile opens the given file and calls ReadPrivateKey to return a crypto.PrivateKey -func ReadPrivateKeyFile(pkFile string) (crypto.PrivateKey, error) { - keyBytes, err := os.ReadFile(pkFile) - if err != nil { - return nil, fmt.Errorf("Failed to read private key file '%s': %w", pkFile, err) - } - return ReadPrivateKey(keyBytes) -} - -// ReadPrivateKey decoded a PEM encoded private key and parses into a crypto.PrivateKey. -func ReadPrivateKey(encodedPrivateKey []byte) (crypto.PrivateKey, error) { - block, _ := pem.Decode(encodedPrivateKey) - - if block == nil { - return nil, fmt.Errorf("Failed to PEM decode private key") - } - - var key crypto.PrivateKey - var err error - - switch block.Type { - case "PRIVATE KEY": - key, err = x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - // Try to handle RSAPSS - psskey, psserr := parseRSAPSS(block) - if psserr == nil { - // success - key = psskey - err = psserr - } - } - case "EC PRIVATE KEY": - key, err = x509.ParseECPrivateKey(block.Bytes) - case "RSA PRIVATE KEY": - key, err = x509.ParsePKCS1PrivateKey(block.Bytes) - default: - return nil, fmt.Errorf("Unsupported private key format '%s'", block.Type) - } - if err != nil { - return nil, fmt.Errorf("Failed to parse private key with format '%s': %w", block.Type, err) - } - return key, nil -} - -func parseRSAPSS(block *pem.Block) (crypto.PrivateKey, error) { - // The rsa-pss key is PKCS8 encoded but the golang 1.19 parser doesn't recognize the algorithm and gives 'PKCS#8 wrapping contained private key with unknown algorithm: 1.2.840.113549.1.1.10 - - // Instead do the asn1 unmarshaling and check here. - pkcs8 := struct { - Version int - Algo pkix.AlgorithmIdentifier - PrivateKey []byte - }{} - - _, err := asn1.Unmarshal(block.Bytes, &pkcs8) - if err != nil { - return nil, fmt.Errorf("Failed to ans1 unmarshal private key: %w", err) - } - - if !pkcs8.Algo.Algorithm.Equal(oidPublicKeyRSAPSS) { - return nil, fmt.Errorf("PKCS#8 wrapping contained private key with unknown algorithm: %s", pkcs8.Algo.Algorithm) - } - return x509.ParsePKCS1PrivateKey(pkcs8.PrivateKey) -} diff --git a/third_party/httpsig-go/keyutil/testdata/test-jwk-ec.json b/third_party/httpsig-go/keyutil/testdata/test-jwk-ec.json deleted file mode 100644 index 171db167f3..0000000000 --- a/third_party/httpsig-go/keyutil/testdata/test-jwk-ec.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "kty": "EC", - "crv": "P-256", - "kid": "test-key-ecc-p256", - "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" -} \ No newline at end of file diff --git a/third_party/httpsig-go/keyutil/testdata/test-jwk-symmetric.json b/third_party/httpsig-go/keyutil/testdata/test-jwk-symmetric.json deleted file mode 100644 index d0bd22e37f..0000000000 --- a/third_party/httpsig-go/keyutil/testdata/test-jwk-symmetric.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "kty": "oct", - "kid": "test-symmetric-key", - "k": "QYPe3dGeFrKhEEK4CSr9_nES-ExXI9Nw" -} \ No newline at end of file diff --git a/third_party/httpsig-go/roundtrip_test.go b/third_party/httpsig-go/roundtrip_test.go deleted file mode 100644 index 52c3ee4466..0000000000 --- a/third_party/httpsig-go/roundtrip_test.go +++ /dev/null @@ -1,217 +0,0 @@ -package httpsig_test - -import ( - "crypto" - "testing" - - "github.com/remitly-oss/httpsig-go" - "github.com/remitly-oss/httpsig-go/keyman" - "github.com/remitly-oss/httpsig-go/keyutil" - "github.com/remitly-oss/httpsig-go/sigtest" -) - -// TestRoundTrip tests that the signing code can be verified by the verify code. -func TestRoundTrip(t *testing.T) { - - testcases := []struct { - Name string - PrivateKey crypto.PrivateKey - MetaKeyID string - Secret []byte - SignProfile httpsig.SigningProfile - RequestFile string - Keys httpsig.KeyFetcher - Profile httpsig.VerifyProfile - ExpectedErrCodeVerify httpsig.ErrCode - }{ - { - Name: "RSA-PSS", - PrivateKey: keyutil.MustReadPrivateKeyFile("testdata/test-key-rsa-pss.key"), - MetaKeyID: "test-key-rsa", - SignProfile: httpsig.SigningProfile{ - Algorithm: httpsig.Algo_RSA_PSS_SHA512, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - Label: "tst-rsa-pss", - }, - RequestFile: "rfc-test-request.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-rsa": { - KeyID: "test-key-rsa", - Algo: httpsig.Algo_RSA_PSS_SHA512, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-rsa-pss.pub"), - }, - }), - Profile: createVerifyProfile("tst-rsa-pss"), - }, - { - Name: "RSA-v15", - PrivateKey: keyutil.MustReadPrivateKeyFile("testdata/key-rsa-v15.key"), - MetaKeyID: "test-key-rsa", - SignProfile: httpsig.SigningProfile{ - Algorithm: httpsig.Algo_RSA_v1_5_sha256, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - Label: "tst-rsa-pss", - }, - RequestFile: "rfc-test-request.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-rsa": { - KeyID: "test-key-rsa", - Algo: httpsig.Algo_RSA_v1_5_sha256, - PubKey: keyutil.MustReadPublicKeyFile("testdata/key-rsa-v15.pub"), - }, - }), - Profile: createVerifyProfile("tst-rsa-pss"), - }, - { - Name: "HMAC_SHA256", - Secret: sigtest.MustReadFile("testdata/test-shared-secret"), - MetaKeyID: "test-key-shared", - SignProfile: httpsig.SigningProfile{ - Algorithm: httpsig.Algo_HMAC_SHA256, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - }, - RequestFile: "rfc-test-request.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-shared": { - KeyID: "test-key-shared", - Algo: httpsig.Algo_HMAC_SHA256, - Secret: sigtest.MustReadFile("testdata/test-shared-secret"), - }, - }), - Profile: createVerifyProfile("sig1"), - }, - { - Name: "ECDSA-p265", - PrivateKey: keyutil.MustReadPrivateKeyFile("testdata/test-key-ecc-p256.key"), - MetaKeyID: "test-key-ecdsa", - SignProfile: httpsig.SigningProfile{ - Algorithm: httpsig.Algo_ECDSA_P256_SHA256, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - Label: "tst-ecdsa", - }, - RequestFile: "rfc-test-request.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-ecdsa": { - KeyID: "test-key-ecds", - Algo: httpsig.Algo_ECDSA_P256_SHA256, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-ecc-p256.pub"), - }, - }), - Profile: createVerifyProfile("tst-ecdsa"), - }, - { - Name: "ECDSA-p384", - PrivateKey: keyutil.MustReadPrivateKeyFile("testdata/test-key-ecc-p384.key"), - MetaKeyID: "test-key-ecdsa", - SignProfile: httpsig.SigningProfile{ - Algorithm: httpsig.Algo_ECDSA_P384_SHA384, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - Label: "tst-ecdsa", - }, - RequestFile: "rfc-test-request.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-ecdsa": { - KeyID: "test-key-ecdsa", - Algo: httpsig.Algo_ECDSA_P384_SHA384, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-ecc-p384.pub"), - }, - }), - Profile: createVerifyProfile("tst-ecdsa"), - }, - { - Name: "ED25519", - PrivateKey: keyutil.MustReadPrivateKeyFile("testdata/test-key-ed25519.key"), - MetaKeyID: "test-key-ed", - SignProfile: httpsig.SigningProfile{ - Algorithm: httpsig.Algo_ED25519, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - Label: "tst-ed", - }, - RequestFile: "rfc-test-request.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-ed": { - KeyID: "test-key-ed", - Algo: httpsig.Algo_ED25519, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-ed25519.pub"), - }, - }), - Profile: createVerifyProfile("tst-ed"), - }, - { - Name: "BadDigest", - PrivateKey: keyutil.MustReadPrivateKeyFile("testdata/test-key-ed25519.key"), - MetaKeyID: "test-key-ed", - SignProfile: httpsig.SigningProfile{ - - Algorithm: httpsig.Algo_ED25519, - Fields: httpsig.DefaultRequiredFields, - Metadata: []httpsig.Metadata{httpsig.MetaCreated, httpsig.MetaKeyID}, - Label: "tst-content-digest", - }, - RequestFile: "request_bad_digest.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-ed": { - KeyID: "test-key-ed", - Algo: httpsig.Algo_ED25519, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-ed25519.pub"), - }, - }), - Profile: createVerifyProfile("tst-content-digest"), - ExpectedErrCodeVerify: httpsig.ErrNoSigWrongDigest, - }, - } - - for _, tc := range testcases { - t.Run(tc.Name, func(t *testing.T) { - var signer *httpsig.Signer - sk := httpsig.SigningKey{ - Key: tc.PrivateKey, - Secret: tc.Secret, - MetaKeyID: tc.MetaKeyID, - } - - signer, err := httpsig.NewSigner(tc.SignProfile, sk) - if err != nil { - t.Fatal(err) - } - - req := sigtest.ReadRequest(t, tc.RequestFile) - err = signer.Sign(req) - if err != nil { - t.Fatalf("%#v", err) - } - t.Log(req.Header.Get("Signature-Input")) - t.Log(req.Header.Get("Signature")) - ver, err := httpsig.NewVerifier(tc.Keys, tc.Profile) - if err != nil { - t.Fatal(err) - } - vf, err := ver.Verify(req) - if err != nil { - if tc.ExpectedErrCodeVerify != "" { - if sigerr, ok := err.(*httpsig.SignatureError); ok { - sigtest.Diff(t, tc.ExpectedErrCodeVerify, sigerr.Code, "Wrong err code") - } - } else { - t.Fatalf("%#v", err) - } - } else if tc.ExpectedErrCodeVerify != "" { - t.Fatal("Expected error") - } - t.Logf("%+v\n", vf) - }) - - } -} - -func createVerifyProfile(label string) httpsig.VerifyProfile { - vp := httpsig.DefaultVerifyProfile - vp.SignatureLabel = label - return vp -} diff --git a/third_party/httpsig-go/side.toml b/third_party/httpsig-go/side.toml deleted file mode 100644 index 95ce9e536b..0000000000 --- a/third_party/httpsig-go/side.toml +++ /dev/null @@ -1,5 +0,0 @@ -max_iterations = 0 -max_planning_iterations = 0 - -[[test_commands]] - command = "go test" diff --git a/third_party/httpsig-go/sigerrors.go b/third_party/httpsig-go/sigerrors.go deleted file mode 100644 index fc7807f055..0000000000 --- a/third_party/httpsig-go/sigerrors.go +++ /dev/null @@ -1,89 +0,0 @@ -package httpsig - -import ( - "errors" - "fmt" -) - -// ErrCode enumerates the reasons a signing or verification can fail -type ErrCode string - -const ( - // Error Codes - - // Errors related to not being able to extract a valid signature. - ErrNoSigInvalidHeader ErrCode = "nosig_invalid_header" - ErrNoSigUnsupportedDigest ErrCode = "nosig_unsupported_digest" - ErrNoSigWrongDigest ErrCode = "nosig_wrong_digest" - ErrNoSigMissingSignature ErrCode = "nosig_missing_signature" - ErrNoSigInvalidSignature ErrCode = "nosig_invalid_signature" - ErrNoSigMessageBody ErrCode = "nosig_message_body" // Could not read message body - - // Errors related to an individual signature. - ErrSigInvalidSignature ErrCode = "sig_invalid_signature" // The signature is unparseable or in the wrong format. - ErrSigKeyFetch ErrCode = "sig_key_fetch" // Failed to the key for a signature - ErrSigVerification ErrCode = "sig_failed_algo_verification" // The signature did not verify according to the algorithm. - ErrSigPublicKey ErrCode = "sig_public_key" // The public key for the signature is invalid or missing. - ErrSigSecretKey ErrCode = "sig_secret_key" // The secret key for the signature is invalid or missing. - ErrSigUnsupportedAlgorithm ErrCode = "sig_unsupported_algorithm" // unsupported or invalid algorithm. - ErrSigProfile ErrCode = "sig_failed_profile" // The signature was valid but failed the verify profile check - - // Signing - ErrInvalidSignatureOptions ErrCode = "invalid_signature_options" - ErrInvalidComponent ErrCode = "invalid_component" - ErrInvalidMetadata ErrCode = "invalid_metadata" - - // Accept Signature - ErrInvalidAcceptSignature ErrCode = "invalid_accept_signature" - ErrMissingAcceptSignature ErrCode = "missing_accept_signature" // The Accept-Signature field was present but had an empty value. - - // General - ErrInternal ErrCode = "internal_error" - ErrUnsupported ErrCode = "unsupported" // A particular feature of the spec is not supported -) - -type SignatureError struct { - Cause error // may be nil - Code ErrCode - Message string -} - -func (se *SignatureError) Error() string { - return se.Message -} - -func (se *SignatureError) Unwrap() error { - return se.Cause -} - -func (se *SignatureError) GoString() string { - cause := "" - if se.Cause != nil { - cause = fmt.Sprintf("Cause: %s\n", se.Cause) - } - return fmt.Sprintf("Code: %s\nMessage: %s\n%s", se.Code, se.Message, cause) -} - -func newError(code ErrCode, msg string, cause ...error) *SignatureError { - var rootErr error - if len(cause) > 0 { - rootErr = cause[0] - } - return &SignatureError{ - Cause: rootErr, - Code: code, - Message: msg, - } -} - -func errCode(err error) (ec ErrCode) { - if err == nil { - return "" - } - var se *SignatureError - if errors.As(err, &se) { - return se.Code - } - - return "" -} diff --git a/third_party/httpsig-go/sign.go b/third_party/httpsig-go/sign.go deleted file mode 100644 index 025721cc9c..0000000000 --- a/third_party/httpsig-go/sign.go +++ /dev/null @@ -1,332 +0,0 @@ -package httpsig - -import ( - "crypto" - "crypto/rand" - "encoding/base64" - "fmt" - "net/http" - "strings" - "time" - "unicode" - - sfv "github.com/dunglas/httpsfv" -) - -type Algorithm string -type Digest string - -// Metadata are the named signature metadata parameters -type Metadata string - -type CreatedScheme int -type ExpiresScheme int -type NonceScheme int - -const ( - // Supported signing algorithms - Algo_RSA_PSS_SHA512 Algorithm = "rsa-pss-sha512" - Algo_RSA_v1_5_sha256 Algorithm = "rsa-v1_5-sha256" - Algo_HMAC_SHA256 Algorithm = "hmac-sha256" - Algo_ECDSA_P256_SHA256 Algorithm = "ecdsa-p256-sha256" - Algo_ECDSA_P384_SHA384 Algorithm = "ecdsa-p384-sha384" - Algo_ED25519 Algorithm = "ed25519" - - DigestSHA256 Digest = "sha-256" - DigestSHA512 Digest = "sha-512" - - // Signature metadata parameters - MetaCreated Metadata = "created" - MetaExpires Metadata = "expires" - MetaNonce Metadata = "nonce" - MetaAlgorithm Metadata = "alg" - MetaKeyID Metadata = "keyid" - MetaTag Metadata = "tag" - - // DefaultSignatureLabel is the label that will be used for a signature if not label is provided in the parameters. - // A request can contain multiple signatures therefore each signature is labeled. - DefaultSignatureLabel = "sig1" - - // Nonce schemes - NonceRandom32 = iota // 32 bit random nonce. Base64 encoded -) - -// SigningProfile is the set of fields, metadata, and the label to include in a signature. -type SigningProfile struct { - Algorithm Algorithm - Digest Digest // The http digest algorithm to apply. Defaults to sha-256. - Fields []SignedField // Fields and Derived components to sign. - Metadata []Metadata // Metadata parameters to add to the signature. - Label string // The signature label. Defaults to DefaultSignatureLabel. - ExpiresDuration time.Duration // Current time plus this duration. Default duration 5 minutes. Used only if included in Metadata. - Nonce NonceScheme // Scheme to use for generating the nonce if included in Metadata. -} - -// SignedField indicates which part of the request or response to use for signing. -// This is the 'message component' in the specification. -type SignedField struct { - Name string - Parameters map[string]any // Parameters are modifiers applied to the field that changes the way the signature is calculated. -} - -// Fields turns a list of fields into the full specification. Used when the signed fields/components do not need to specify any parameters -func Fields(fields ...string) []SignedField { - all := []SignedField{} - for _, field := range fields { - all = append(all, SignedField{ - Name: strings.ToLower(field), - Parameters: map[string]any{}, - }) - } - return all -} - -type SigningKey struct { - Key crypto.PrivateKey // private key for asymmetric algorithms - Secret []byte // Secret to use for symmetric algorithms - // Meta fields - MetaKeyID string // 'keyid' - Only used if 'keyid' is set in the SigningProfile. A value must be provided if the parameter is required in the SigningProfile. Metadata. - MetaTag string // 'tag'. Only used if 'tag' is set in the SigningProfile. A value must be provided if the parameter is required in the SigningProfile. -} - -type Signer struct { - profile SigningProfile - skey SigningKey -} - -func NewSigner(profile SigningProfile, skey SigningKey) (*Signer, error) { - err := profile.validate(skey) - if err != nil { - return nil, err - } - - opts := profile.withDefaults() - s := &Signer{ - profile: opts, - skey: skey, - } - return s, nil -} - -func Sign(req *http.Request, params SigningProfile, skey SigningKey) error { - s, err := NewSigner(params, skey) - if err != nil { - return err - } - return s.Sign(req) -} - -// Sign signs the request and adds the signature headers to the request. -// If the signature fields includes Content-Digest and Content-Digest is not already included in the request then Sign will read the request body to calculate the digest and set the header. The request body will be replaced with a new io.ReaderCloser. -func (s *Signer) Sign(req *http.Request) error { - // Add the content-digest if covered by the signature and not already present - if signedFields(s.profile.Fields).includes("content-digest") && req.Header.Get("Content-Digest") == "" { - di, err := digestBody(s.profile.Digest, req.Body) - if err != nil { - return err - } - req.Body = di.NewBody - digestValue, err := createDigestHeader(s.profile.Digest, di.Digest) - if err != nil { - return err - } - req.Header.Set("Content-Digest", digestValue) - } - - baseParams, err := s.baseParameters() - if err != nil { - return err - } - - return sign( - httpMessage{ - Req: req, - }, sigParameters{ - Base: baseParams, - Algo: s.profile.Algorithm, - PrivateKey: s.skey.Key, - Secret: s.skey.Secret, - Label: s.profile.Label, - }) -} - -func (s *Signer) SignResponse(resp *http.Response) error { - baseParams, err := s.baseParameters() - if err != nil { - return err - } - - return sign( - httpMessage{ - IsResponse: true, - Resp: resp, - }, sigParameters{ - Base: baseParams, - Algo: s.profile.Algorithm, - PrivateKey: s.skey.Key, - Secret: s.skey.Secret, - Label: s.profile.Label, - }) -} - -func (s *Signer) baseParameters() (sigBaseInput, error) { - bp := sigBaseInput{ - Components: componentsIDs(s.profile.Fields), - MetadataParams: s.profile.Metadata, - MetadataValues: s, - } - - return bp, nil -} - -func (s *Signer) Created() (int, error) { - return int(time.Now().Unix()), nil -} - -func (s *Signer) Expires() (int, error) { - return int(time.Now().Add(s.profile.ExpiresDuration).Unix()), nil -} - -func (s *Signer) Nonce() (string, error) { - switch s.profile.Nonce { - case NonceRandom32: - return nonceRandom32() - } - return "", fmt.Errorf("Invalid nonce scheme '%d'", s.profile.Nonce) -} - -func (s *Signer) Alg() (string, error) { - return string(s.profile.Algorithm), nil -} - -func (s *Signer) KeyID() (string, error) { - return s.skey.MetaKeyID, nil -} - -func (s *Signer) Tag() (string, error) { - return s.skey.MetaTag, nil -} - -func (so SigningProfile) validate(skey SigningKey) error { - if so.Algorithm == "" { - return fmt.Errorf("Missing required signing option 'Algorithm'") - } - if so.Algorithm.symmetric() && len(skey.Secret) == 0 { - return newError(ErrInvalidSignatureOptions, "Missing required 'Secret' value in SigningKey") - } - if !so.Algorithm.symmetric() && skey.Key == nil { - return newError(ErrInvalidSignatureOptions, "Missing required 'Key' value in SigningKey") - } - if !isSafeString(so.Label) { - return fmt.Errorf("Invalid label name '%s'", so.Label) - } - for _, sf := range so.Fields { - if !isSafeString(sf.Name) { - return fmt.Errorf("Invalid signing field name '%s'", sf.Name) - } - } - - for _, md := range so.Metadata { - switch md { - case MetaKeyID: - if skey.MetaKeyID == "" { - return fmt.Errorf("'keyid' metadata parameter was listed but missing MetaKeyID value'") - } - if !isSafeString(skey.MetaKeyID) { - return fmt.Errorf("'keyid' metadata parameter can only contain printable characters'") - } - case MetaTag: - if skey.MetaTag == "" { - return fmt.Errorf("'tag' metadata parameter was listed but missing MetaTag value'") - } - if !isSafeString(skey.MetaTag) { - return fmt.Errorf("'tag' metadata parameter can only contain printable characters'") - } - } - } - return nil -} - -func (sp SigningProfile) withDefaults() SigningProfile { - final := SigningProfile{ - Algorithm: sp.Algorithm, - Digest: sp.Digest, - Fields: sp.Fields, - Metadata: sp.Metadata, - Label: sp.Label, - ExpiresDuration: sp.ExpiresDuration, - Nonce: NonceRandom32, - } - // Defaults - if final.Label == "" { - final.Label = DefaultSignatureLabel - } - if final.ExpiresDuration == 0 { - final.ExpiresDuration = time.Minute * 5 - } - if final.Digest == "" { - final.Digest = DigestSHA256 - } - - return final -} - -func (sf SignedField) componentID() componentID { - item := sfv.NewItem(sf.Name) - for key, param := range sf.Parameters { - item.Params.Add(key, param) - } - return componentID{ - Name: strings.ToLower(sf.Name), - Item: item, - } -} - -type signedFields []SignedField - -func (sf signedFields) includes(field string) bool { - target := strings.ToLower(field) - for _, fld := range sf { - if fld.Name == target { - return true - } - } - return false -} - -func (a Algorithm) symmetric() bool { - switch a { - case Algo_HMAC_SHA256: - return true - } - return false -} -func componentsIDs(sfs []SignedField) []componentID { - cIDs := []componentID{} - for _, sf := range sfs { - cIDs = append(cIDs, sf.componentID()) - } - - return cIDs -} - -func nonceRandom32() (string, error) { - nonce := make([]byte, 32) - n, err := rand.Read(nonce) - if err != nil || n < 32 { - return "", fmt.Errorf("could not generate nonce") - } - return base64.StdEncoding.EncodeToString(nonce), nil -} - -func isSafeString(s string) bool { - for _, c := range s { - if !unicode.IsPrint(c) { - return false - } - if c > unicode.MaxASCII { - return false - } - } - return true -} diff --git a/third_party/httpsig-go/sign_test.go b/third_party/httpsig-go/sign_test.go deleted file mode 100644 index 7cb3480a04..0000000000 --- a/third_party/httpsig-go/sign_test.go +++ /dev/null @@ -1,275 +0,0 @@ -package httpsig - -import ( - "bufio" - "fmt" - "net/http" - "os" - "testing" - - "github.com/remitly-oss/httpsig-go/sigtest" -) - -// testcaseSigBase is a test case for signature bases -type testcaseSigBase struct { - Name string - Params sigBaseInput - IsResponse bool - SourceFile string // defaults to the specification request or response file - ExpectedFile string - ExpectedErr ErrCode -} - -func TestSignatureBase(t *testing.T) { - cases := []testcaseSigBase{ - { - Name: "RepeatedComponents", - Params: sigBaseInput{ - Components: makeComponents("one", "two", "one", "three"), - MetadataParams: []Metadata{}, - MetadataValues: emptyMeta, - }, - SourceFile: "request_repeated_components.txt", - ExpectedErr: ErrInvalidSignatureOptions, - }, - { - Name: "BadComponentName", - Params: sigBaseInput{ - Components: makeComponents("\xd3", "two", "one", "three"), - MetadataParams: []Metadata{}, - MetadataValues: emptyMeta, - }, - ExpectedErr: ErrInvalidComponent, - }, - { - Name: "NoMultiValueSuport", - Params: sigBaseInput{ - Components: makeComponents("one"), - MetadataParams: []Metadata{}, - MetadataValues: emptyMeta, - }, - ExpectedErr: ErrUnsupported, - SourceFile: "request_multivalue.txt", - }, - { - Name: "BadMeta-Created", - Params: sigBaseInput{ - Components: makeComponents(), - MetadataParams: []Metadata{ - MetaCreated, - }, - MetadataValues: errorMetadataProvider{}, - }, - ExpectedErr: ErrInvalidMetadata, - }, - { - Name: "BadMeta-Expires", - Params: sigBaseInput{ - Components: makeComponents(), - MetadataParams: []Metadata{ - MetaExpires, - }, - MetadataValues: errorMetadataProvider{}, - }, - ExpectedErr: ErrInvalidMetadata, - }, - { - Name: "BadMeta-Nonce", - Params: sigBaseInput{ - Components: makeComponents(), - MetadataParams: []Metadata{ - MetaNonce, - }, - MetadataValues: errorMetadataProvider{}, - }, - ExpectedErr: ErrInvalidMetadata, - }, - { - Name: "BadMeta-Algorithm", - Params: sigBaseInput{ - Components: makeComponents(), - MetadataParams: []Metadata{ - MetaAlgorithm, - }, - MetadataValues: errorMetadataProvider{}, - }, - ExpectedErr: ErrInvalidMetadata, - }, - { - Name: "BadMeta-KeyID", - Params: sigBaseInput{ - Components: makeComponents(), - MetadataParams: []Metadata{ - MetaKeyID, - }, - MetadataValues: errorMetadataProvider{}, - }, - ExpectedErr: ErrInvalidMetadata, - }, - { - Name: "BadMeta-Tag", - Params: sigBaseInput{ - Components: makeComponents(), - MetadataParams: []Metadata{ - MetaTag, - }, - MetadataValues: errorMetadataProvider{}, - }, - ExpectedErr: ErrInvalidMetadata, - }, - } - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - runTestSigBase(t, tc) - }) - } - -} - -func runTestSigBase(t *testing.T, tc testcaseSigBase) { - sourceFile := tc.SourceFile - hrr := httpMessage{ - IsResponse: tc.IsResponse, - } - if tc.IsResponse { - if sourceFile == "" { - sourceFile = "rfc-test-response.txt" - } - resptxt, err := os.Open(fmt.Sprintf("testdata/%s", sourceFile)) - if err != nil { - t.Fatal(err) - } - - resp, err := http.ReadResponse(bufio.NewReader(resptxt), nil) - if err != nil { - t.Fatal(err) - } - hrr.Resp = resp - } else { - if sourceFile == "" { - sourceFile = "rfc-test-request.txt" - } - // request - reqtxt, err := os.Open(fmt.Sprintf("testdata/%s", sourceFile)) - if err != nil { - t.Fatal(err) - } - - req, err := http.ReadRequest(bufio.NewReader(reqtxt)) - if err != nil { - t.Fatal(err) - } - hrr.Req = req - } - - actualBase, err := calculateSignatureBase(hrr, tc.Params) - if sigtest.Diff(t, tc.ExpectedErr, errCode(err), "Wrong error code") { - return - } else if tc.ExpectedErr != "" { - // If an error is expected and the err Diff check has passed then don't continue on to test the result - return - } - - t.Log(string(actualBase.base)) - expectedBase, err := os.ReadFile(fmt.Sprintf("testdata/%s", tc.ExpectedFile)) - if err != nil { - t.Fatal(err) - } - if sigtest.Diff(t, string(expectedBase), string(actualBase.base), "Signature base did not match") { - t.FailNow() - } -} - -type errorMetadataProvider struct{} - -func (fmp errorMetadataProvider) Created() (int, error) { - return 0, fmt.Errorf("No created value") -} - -func (fmp errorMetadataProvider) Expires() (int, error) { - return 0, fmt.Errorf("No expires value") -} - -func (fmp errorMetadataProvider) Nonce() (string, error) { - return "", fmt.Errorf("No nonce value") -} - -func (fmp errorMetadataProvider) Alg() (string, error) { - return "", fmt.Errorf("No alg value") -} - -func (fmp errorMetadataProvider) KeyID() (string, error) { - return "", fmt.Errorf("No keyid value") -} - -func (fmp errorMetadataProvider) Tag() (string, error) { - return "", fmt.Errorf("No tag value") -} - -var emptyMeta = fixedMetadataProvider{ - values: map[Metadata]any{}, -} - -type fixedMetadataProvider struct { - values map[Metadata]any -} - -func (fmp fixedMetadataProvider) Created() (int, error) { - if val, ok := fmp.values[MetaCreated]; ok { - return int(val.(int64)), nil - } - return 0, fmt.Errorf("No created value") -} - -func (fmp fixedMetadataProvider) Expires() (int, error) { - if val, ok := fmp.values[MetaExpires]; ok { - return int(val.(int64)), nil - } - return 0, fmt.Errorf("No expires value") -} - -func (fmp fixedMetadataProvider) Nonce() (string, error) { - if val, ok := fmp.values[MetaNonce]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No nonce value") -} - -func (fmp fixedMetadataProvider) Alg() (string, error) { - if val, ok := fmp.values[MetaAlgorithm]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No alg value") -} - -func (fmp fixedMetadataProvider) KeyID() (string, error) { - if val, ok := fmp.values[MetaKeyID]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No keyid value") -} - -func (fmp fixedMetadataProvider) Tag() (string, error) { - if val, ok := fmp.values[MetaTag]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No tag value") -} - -func makeComponents(ids ...string) []componentID { - cids := []componentID{} - for _, id := range ids { - cids = append(cids, SignedField{ - Name: id, - }.componentID()) - } - return cids -} -func makeComponentIDs(sfs ...SignedField) []componentID { - - cids := []componentID{} - for _, sf := range sfs { - cids = append(cids, sf.componentID()) - } - return cids -} diff --git a/third_party/httpsig-go/signatures.go b/third_party/httpsig-go/signatures.go deleted file mode 100644 index 940f4f0eae..0000000000 --- a/third_party/httpsig-go/signatures.go +++ /dev/null @@ -1,172 +0,0 @@ -package httpsig - -import ( - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/hmac" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/sha512" - "fmt" - "time" - - sfv "github.com/dunglas/httpsfv" -) - -// derived component names -type derived string - -const ( - sigparams derived = "@signature-params" - method derived = "@method" - path derived = "@path" - targetURI derived = "@target-uri" - authority derived = "@authority" -) - -// MetadataProvider allows customized functions for metadata parameter values. Not needed for default usage. -type MetadataProvider interface { - Created() (int, error) - Expires() (int, error) - Nonce() (string, error) - Alg() (string, error) - KeyID() (string, error) - Tag() (string, error) -} - -type signatureBase struct { - base []byte // The full signature base. Use this as input to signing and verification - signatureInput string // The signature-input line -} - -type sigParameters struct { - Base sigBaseInput - Algo Algorithm - Label string - PrivateKey crypto.PrivateKey - Secret []byte -} - -func sign(hrr httpMessage, sp sigParameters) error { - base, err := calculateSignatureBase(hrr, sp.Base) - if err != nil { - return err - } - - var sigBytes []byte - switch sp.Algo { - - case Algo_RSA_PSS_SHA512: - msgHash := sha512.Sum512(base.base) - opts := &rsa.PSSOptions{ - SaltLength: 64, - Hash: crypto.SHA512, - } - switch rsapk := sp.PrivateKey.(type) { - case *rsa.PrivateKey: - sigBytes, err = rsa.SignPSS(rand.Reader, rsapk, crypto.SHA512, msgHash[:], opts) - if err != nil { - return err - } - case crypto.Signer: - sigBytes, err = rsapk.Sign(rand.Reader, msgHash[:], crypto.SHA512) - if err != nil { - return err - } - default: - return fmt.Errorf("Invalid private key. Requires *rsa.PrivateKey or crypto.Signer: %T", sp.PrivateKey) - } - case Algo_RSA_v1_5_sha256: - msgHash := sha256.Sum256(base.base) - switch rsapk := sp.PrivateKey.(type) { - case *rsa.PrivateKey: - sigBytes, err = rsa.SignPKCS1v15(rand.Reader, rsapk, crypto.SHA256, msgHash[:]) - if err != nil { - return err - } - case crypto.Signer: - sigBytes, err = rsapk.Sign(rand.Reader, msgHash[:], crypto.SHA256) - if err != nil { - return err - } - default: - return fmt.Errorf("Invalid private key. Requires *rsa.PrivateKey or crypto.Signer: %T", sp.PrivateKey) - } - case Algo_ECDSA_P256_SHA256: - msgHash := sha256.Sum256(base.base) - switch eccpk := sp.PrivateKey.(type) { - case *ecdsa.PrivateKey: - r, s, err := ecdsa.Sign(rand.Reader, eccpk, msgHash[:]) - if err != nil { - return newError(ErrInternal, "Failed to sign with ecdsa private key", err) - } - // Concatenate r and s to make the signature as per the spec. r and s are *not* encoded in ASN1 format - sigBytes = make([]byte, 64) - r.FillBytes(sigBytes[0:32]) - s.FillBytes(sigBytes[32:64]) - case crypto.Signer: - sigBytes, err = eccpk.Sign(rand.Reader, msgHash[:], crypto.SHA256) - if err != nil { - return newError(ErrInternal, "Failed to sign with ecdsa custom signer", err) - } - default: - return fmt.Errorf("Invalid private key. Requires *ecdsa.PrivateKey or crypto.Signer: %T", sp.PrivateKey) - } - case Algo_ECDSA_P384_SHA384: - msgHash := sha512.Sum384(base.base) - switch eccpk := sp.PrivateKey.(type) { - case *ecdsa.PrivateKey: - r, s, err := ecdsa.Sign(rand.Reader, eccpk, msgHash[:]) - if err != nil { - return newError(ErrInternal, "Failed to sign with ecdsa private key", err) - } - // Concatenate r and s to make the signature as per the spec. r and s are *not* encoded in ASN1 format - sigBytes = make([]byte, 96) - r.FillBytes(sigBytes[0:48]) - s.FillBytes(sigBytes[48:96]) - case crypto.Signer: - sigBytes, err = eccpk.Sign(rand.Reader, msgHash[:], crypto.SHA384) - if err != nil { - return newError(ErrInternal, "Failed to sign with ecdsa custom signer", err) - } - default: - return fmt.Errorf("Invalid private key. Requires *ecdsa.PrivateKey or crypto.Signer: %T", sp.PrivateKey) - } - case Algo_ED25519: - switch edpk := sp.PrivateKey.(type) { - case ed25519.PrivateKey: - sigBytes = ed25519.Sign(edpk, base.base) - case crypto.Signer: - sigBytes, err = edpk.Sign(rand.Reader, base.base, crypto.Hash(0)) - if err != nil { - return newError(ErrInternal, "Failed to sign with ed25519 custom signer", err) - } - default: - return fmt.Errorf("Invalid private key. Requires ed25519.PrivateKey or crypto.Signer: %T", sp.PrivateKey) - } - case Algo_HMAC_SHA256: - if len(sp.Secret) == 0 { - return newError(ErrInvalidSignatureOptions, fmt.Sprintf("No secret provided for symmetric algorithm '%s'", Algo_HMAC_SHA256)) - } - msgHash := hmac.New(sha256.New, sp.Secret) - msgHash.Write(base.base) // write does not return an error per hash.Hash documentation - sigBytes = msgHash.Sum(nil) - default: - return newError(ErrInvalidSignatureOptions, fmt.Sprintf("Signing algorithm not supported: '%s'", sp.Algo)) - } - sigField := sfv.NewDictionary() - sigField.Add(sp.Label, sfv.NewItem(sigBytes)) - signature, err := sfv.Marshal(sigField) - if err != nil { - return newError(ErrInternal, fmt.Sprintf("Failed to marshal signature for label '%s'", sp.Label), err) - } - hrr.Headers().Set("Signature-Input", fmt.Sprintf("%s=%s", sp.Label, base.signatureInput)) - hrr.Headers().Set("Signature", signature) - return nil -} - -func timestamp(nowtime func() time.Time) int { - return int(nowtime().Unix()) -} diff --git a/third_party/httpsig-go/sigtest/helpers.go b/third_party/httpsig-go/sigtest/helpers.go deleted file mode 100644 index 1beda295da..0000000000 --- a/third_party/httpsig-go/sigtest/helpers.go +++ /dev/null @@ -1,85 +0,0 @@ -package sigtest - -import ( - "bufio" - "bytes" - "crypto" - "encoding/base64" - "fmt" - "io" - "net/http" - "os" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/remitly-oss/httpsig-go/keyutil" -) - -func ReadRequest(t testing.TB, reqFile string) *http.Request { - reqtxt, err := os.Open(fmt.Sprintf("testdata/%s", reqFile)) - if err != nil { - t.Fatal(err) - } - - req, err := http.ReadRequest(bufio.NewReader(reqtxt)) - if err != nil { - t.Fatal(err) - } - return req -} - -func MustReadFile(file string) []byte { - data, err := os.ReadFile(file) - if err != nil { - panic(err) - } - return data -} - -func MakeBody(body string) io.ReadCloser { - return io.NopCloser(bytes.NewReader([]byte(body))) -} - -func ReadSharedSecret(t *testing.T, sharedSecretFile string) []byte { - secretBytes, err := os.ReadFile(fmt.Sprintf("testdata/%s", sharedSecretFile)) - if err != nil { - t.Fatal(err) - } - secret, err := base64.StdEncoding.DecodeString(string(secretBytes)) - if err != nil { - t.Fatal(err) - } - return secret -} - -func ReadTestPubkey(t *testing.T, pubkeyFile string) crypto.PublicKey { - keybytes, err := os.ReadFile(fmt.Sprintf("testdata/%s", pubkeyFile)) - if err != nil { - t.Fatal(err) - } - pubkey, err := keyutil.ReadPublicKey(keybytes) - if err != nil { - t.Fatal(err) - } - return pubkey -} - -func ReadTestPrivateKey(t testing.TB, pkFile string, hint ...string) crypto.PrivateKey { - keybytes, err := os.ReadFile(fmt.Sprintf("testdata/%s", pkFile)) - if err != nil { - t.Fatal(err) - } - pkey, err := keyutil.ReadPrivateKey(keybytes) - if err != nil { - t.Fatal(err) - } - return pkey -} - -func Diff(t *testing.T, expected, actual any, msg string, opts ...cmp.Option) bool { - if diff := cmp.Diff(expected, actual, opts...); diff != "" { - t.Errorf("%s (-want +got):\n%s", msg, diff) - return true - } - return false -} diff --git a/third_party/httpsig-go/spec_test.go b/third_party/httpsig-go/spec_test.go deleted file mode 100644 index 788d4d6e72..0000000000 --- a/third_party/httpsig-go/spec_test.go +++ /dev/null @@ -1,385 +0,0 @@ -package httpsig - -import ( - "bufio" - "context" - "fmt" - "net/http" - "os" - "testing" - - "github.com/remitly-oss/httpsig-go/sigtest" -) - -/* - Test cases from https://www.rfc-editor.org/rfc/rfc9421.pdf -*/ - -// TestSpecVerify ensures that requests from spec can be verified -func TestSpecVerify(t *testing.T) { - cases := []struct { - Name string - IsResponse bool - Key KeySpec - SignedRequestOrResonseFile string - Skip bool - }{ - { - Name: "b21", - Key: KeySpec{ - KeyID: "test-key-rsa-pss", - Algo: Algo_RSA_PSS_SHA512, - PubKey: sigtest.ReadTestPubkey(t, "test-key-rsa-pss.pub"), - }, - SignedRequestOrResonseFile: "b21_request_signed.txt", - }, - { - Name: "b22", - Key: KeySpec{ - KeyID: "test-key-rsa-pss", - Algo: Algo_RSA_PSS_SHA512, - PubKey: sigtest.ReadTestPubkey(t, "test-key-rsa-pss.pub"), - }, - SignedRequestOrResonseFile: "b22_request_signed.txt", - }, - { - Name: "b23", - Key: KeySpec{ - KeyID: "test-key-rsa-pss", - Algo: Algo_RSA_PSS_SHA512, - PubKey: sigtest.ReadTestPubkey(t, "test-key-rsa-pss.pub"), - }, - SignedRequestOrResonseFile: "b23_request_signed.txt", - }, - { - Name: "b24", - IsResponse: true, - Key: KeySpec{ - KeyID: "test-key-ecc-p256", - Algo: Algo_ECDSA_P256_SHA256, - PubKey: sigtest.ReadTestPubkey(t, "test-key-ecc-p256.pub"), - }, - SignedRequestOrResonseFile: "b24_response_signed.txt", - }, - { - Name: "b25", - Key: KeySpec{ - KeyID: "test-shared-secret", - Algo: Algo_HMAC_SHA256, - Secret: sigtest.ReadSharedSecret(t, "test-shared-secret"), - }, - SignedRequestOrResonseFile: "b25_request_signed.txt", - }, - { - Name: "b26", - Key: KeySpec{ - KeyID: "test-key-ed25519", - Algo: Algo_ED25519, - PubKey: sigtest.ReadTestPubkey(t, "test-key-ed25519.pub"), - }, - SignedRequestOrResonseFile: "b26_request_signed.txt", - }, - } - - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - if tc.Skip { - t.Skip(fmt.Sprintf("Skipping test %s", tc.Name)) - } - - hrrtxt, err := os.Open(fmt.Sprintf("testdata/%s", tc.SignedRequestOrResonseFile)) - if err != nil { - t.Fatal(err) - } - - ver, err := NewVerifier(&fixedKeyFetch{ - requiredKeyID: tc.Key.KeyID, - key: tc.Key, - }, VerifyProfile{ - SignatureLabel: fmt.Sprintf("sig-%s", tc.Name), - }) - - var verifyErr error - if tc.IsResponse { - resp, err := http.ReadResponse(bufio.NewReader(hrrtxt), nil) - if err != nil { - t.Fatal(err) - } - _, verifyErr = ver.VerifyResponse(resp) - } else { - req, err := http.ReadRequest(bufio.NewReader(hrrtxt)) - if err != nil { - t.Fatal(err) - } - _, verifyErr = ver.Verify(req) - } - - if verifyErr != nil { - t.Fatalf("%#v\n", verifyErr) - } - }) - } -} - -// TestSpecBase test recreation of the signature bases from the spec -func TestSpecBase(t *testing.T) { - cases := []testcaseSigBase{ - { - Name: "b21", - Params: sigBaseInput{ - Components: []componentID{}, - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - MetaNonce, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-key-rsa-pss", - MetaNonce: "b3k2pp5k7z-50gnwp.yemd", - }, - }, - }, - ExpectedFile: "b21_request_sigbase.txt", - }, - { - Name: "b22", - Params: sigBaseInput{ - Components: makeComponentIDs( - SignedField{ - Name: "@authority", - }, - SignedField{ - Name: "content-digest", - }, - SignedField{ - Name: "@query-param", - Parameters: map[string]any{ - "name": "Pet", - }, - }, - ), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - MetaTag, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-key-rsa-pss", - MetaTag: "header-example", - }, - }, - }, - ExpectedFile: "b22_request_sigbase.txt", - }, - { - Name: "b23", - Params: sigBaseInput{ - Components: makeComponentIDs( - SignedField{ - Name: "date", - }, - SignedField{ - Name: "@method", - }, - SignedField{ - Name: "@path", - }, - SignedField{ - Name: "@query", - }, - SignedField{ - Name: "@authority", - }, - SignedField{ - Name: "content-type", - }, - SignedField{ - Name: "content-digest", - }, - SignedField{ - Name: "content-length", - }, - ), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-key-rsa-pss", - }, - }, - }, - ExpectedFile: "b23_request_sigbase.txt", - }, - { - Name: "b24", - IsResponse: true, - Params: sigBaseInput{ - Components: componentsIDs(Fields("@status", "content-type", "content-digest", "content-length")), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-key-ecc-p256", - }, - }, - }, - ExpectedFile: "b24_response_sigbase.txt", - }, - { - Name: "b25", - Params: sigBaseInput{ - Components: componentsIDs(Fields("date", "@authority", "content-type")), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-shared-secret", - }, - }, - }, - ExpectedFile: "b25_request_sigbase.txt", - }, - { - Name: "b26", - Params: sigBaseInput{ - Components: componentsIDs(Fields("date", "@method", "@path", "@authority", "content-type", "content-length")), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-key-ed25519", - }, - }, - }, - ExpectedFile: "b26_request_sigbase.txt", - }, - } - - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - runTestSigBase(t, tc) - }) - } -} - -type fixedKeyFetch struct { - requiredKeyID string // if not empty, Fetch will check if the input keyID matches - key KeySpec -} - -func (kf fixedKeyFetch) FetchByKeyID(ctx context.Context, rh http.Header, keyID string) (KeySpecer, error) { - if kf.requiredKeyID != "" && keyID != kf.requiredKeyID { - return nil, &KeyError{ - error: fmt.Errorf("Invalid key id. Wanted '%s' got '%s'", kf.requiredKeyID, keyID), - } - } - return kf.key, nil -} - -func (kf fixedKeyFetch) Fetch(ctx context.Context, rh http.Header, md MetadataProvider) (KeySpecer, error) { - return nil, fmt.Errorf("Fetch without a key id not supported.") -} - -// TestSpecRecreateSignature recreates the signature in the test cases. -// Algorithms that include randomness in the signing (each signature is unique) cannot be tested in this way. -func TestSpecRecreateSignature(t *testing.T) { - cases := []struct { - Name string - Params sigParameters - ExpectedFile string - }{ - { - Name: "b25", - Params: sigParameters{ - Base: sigBaseInput{ - Components: componentsIDs(Fields("date", "@authority", "content-type")), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-shared-secret", - }, - }, - }, - Algo: Algo_HMAC_SHA256, - Label: "sig-b25", - Secret: sigtest.ReadSharedSecret(t, "test-shared-secret"), - }, - - ExpectedFile: "b25_request_signed.txt", - }, - { - Name: "b26", - Params: sigParameters{ - Base: sigBaseInput{ - Components: componentsIDs(Fields("date", "@method", "@path", "@authority", "content-type", "content-length")), - MetadataParams: []Metadata{ - MetaCreated, - MetaKeyID, - }, - MetadataValues: fixedMetadataProvider{ - values: map[Metadata]any{ - MetaCreated: int64(1618884473), - MetaKeyID: "test-key-ed25519", - }, - }, - }, - - Algo: Algo_ED25519, - Label: "sig-b26", - PrivateKey: sigtest.ReadTestPrivateKey(t, "test-key-ed25519.key"), - }, - ExpectedFile: "b26_request_signed.txt", - }, - } - - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - reqtxt, err := os.Open("testdata/rfc-test-request.txt") - if err != nil { - t.Fatal(err) - } - - req, err := http.ReadRequest(bufio.NewReader(reqtxt)) - if err != nil { - t.Fatal(err) - } - - err = sign(httpMessage{ - Req: req, - }, tc.Params) - if err != nil { - t.Fatalf("%#v\n", err) - } - expectedtxt, err := os.Open(fmt.Sprintf("testdata/%s", tc.ExpectedFile)) - if err != nil { - t.Fatal(err) - } - - expected, err := http.ReadRequest(bufio.NewReader(expectedtxt)) - if err != nil { - t.Fatal(err) - } - sigtest.Diff(t, expected.Header, req.Header, "") - }) - } -} diff --git a/third_party/httpsig-go/testdata/b21_request_sigbase.txt b/third_party/httpsig-go/testdata/b21_request_sigbase.txt deleted file mode 100644 index 4e60f3ed74..0000000000 --- a/third_party/httpsig-go/testdata/b21_request_sigbase.txt +++ /dev/null @@ -1 +0,0 @@ -"@signature-params": ();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd" \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/b21_request_signed.txt b/third_party/httpsig-go/testdata/b21_request_signed.txt deleted file mode 100644 index ccc1bdb590..0000000000 --- a/third_party/httpsig-go/testdata/b21_request_signed.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b21=();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd" -Signature: sig-b21=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopemLJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3+7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==: - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/b22_request_sigbase.txt b/third_party/httpsig-go/testdata/b22_request_sigbase.txt deleted file mode 100644 index b4f1935d55..0000000000 --- a/third_party/httpsig-go/testdata/b22_request_sigbase.txt +++ /dev/null @@ -1,4 +0,0 @@ -"@authority": example.com -"content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -"@query-param";name="Pet": dog -"@signature-params": ("@authority" "content-digest" "@query-param";name="Pet");created=1618884473;keyid="test-key-rsa-pss";tag="header-example" \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/b22_request_signed.txt b/third_party/httpsig-go/testdata/b22_request_signed.txt deleted file mode 100644 index 8ddd3c54b8..0000000000 --- a/third_party/httpsig-go/testdata/b22_request_signed.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b22=("@authority" "content-digest" "@query-param";name="Pet");created=1618884473;keyid="test-key-rsa-pss";tag="header-example" -Signature: sig-b22=:LjbtqUbfmvjj5C5kr1Ugj4PmLYvx9wVjZvD9GsTT4F7GrcQEdJzgI9qHxICagShLRiLMlAJjtq6N4CDfKtjvuJyE5qH7KT8UCMkSowOB4+ECxCmT8rtAmj/0PIXxi0A0nxKyB09RNrCQibbUjsLS/2YyFYXEu4TRJQzRw1rLEuEfY17SARYhpTlaqwZVtR8NV7+4UKkjqpcAoFqWFQh62s7Cl+H2fjBSpqfZUJcsIk4N6wiKYd4je2U/lankenQ99PZfB4jY3I5rSV2DSBVkSFsURIjYErOs0tFTQosMTAoxk//0RoKUqiYY8Bh0aaUEb0rQl3/XaVe4bXTugEjHSw==: - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/b23_request_sigbase.txt b/third_party/httpsig-go/testdata/b23_request_sigbase.txt deleted file mode 100644 index fa2f73beca..0000000000 --- a/third_party/httpsig-go/testdata/b23_request_sigbase.txt +++ /dev/null @@ -1,9 +0,0 @@ -"date": Tue, 20 Apr 2021 02:07:55 GMT -"@method": POST -"@path": /foo -"@query": ?param=Value&Pet=dog -"@authority": example.com -"content-type": application/json -"content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -"content-length": 18 -"@signature-params": ("date" "@method" "@path" "@query" "@authority" "content-type" "content-digest" "content-length");created=1618884473;keyid="test-key-rsa-pss" \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/b23_request_signed.txt b/third_party/httpsig-go/testdata/b23_request_signed.txt deleted file mode 100644 index ee53a21fdd..0000000000 --- a/third_party/httpsig-go/testdata/b23_request_signed.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b23=("date" "@method" "@path" "@query" "@authority" "content-type" "content-digest" "content-length");created=1618884473;keyid="test-key-rsa-pss" -Signature: sig-b23=:bbN8oArOxYoyylQQUU6QYwrTuaxLwjAC9fbY2F6SVWvh0yBiMIRGOnMYwZ/5MR6fb0Kh1rIRASVxFkeGt683+qRpRRU5p2voTp768ZrCUb38K0fUxN0O0iC59DzYx8DFll5GmydPxSmme9v6ULbMFkl+V5B1TP/yPViV7KsLNmvKiLJH1pFkh/aYA2HXXZzNBXmIkoQoLd7YfW91kE9o/CCoC1xMy7JA1ipwvKvfrs65ldmlu9bpG6A9BmzhuzF8Eim5f8ui9eH8LZH896+QIF61ka39VBrohr9iyMUJpvRX2Zbhl5ZJzSRxpJyoEZAFL2FUo5fTIztsDZKEgM4cUA==: - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/b24_response_sigbase.txt b/third_party/httpsig-go/testdata/b24_response_sigbase.txt deleted file mode 100644 index 172719d614..0000000000 --- a/third_party/httpsig-go/testdata/b24_response_sigbase.txt +++ /dev/null @@ -1,5 +0,0 @@ -"@status": 200 -"content-type": application/json -"content-digest": sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ41QJgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==: -"content-length": 23 -"@signature-params": ("@status" "content-type" "content-digest" "content-length");created=1618884473;keyid="test-key-ecc-p256" \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/b24_response_signed.txt b/third_party/httpsig-go/testdata/b24_response_signed.txt deleted file mode 100644 index ce04573499..0000000000 --- a/third_party/httpsig-go/testdata/b24_response_signed.txt +++ /dev/null @@ -1,9 +0,0 @@ -HTTP/1.1 200 OK -Date: Tue, 20 Apr 2021 02:07:56 GMT -Content-Type: application/json -Content-Digest: sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ41QJgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==: -Content-Length: 23 -Signature-Input: sig-b24=("@status" "content-type" "content-digest" "content-length");created=1618884473;keyid="test-key-ecc-p256" -Signature: sig-b24=:wNmSUAhwb5LxtOtOpNa6W5xj067m5hFrj0XQ4fvpaCLx0NKocgPquLgyahnzDnDAUy5eCdlYUEkLIj+32oiasw==: - -{"message": "good dog"} diff --git a/third_party/httpsig-go/testdata/b25_request_sigbase.txt b/third_party/httpsig-go/testdata/b25_request_sigbase.txt deleted file mode 100644 index ed7f8eb88b..0000000000 --- a/third_party/httpsig-go/testdata/b25_request_sigbase.txt +++ /dev/null @@ -1,4 +0,0 @@ -"date": Tue, 20 Apr 2021 02:07:55 GMT -"@authority": example.com -"content-type": application/json -"@signature-params": ("date" "@authority" "content-type");created=1618884473;keyid="test-shared-secret" \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/b25_request_signed.txt b/third_party/httpsig-go/testdata/b25_request_signed.txt deleted file mode 100644 index 680d75578e..0000000000 --- a/third_party/httpsig-go/testdata/b25_request_signed.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b25=("date" "@authority" "content-type");created=1618884473;keyid="test-shared-secret" -Signature: sig-b25=:pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=: - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/b26_request_sigbase.txt b/third_party/httpsig-go/testdata/b26_request_sigbase.txt deleted file mode 100644 index 7a727fc446..0000000000 --- a/third_party/httpsig-go/testdata/b26_request_sigbase.txt +++ /dev/null @@ -1,7 +0,0 @@ -"date": Tue, 20 Apr 2021 02:07:55 GMT -"@method": POST -"@path": /foo -"@authority": example.com -"content-type": application/json -"content-length": 18 -"@signature-params": ("date" "@method" "@path" "@authority" "content-type" "content-length");created=1618884473;keyid="test-key-ed25519" \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/b26_request_signed.txt b/third_party/httpsig-go/testdata/b26_request_signed.txt deleted file mode 100644 index 7ff0f2ae65..0000000000 --- a/third_party/httpsig-go/testdata/b26_request_signed.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b26=("date" "@method" "@path" "@authority" "content-type" "content-length");created=1618884473;keyid="test-key-ed25519" -Signature: sig-b26=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==: - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzExtractSignatures/00e15d22123489fd b/third_party/httpsig-go/testdata/fuzz/FuzzExtractSignatures/00e15d22123489fd deleted file mode 100644 index b0e98fbe10..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzExtractSignatures/00e15d22123489fd +++ /dev/null @@ -1,3 +0,0 @@ -go test fuzz v1 -string("0") -string("0") diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzExtractSignatures/85649d45641911d0 b/third_party/httpsig-go/testdata/fuzz/FuzzExtractSignatures/85649d45641911d0 deleted file mode 100644 index befd7df0d5..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzExtractSignatures/85649d45641911d0 +++ /dev/null @@ -1,3 +0,0 @@ -go test fuzz v1 -string("0") -string("") diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzSign/01c151dfb31ce1b1 b/third_party/httpsig-go/testdata/fuzz/FuzzSign/01c151dfb31ce1b1 deleted file mode 100644 index 1c15e48278..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzSign/01c151dfb31ce1b1 +++ /dev/null @@ -1,4 +0,0 @@ -go test fuzz v1 -string("0") -string("\x0f") -string("\x7f") diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzSign/2da5aa149625a656 b/third_party/httpsig-go/testdata/fuzz/FuzzSign/2da5aa149625a656 deleted file mode 100644 index 87dae56e3f..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzSign/2da5aa149625a656 +++ /dev/null @@ -1,4 +0,0 @@ -go test fuzz v1 -string("0") -string("0") -string("\x7f") diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzSign/58358ab8a686cc26 b/third_party/httpsig-go/testdata/fuzz/FuzzSign/58358ab8a686cc26 deleted file mode 100644 index 504227963c..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzSign/58358ab8a686cc26 +++ /dev/null @@ -1,4 +0,0 @@ -go test fuzz v1 -string("\n") -string("0") -string("0") diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzSign/ecf6af09b9da5a62 b/third_party/httpsig-go/testdata/fuzz/FuzzSign/ecf6af09b9da5a62 deleted file mode 100644 index be810eb00a..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzSign/ecf6af09b9da5a62 +++ /dev/null @@ -1,4 +0,0 @@ -go test fuzz v1 -string("") -string("0") -string("@") diff --git a/third_party/httpsig-go/testdata/fuzz/FuzzSign/ff08bb6e0efa59be b/third_party/httpsig-go/testdata/fuzz/FuzzSign/ff08bb6e0efa59be deleted file mode 100644 index cc15c00ba9..0000000000 --- a/third_party/httpsig-go/testdata/fuzz/FuzzSign/ff08bb6e0efa59be +++ /dev/null @@ -1,4 +0,0 @@ -go test fuzz v1 -string("") -string("0") -string("\xde") diff --git a/third_party/httpsig-go/testdata/key-rsa-v15.key b/third_party/httpsig-go/testdata/key-rsa-v15.key deleted file mode 100644 index 20ca7b388e..0000000000 --- a/third_party/httpsig-go/testdata/key-rsa-v15.key +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAK+BnK7LFtj6kjM1 -bL7HtCQQpCXj1mZtCTATz4HZS9QD5eGiu7RWdSW+dd0poGmfOE3GS2fofAt3oJip -mV8tNW9uB4NYTwq5fFb0MmL34BDQBQo6obCOAkx6UG0h8y91Y+Dp9lcKwN++O8mR -w6bHiVyfATJm3hgSHnmgiKyL0yJFAgMBAAECgYAj2Dvw8yeabyq27L1mBZGEICX2 -Wx8p0jEjMZia849qINWtjLf7cAEDEXAvGFZb3Bn6wHocIb5b9TXGmDTr3GbiIWhH -glwfz/aFths6xTquHcLZJ1TEGkZqRYcKvxSN3PBUYdpq40D5hPKKjerpsvN5YPk2 -b/P4V+jrCNr6futBoQJBAOS3NJuxur5vHRVdUJ3T1Me6Jv+Rs6mtK8DdJ02ARzvo -fBntb6BYEjEoSanqdFkeGHer5YyHLXqquGWA86Ghsy0CQQDEcW9pIxNZlaNbV8yq -2JaHFCalYoH8+dRbGKYqJW3izL/Kffp9jpmbTf8B7VzugHOO43lk12X+f1idxuD8 -Snp5AkBCkbxjMKi88tRROpbTSSuecmUVb9AOK9QXT4c3/IU/P5yXY09hKSEqY6KF -LTNuGN9gPY0TiOjI0lXXXWAMBGeFAkEAiQTWQO9GP+Yv2zaSe1g3JmDX0+Ox51Ia -3K+Et0EENH28CPF2Fr2wRrNQe3ekqnbOI4xmz/+uFKWeme5uX4tTgQJBAMJZY53Q -kIO53yMCvxth9HY6yAXMhbDTVyW7CW6yeyscM43HtQdlNtWPYwn4XuEzxyXHJBYb -l0097RDjZs8KCHw= ------END PRIVATE KEY----- diff --git a/third_party/httpsig-go/testdata/key-rsa-v15.pub b/third_party/httpsig-go/testdata/key-rsa-v15.pub deleted file mode 100644 index c80080ea44..0000000000 --- a/third_party/httpsig-go/testdata/key-rsa-v15.pub +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvgZyuyxbY+pIzNWy+x7QkEKQl -49ZmbQkwE8+B2UvUA+Xhoru0VnUlvnXdKaBpnzhNxktn6HwLd6CYqZlfLTVvbgeD -WE8KuXxW9DJi9+AQ0AUKOqGwjgJMelBtIfMvdWPg6fZXCsDfvjvJkcOmx4lcnwEy -Zt4YEh55oIisi9MiRQIDAQAB ------END PUBLIC KEY----- diff --git a/third_party/httpsig-go/testdata/request1.txt b/third_party/httpsig-go/testdata/request1.txt deleted file mode 100644 index d7db85f41a..0000000000 --- a/third_party/httpsig-go/testdata/request1.txt +++ /dev/null @@ -1,8 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/request_bad_digest.txt b/third_party/httpsig-go/testdata/request_bad_digest.txt deleted file mode 100644 index bbf1d7bf57..0000000000 --- a/third_party/httpsig-go/testdata/request_bad_digest.txt +++ /dev/null @@ -1,8 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 - -{"hello": "bad digest world"} diff --git a/third_party/httpsig-go/testdata/request_multivalue.txt b/third_party/httpsig-go/testdata/request_multivalue.txt deleted file mode 100644 index 2312380851..0000000000 --- a/third_party/httpsig-go/testdata/request_multivalue.txt +++ /dev/null @@ -1,9 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -One: multivalue1 -One: multivalue2 -Content-Type: application/json -Content-Length: 18 - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/request_no_digest.txt b/third_party/httpsig-go/testdata/request_no_digest.txt deleted file mode 100644 index 9150dfe018..0000000000 --- a/third_party/httpsig-go/testdata/request_no_digest.txt +++ /dev/null @@ -1,7 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Length: 18 - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/request_repeated_components.txt b/third_party/httpsig-go/testdata/request_repeated_components.txt deleted file mode 100644 index 6071d40d6a..0000000000 --- a/third_party/httpsig-go/testdata/request_repeated_components.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -One: value1 -Two: value2 -Three: value2 -Content-Type: application/json -Content-Length: 18 - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/rfc-test-request.txt b/third_party/httpsig-go/testdata/rfc-test-request.txt deleted file mode 100644 index d7db85f41a..0000000000 --- a/third_party/httpsig-go/testdata/rfc-test-request.txt +++ /dev/null @@ -1,8 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/rfc-test-response.txt b/third_party/httpsig-go/testdata/rfc-test-response.txt deleted file mode 100644 index d118949ef2..0000000000 --- a/third_party/httpsig-go/testdata/rfc-test-response.txt +++ /dev/null @@ -1,7 +0,0 @@ -HTTP/1.1 200 OK -Date: Tue, 20 Apr 2021 02:07:56 GMT -Content-Type: application/json -Content-Digest: sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ41QJgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==: -Content-Length: 23 - -{"message": "good dog"} diff --git a/third_party/httpsig-go/testdata/test-key-ecc-p256.key b/third_party/httpsig-go/testdata/test-key-ecc-p256.key deleted file mode 100644 index 7a74374f1a..0000000000 --- a/third_party/httpsig-go/testdata/test-key-ecc-p256.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIFKbhfNZfpDsW43+0+JjUr9K+bTeuxopu653+hBaXGA7oAoGCCqGSM49 -AwEHoUQDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lfw0EkjqF7xB4FivAxzic30tMM -4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ== ------END EC PRIVATE KEY----- \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/test-key-ecc-p256.pub b/third_party/httpsig-go/testdata/test-key-ecc-p256.pub deleted file mode 100644 index d72cdc86b7..0000000000 --- a/third_party/httpsig-go/testdata/test-key-ecc-p256.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lf -w0EkjqF7xB4FivAxzic30tMM4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ== ------END PUBLIC KEY----- diff --git a/third_party/httpsig-go/testdata/test-key-ecc-p384.key b/third_party/httpsig-go/testdata/test-key-ecc-p384.key deleted file mode 100644 index 0057681102..0000000000 --- a/third_party/httpsig-go/testdata/test-key-ecc-p384.key +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MIGkAgEBBDBlRoIHTHCg2hgrwD5cQMl1aGP8ZZ/ulSbHCDHHIC4ENlQIdjLDhOBG -JO2ZMKJK33WgBwYFK4EEACKhZANiAAS46EhdhNcOPg3ZKMhlryAFfy6eKt3M3f+w -31ikDCbu10GLZjvCoevMvSY+TQIL9EQlAHRxcJ5ciXt1ukiH2zo1NpR7S8ozmvCV -t/IvcUiS9XS1nRiHKkM1e8NMNtg3avo= ------END EC PRIVATE KEY----- diff --git a/third_party/httpsig-go/testdata/test-key-ecc-p384.pub b/third_party/httpsig-go/testdata/test-key-ecc-p384.pub deleted file mode 100644 index 56ce078f98..0000000000 --- a/third_party/httpsig-go/testdata/test-key-ecc-p384.pub +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PUBLIC KEY----- -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEuOhIXYTXDj4N2SjIZa8gBX8unirdzN3/ -sN9YpAwm7tdBi2Y7wqHrzL0mPk0CC/REJQB0cXCeXIl7dbpIh9s6NTaUe0vKM5rw -lbfyL3FIkvV0tZ0YhypDNXvDTDbYN2r6 ------END PUBLIC KEY----- diff --git a/third_party/httpsig-go/testdata/test-key-ed25519.key b/third_party/httpsig-go/testdata/test-key-ed25519.key deleted file mode 100644 index 06abe8cf0f..0000000000 --- a/third_party/httpsig-go/testdata/test-key-ed25519.key +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF ------END PRIVATE KEY----- \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/test-key-ed25519.pub b/third_party/httpsig-go/testdata/test-key-ed25519.pub deleted file mode 100644 index 03c11eaf0c..0000000000 --- a/third_party/httpsig-go/testdata/test-key-ed25519.pub +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN PUBLIC KEY----- -MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs= ------END PUBLIC KEY----- \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/test-key-rsa-pss.key b/third_party/httpsig-go/testdata/test-key-rsa-pss.key deleted file mode 100644 index 09b0898251..0000000000 --- a/third_party/httpsig-go/testdata/test-key-rsa-pss.key +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADALBgkqhkiG9w0BAQoEggSqMIIEpgIBAAKCAQEAr4tmm3r20Wd/Pbqv P1s2+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry5 3mm+oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7Oyr FAHqgDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUA AN5WUtzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw 9lq4aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oy c6XI2wIDAQABAoIBAQCUB8ip+kJiiZVKF8AqfB/aUP0jTAqOQewK1kKJ/iQCXBCq pbo360gvdt05H5VZ/RDVkEgO2k73VSsbulqezKs8RFs2tEmU+JgTI9MeQJPWcP6X aKy6LIYs0E2cWgp8GADgoBs8llBq0UhX0KffglIeek3n7Z6Gt4YFge2TAcW2WbN4 XfK7lupFyo6HHyWRiYHMMARQXLJeOSdTn5aMBP0PO4bQyk5ORxTUSeOciPJUFktQ HkvGbym7KryEfwH8Tks0L7WhzyP60PL3xS9FNOJi9m+zztwYIXGDQuKM2GDsITeD 2mI2oHoPMyAD0wdI7BwSVW18p1h+jgfc4dlexKYRAoGBAOVfuiEiOchGghV5vn5N RDNscAFnpHj1QgMr6/UG05RTgmcLfVsI1I4bSkbrIuVKviGGf7atlkROALOG/xRx DLadgBEeNyHL5lz6ihQaFJLVQ0u3U4SB67J0YtVO3R6lXcIjBDHuY8SjYJ7Ci6Z6 vuDcoaEujnlrtUhaMxvSfcUJAoGBAMPsCHXte1uWNAqYad2WdLjPDlKtQJK1diCm rqmB2g8QE99hDOHItjDBEdpyFBKOIP+NpVtM2KLhRajjcL9Ph8jrID6XUqikQuVi 4J9FV2m42jXMuioTT13idAILanYg8D3idvy/3isDVkON0X3UAVKrgMEne0hJpkPL FYqgetvDAoGBAKLQ6JZMbSe0pPIJkSamQhsehgL5Rs51iX4m1z7+sYFAJfhvN3Q/ OGIHDRp6HjMUcxHpHw7U+S1TETxePwKLnLKj6hw8jnX2/nZRgWHzgVcY+sPsReRx NJVf+Cfh6yOtznfX00p+JWOXdSY8glSSHJwRAMog+hFGW1AYdt7w80XBAoGBAImR NUugqapgaEA8TrFxkJmngXYaAqpA0iYRA7kv3S4QavPBUGtFJHBNULzitydkNtVZ 3w6hgce0h9YThTo/nKc+OZDZbgfN9s7cQ75x0PQCAO4fx2P91Q+mDzDUVTeG30mE t2m3S0dGe47JiJxifV9P3wNBNrZGSIF3mrORBVNDAoGBAI0QKn2Iv7Sgo4T/XjND dl2kZTXqGAk8dOhpUiw/HdM3OGWbhHj2NdCzBliOmPyQtAr770GITWvbAI+IRYyF S7Fnk6ZVVVHsxjtaHy1uJGFlaZzKR4AGNaUTOJMs6NadzCmGPAxNQQOCqoUjn4XR -rOjr9w349JooGXhOxbu8nOxX ------END PRIVATE KEY----- \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/test-key-rsa-pss.pub b/third_party/httpsig-go/testdata/test-key-rsa-pss.pub deleted file mode 100644 index 97024bc00d..0000000000 --- a/third_party/httpsig-go/testdata/test-key-rsa-pss.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2 +QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+ oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4 aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI -2wIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/test-key-rsa.key b/third_party/httpsig-go/testdata/test-key-rsa.key deleted file mode 100644 index 05d0c16868..0000000000 --- a/third_party/httpsig-go/testdata/test-key-rsa.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEqAIBAAKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsP -BRrwWEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsd -JKFqMGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75 -jfZgkne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKI -lE0PuKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZ -SFlQPSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQABAoIBAG/JZuSWdoVHbi56 -vjgCgkjg3lkO1KrO3nrdm6nrgA9P9qaPjxuKoWaKO1cBQlE1pSWp/cKncYgD5WxE -CpAnRUXG2pG4zdkzCYzAh1i+c34L6oZoHsirK6oNcEnHveydfzJL5934egm6p8DW -+m1RQ70yUt4uRc0YSor+q1LGJvGQHReF0WmJBZHrhz5e63Pq7lE0gIwuBqL8SMaA -yRXtK+JGxZpImTq+NHvEWWCu09SCq0r838ceQI55SvzmTkwqtC+8AT2zFviMZkKR -Qo6SPsrqItxZWRty2izawTF0Bf5S2VAx7O+6t3wBsQ1sLptoSgX3QblELY5asI0J -YFz7LJECgYkAsqeUJmqXE3LP8tYoIjMIAKiTm9o6psPlc8CrLI9CH0UbuaA2JCOM -cCNq8SyYbTqgnWlB9ZfcAm/cFpA8tYci9m5vYK8HNxQr+8FS3Qo8N9RJ8d0U5Csw -DzMYfRghAfUGwmlWj5hp1pQzAuhwbOXFtxKHVsMPhz1IBtF9Y8jvgqgYHLbmyiu1 -mwJ5AL0pYF0G7x81prlARURwHo0Yf52kEw1dxpx+JXER7hQRWQki5/NsUEtv+8RT -qn2m6qte5DXLyn83b1qRscSdnCCwKtKWUug5q2ZbwVOCJCtmRwmnP131lWRYfj67 -B/xJ1ZA6X3GEf4sNReNAtaucPEelgR2nsN0gKQKBiGoqHWbK1qYvBxX2X3kbPDkv -9C+celgZd2PW7aGYLCHq7nPbmfDV0yHcWjOhXZ8jRMjmANVR/eLQ2EfsRLdW69bn -f3ZD7JS1fwGnO3exGmHO3HZG+6AvberKYVYNHahNFEw5TsAcQWDLRpkGybBcxqZo -81YCqlqidwfeO5YtlO7etx1xLyqa2NsCeG9A86UjG+aeNnXEIDk1PDK+EuiThIUa -/2IxKzJKWl1BKr2d4xAfR0ZnEYuRrbeDQYgTImOlfW6/GuYIxKYgEKCFHFqJATAG -IxHrq1PDOiSwXd2GmVVYyEmhZnbcp8CxaEMQoevxAta0ssMK3w6UsDtvUvYvF22m -qQKBiD5GwESzsFPy3Ga0MvZpn3D6EJQLgsnrtUPZx+z2Ep2x0xc5orneB5fGyF1P -WtP+fG5Q6Dpdz3LRfm+KwBCWFKQjg7uTxcjerhBWEYPmEMKYwTJF5PBG9/ddvHLQ -EQeNC8fHGg4UXU8mhHnSBt3EA10qQJfRDs15M38eG2cYwB1PZpDHScDnDA0= ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/test-key-rsa.pub b/third_party/httpsig-go/testdata/test-key-rsa.pub deleted file mode 100644 index 6be6e4c8a5..0000000000 --- a/third_party/httpsig-go/testdata/test-key-rsa.pub +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsPBRrw -WEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsdJKFq -MGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75jfZg -kne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKIlE0P -uKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZSFlQ -PSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQAB ------END RSA PUBLIC KEY----- diff --git a/third_party/httpsig-go/testdata/test-shared-secret b/third_party/httpsig-go/testdata/test-shared-secret deleted file mode 100644 index 62530dacdf..0000000000 --- a/third_party/httpsig-go/testdata/test-shared-secret +++ /dev/null @@ -1 +0,0 @@ -uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ== \ No newline at end of file diff --git a/third_party/httpsig-go/testdata/verify_request1.txt b/third_party/httpsig-go/testdata/verify_request1.txt deleted file mode 100644 index ccc1bdb590..0000000000 --- a/third_party/httpsig-go/testdata/verify_request1.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b21=();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd" -Signature: sig-b21=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopemLJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3+7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==: - -{"hello": "world"} diff --git a/third_party/httpsig-go/testdata/verify_request2.txt b/third_party/httpsig-go/testdata/verify_request2.txt deleted file mode 100644 index 62586b55d0..0000000000 --- a/third_party/httpsig-go/testdata/verify_request2.txt +++ /dev/null @@ -1,10 +0,0 @@ -POST /foo?param=Value&Pet=dog HTTP/1.1 -Host: example.com -Date: Tue, 20 Apr 2021 02:07:55 GMT -Content-Type: application/json -Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: -Content-Length: 18 -Signature-Input: sig-b21=();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd",bad-sig=("@authority");created=1618884473;keyid="test-key-rsa-pss" -Signature: sig-b21=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopemLJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3+7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==:,bad-sig=:pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=: - -{"hello": "world"} diff --git a/third_party/httpsig-go/verify.go b/third_party/httpsig-go/verify.go deleted file mode 100644 index a38ee14024..0000000000 --- a/third_party/httpsig-go/verify.go +++ /dev/null @@ -1,476 +0,0 @@ -package httpsig - -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/hmac" - "crypto/rsa" - "crypto/sha256" - "crypto/sha512" - "fmt" - "math/big" - "net/http" - "slices" - "time" - - sfv "github.com/dunglas/httpsfv" -) - -var ( - DefaultVerifyProfile = VerifyProfile{ - SignatureLabel: DefaultSignatureLabel, - AllowedAlgorithms: []Algorithm{Algo_ECDSA_P256_SHA256, Algo_ECDSA_P384_SHA384, Algo_ED25519, Algo_HMAC_SHA256}, - RequiredFields: DefaultRequiredFields, - RequiredMetadata: []Metadata{MetaCreated, MetaKeyID}, - DisallowedMetadata: []Metadata{MetaAlgorithm}, // The algorithm should be looked up from the keyid not an explicit setting. - CreatedValidDuration: time.Minute * 5, // Signatures must have been created within within the last 5 minutes - DateFieldSkew: time.Minute, // If the created parameter is present, the Date header cannot be more than a minute off. - } - - // DefaultRequiredFields covers the request body with 'content-digest' the method and full URI. - // As per the specification Date is not covered in favor of using the 'created' metadata parameter. - DefaultRequiredFields = Fields("content-digest", "@method", "@target-uri") - - ctxKeyAddDebug = struct{}{} -) - -// KeySpec is the per-key information needed to verify a signature. -type KeySpec struct { - KeyID string - Algo Algorithm - PubKey crypto.PublicKey - Secret []byte // shared secret for symmetric algorithms -} - -// KeySpec implements KeySpecer -func (ks KeySpec) KeySpec() (KeySpec, error) { - return ks, nil -} - -// KeySpecer should be implemented by your key/credential store -type KeySpecer interface { - KeySpec() (KeySpec, error) -} - -type KeyErrorReason string -type KeyError struct { - error - Reason KeyErrorReason - Message string -} - -type KeyFetcher interface { - // FetchByKeyID looks up a KeySpec from the 'keyid' metadata parameter on the signature. - FetchByKeyID(ctx context.Context, rh http.Header, keyID string) (KeySpecer, error) - // Fetch looks up a KeySpec when the keyid is not in the signature. - Fetch(ctx context.Context, rh http.Header, md MetadataProvider) (KeySpecer, error) -} - -// VerifyProfile sets the parameters for a fully valid request or response. -// A valid signature is a relatively easy accomplishment. Did the signature include all the important parts of the request? Did it use a strong enough algorithm? Was it signed 41 days ago? There are choices to make about what constitutes a valid signed request or response beyond just a verified signature. -type VerifyProfile struct { - SignatureLabel string // Which signature this profile applies to. '*' applies to all - RequiredFields []SignedField - RequiredMetadata []Metadata - DisallowedMetadata []Metadata - AllowedAlgorithms []Algorithm // Which algorithms are allowed, either from keyid meta or in the KeySpec - - // Timing enforcement options - DisableTimeEnforcement bool // If true do no time enforcement on any fields - DisableExpirationEnforcement bool // If expiration is present default to enforce - CreatedValidDuration time.Duration // Duration allowed for between time.Now and the created time - ExpiredSkew time.Duration // Maximum duration allowed between time.Now and the expired time - DateFieldSkew time.Duration // Maximum duration allowed between Date field and created -} - -type VerifyResult struct { - Verified bool - Label string - KeySpecer KeySpecer - DebugInfo VerifyDebugInfo // Present if the verifier debug flag is set and the signature was valid. - MetadataProvider -} - -type VerifyDebugInfo struct { - SignatureBase string // The signature base derived from the request. -} - -type Verifier struct { - keys KeyFetcher - profile VerifyProfile -} - -// Verify validates the signatures in a request and ensured the signature meets the required profile. -func Verify(req *http.Request, kf KeyFetcher, profile VerifyProfile) (VerifyResult, error) { - ver, err := NewVerifier(kf, profile) - if err != nil { - return VerifyResult{}, err - } - return ver.Verify(req) -} - -func VerifyResponse(resp *http.Response, kf KeyFetcher, profile VerifyProfile) (VerifyResult, error) { - ver, err := NewVerifier(kf, profile) - if err != nil { - return VerifyResult{}, err - } - return ver.VerifyResponse(resp) -} - -func NewVerifier(kf KeyFetcher, profile VerifyProfile) (*Verifier, error) { - if kf == nil { - return nil, newError(ErrSigKeyFetch, "KeyFetcher cannot be nil") - } - return &Verifier{ - keys: kf, - profile: profile, - }, nil -} - -// Verify verifies the signature(s) in an http request. Any invalid signature will return an error. -// A valid VerifyResult is returned even if error is also returned. -func (ver *Verifier) Verify(req *http.Request) (VerifyResult, error) { - return ver.verify(httpMessage{ - Req: req, - }) -} - -func (ver *Verifier) VerifyResponse(resp *http.Response) (VerifyResult, error) { - return ver.verify(httpMessage{ - IsResponse: true, - Resp: resp, - }) -} - -// verify verifies the request or response. -func (ver *Verifier) verify(hrr httpMessage) (VerifyResult, error) { - vr := VerifyResult{} - - /* calculate content digest if needed */ - if hrr.Headers().Get("Content-Digest") != "" { - digestAlgo, expectedDigest, err := getSupportedDigestFromHeader(hrr.Headers().Values("Content-Digest")) - if err != nil { - return vr, err - } - - di, err := digestBody(digestAlgo, hrr.Body()) - if err != nil { - return vr, err - } - hrr.SetBody(di.NewBody) - if !bytes.Equal(expectedDigest, di.Digest) { - return vr, newError(ErrNoSigWrongDigest, "Digest does not match") - } - } - - /* parse and extract the signature */ - sigsfv, err := parseSignaturesFromRequest(hrr.Headers()) - if err != nil { - return vr, err - } - - sig, err := unmarshalSignature(sigsfv, ver.profile.SignatureLabel) - if err != nil { - return vr, err - } - vr.Label = sig.Label - vr.MetadataProvider = sig.Input.MetadataValues - - /* verify and validate */ - base, err := calculateSignatureBase(hrr, sig.Input) - if err != nil { - return vr, err - } - - if hrr.isDebug() { - vr.DebugInfo = VerifyDebugInfo{ - SignatureBase: string(base.base), - } - } - keyspec, err := ver.verifySignature(hrr, sig, base) - vr.KeySpecer = keyspec - if err != nil { - return vr, err - } - - if err := ver.profile.validate(sig); err != nil { - return vr, err - } - - return VerifyResult{ - Verified: true, - Label: sig.Label, - KeySpecer: keyspec, - MetadataProvider: sig.Input.MetadataValues, - DebugInfo: vr.DebugInfo, - }, nil -} - -type extractedSignature struct { - Label string - Signature []byte - Input sigBaseInput -} - -// signaturesSFV is the structured field value representation of all the signatures of a request. -type signaturesSFV struct { - SigInputs *sfv.Dictionary - Sigs *sfv.Dictionary -} - -func parseSignaturesFromRequest(headers http.Header) (signaturesSFV, error) { - psd := signaturesSFV{} - /* Pull signature and signature-input header */ - sigHeader := headers.Get("signature") - if sigHeader == "" { - return psd, newError(ErrNoSigMissingSignature, "Missing signature header") - } - sigInputHeader := headers.Get("signature-input") - if sigInputHeader == "" { - return psd, newError(ErrNoSigMissingSignature, "Missing signature-input header") - } - - /* Parse headers into their appropriate HTTP structured field values */ - // signature-input must be a HTTP structured field value of type Dictionary. - var err error - psd.SigInputs, err = sfv.UnmarshalDictionary([]string{sigInputHeader}) - if err != nil { - return psd, newError(ErrNoSigInvalidSignature, "Invalid signature-input header. Not a valid Dictionary", err) - } - // signature must be a HTTP structured field value of type Dictionary. - psd.Sigs, err = sfv.UnmarshalDictionary([]string{sigHeader}) - if err != nil { - return psd, newError(ErrNoSigInvalidSignature, "Invalid signature header. Not a valid Dictionary", err) - } - return psd, nil -} - -// unmarshalSignature unmarshals a signature from hhtp structured field value (sfv) format. -func unmarshalSignature(sigs signaturesSFV, label string) (extractedSignature, error) { - sigInfo := extractedSignature{ - Label: label, - } - - sigMember, found := sigs.Sigs.Get(label) - if !found { - return sigInfo, newError(ErrNoSigMissingSignature, fmt.Sprintf("The signature for label '%s' not found", label)) - } - sigInputMember, found := sigs.SigInputs.Get(label) - if !found { - return sigInfo, newError(ErrNoSigMissingSignature, fmt.Sprintf("The signature-input for label '%s' not found", label)) - } - - // The signature must be of sfv type 'Item' - sigItem, isItem := sigMember.(sfv.Item) - if !isItem { - return sigInfo, newError(ErrSigInvalidSignature, fmt.Sprintf("The signature for label '%s' must be type Item. It was type %T", label, sigMember)) - } - // Signatures must be byte sequences. The sfv library uses []byte for byte sequences. - sigBytes, isByteSequence := sigItem.Value.([]byte) - if !isByteSequence { - return sigInfo, newError(ErrSigInvalidSignature, fmt.Sprintf("The signature for label '%s' was not a byte sequence. It was type %T", label, sigItem.Value)) - - } - sigInfo.Signature = sigBytes - - // The signature input must be of sfv type InnerList - sigInputList, isList := sigInputMember.(sfv.InnerList) - if !isList { - return sigInfo, newError(ErrSigInvalidSignature, fmt.Sprintf("The signature-input for label '%s' must be type InnerList. It was type '%T'.", label, sigInputMember)) - - } - cIDs := []componentID{} - for _, componentItem := range sigInputList.Items { - name, ok := componentItem.Value.(string) - if !ok { - return sigInfo, newError(ErrSigInvalidSignature, fmt.Sprintf("signature components must be string types")) - } - cIDs = append(cIDs, componentID{ - Name: name, - Item: componentItem, - }) - } - mds := []Metadata{} - for _, name := range sigInputList.Params.Names() { - mds = append(mds, Metadata(name)) - } - sigInfo.Input = sigBaseInput{ - Components: cIDs, - MetadataParams: mds, - MetadataValues: metadataProviderFromParams{sigInputList.Params}, - } - - return sigInfo, nil -} - -func (ver *Verifier) verifySignature(r httpMessage, sig extractedSignature, base signatureBase) (KeySpecer, error) { - var specer KeySpecer - var ks KeySpec - var err error - // Get keyspec - if slices.Contains(sig.Input.MetadataParams, MetaKeyID) { - keyid, err := sig.Input.MetadataValues.KeyID() - if err != nil { - return nil, newError(ErrSigKeyFetch, "Could not get keyid from signature metadata", err) - } - - specer, err = ver.keys.FetchByKeyID(r.Context(), r.Headers(), keyid) - if err != nil { - return nil, newError(ErrSigKeyFetch, fmt.Sprintf("Failed to fetch key for keyid '%s'", keyid), err) - } - ks, err = specer.KeySpec() - if err != nil { - return nil, newError(ErrSigKeyFetch, fmt.Sprintf("Failed to fetch key for keyid '%s'", keyid), err) - } - } else { - specer, err = ver.keys.Fetch(r.Context(), r.Headers(), sig.Input.MetadataValues) - if err != nil { - return specer, newError(ErrSigKeyFetch, fmt.Sprintf("Failed to fetch key for signature without a keyid and with label '%s'\n", sig.Label), err) - } - ks, err = specer.KeySpec() - if err != nil { - return specer, newError(ErrSigKeyFetch, fmt.Sprintf("Failed to fetch key for signature without a keyid and with label '%s'\n", sig.Label), err) - } - } - - switch ks.Algo { - case Algo_RSA_PSS_SHA512: - if rsapub, ok := ks.PubKey.(*rsa.PublicKey); ok { - opts := &rsa.PSSOptions{ - SaltLength: 64, - Hash: crypto.SHA512, - } - msgHash := sha512.Sum512(base.base) - err := rsa.VerifyPSS(rsapub, crypto.SHA512, msgHash[:], sig.Signature, opts) - if err != nil { - return specer, newError(ErrSigVerification, fmt.Sprintf("Signature did not verify for algo '%s'", ks.Algo), err) - } - return specer, nil - } else { - return specer, newError(ErrSigPublicKey, fmt.Sprintf("Invalid public key. Requires rsa.PublicKey but got type: %T", ks.PubKey)) - } - case Algo_RSA_v1_5_sha256: - if rsapub, ok := ks.PubKey.(*rsa.PublicKey); ok { - msgHash := sha256.Sum256(base.base) - err := rsa.VerifyPKCS1v15(rsapub, crypto.SHA256, msgHash[:], sig.Signature) - if err != nil { - return specer, newError(ErrSigVerification, fmt.Sprintf("Signature did not verify for algo '%s'", ks.Algo), err) - } - return specer, nil - } else { - return specer, newError(ErrSigPublicKey, fmt.Sprintf("Invalid public key. Requires rsa.PublicKey but got type: %T", ks.PubKey)) - } - case Algo_HMAC_SHA256: - if len(ks.Secret) == 0 { - return specer, newError(ErrInvalidSignatureOptions, fmt.Sprintf("No secret provided for symmetric algorithm '%s'", Algo_HMAC_SHA256)) - } - msgHash := hmac.New(sha256.New, ks.Secret) - msgHash.Write(base.base) // write does not return an error per hash.Hash documentation - calcualtedSignature := msgHash.Sum(nil) - if !hmac.Equal(calcualtedSignature, sig.Signature) { - return specer, newError(ErrSigVerification, fmt.Sprintf("Signature did not verify for algo '%s'", ks.Algo), err) - } - case Algo_ECDSA_P256_SHA256: - if epub, ok := ks.PubKey.(*ecdsa.PublicKey); ok { - if len(sig.Signature) != 64 { - return specer, newError(ErrSigInvalidSignature, fmt.Sprintf("Signature must be 64 bytes for algorithm '%s'", Algo_ECDSA_P256_SHA256)) - } - msgHash := sha256.Sum256(base.base) - // Concatenate r and s to form the signature as per the spec. r and s and *not* ANS1 encoded. - r := new(big.Int) - r.SetBytes(sig.Signature[0:32]) - s := new(big.Int) - s.SetBytes(sig.Signature[32:64]) - if !ecdsa.Verify(epub, msgHash[:], r, s) { - return specer, newError(ErrSigVerification, fmt.Sprintf("Signature did not verify for algo '%s'", ks.Algo), err) - } - } else { - return specer, newError(ErrSigPublicKey, fmt.Sprintf("Invalid public key. Requires *ecdsa.PublicKey but got type: %T", ks.PubKey)) - } - case Algo_ECDSA_P384_SHA384: - if epub, ok := ks.PubKey.(*ecdsa.PublicKey); ok { - if len(sig.Signature) != 96 { - return specer, newError(ErrSigInvalidSignature, fmt.Sprintf("Signature must be 96 bytes for algorithm '%s'", Algo_ECDSA_P256_SHA256)) - } - msgHash := sha512.Sum384(base.base) - // Concatenate r and s to form the signature as per the spec. r and s and *not* ANS1 encoded. - r := new(big.Int) - r.SetBytes(sig.Signature[0:48]) - s := new(big.Int) - s.SetBytes(sig.Signature[48:96]) - if !ecdsa.Verify(epub, msgHash[:], r, s) { - return specer, newError(ErrSigVerification, fmt.Sprintf("Signature did not verify for algo '%s'", ks.Algo), err) - } - } else { - return specer, newError(ErrSigPublicKey, fmt.Sprintf("Invalid public key. Requires *ecdsa.PublicKey but got type: %T", ks.PubKey)) - } - case Algo_ED25519: - if edpubkey, ok := ks.PubKey.(ed25519.PublicKey); ok { - if !ed25519.Verify(edpubkey, base.base, sig.Signature) { - return specer, newError(ErrSigVerification, fmt.Sprintf("Signature did not verify for algo '%s'", ks.Algo), err) - } - } else { - return specer, newError(ErrSigPublicKey, fmt.Sprintf("Invalid public key. Requires ed25519.PublicKey but got type: %T", ks.PubKey)) - } - default: - return specer, newError(ErrSigUnsupportedAlgorithm, fmt.Sprintf("Invalid verification algorithm '%s'", ks.Algo)) - } - return specer, nil -} - -func (vp VerifyProfile) validate(sig extractedSignature) error { - return nil -} - -type metadataProviderFromParams struct { - Params *sfv.Params -} - -func (mp metadataProviderFromParams) Created() (int, error) { - if val, ok := mp.Params.Get(string(MetaCreated)); ok { - return int(val.(int64)), nil - } - return 0, fmt.Errorf("No created value") -} - -func (mp metadataProviderFromParams) Expires() (int, error) { - if val, ok := mp.Params.Get(string(MetaExpires)); ok { - return int(val.(int64)), nil - } - return 0, fmt.Errorf("No expires value") -} - -func (mp metadataProviderFromParams) Nonce() (string, error) { - if val, ok := mp.Params.Get(string(MetaNonce)); ok { - return val.(string), nil - } - return "", fmt.Errorf("No nonce value") -} - -func (mp metadataProviderFromParams) Alg() (string, error) { - if val, ok := mp.Params.Get(string(MetaAlgorithm)); ok { - return val.(string), nil - } - return "", fmt.Errorf("No alg value") -} - -func (mp metadataProviderFromParams) KeyID() (string, error) { - if val, ok := mp.Params.Get(string(MetaKeyID)); ok { - return val.(string), nil - } - return "", fmt.Errorf("No keyid value") -} - -func (mp metadataProviderFromParams) Tag() (string, error) { - if val, ok := mp.Params.Get(string(MetaTag)); ok { - return val.(string), nil - } - return "", fmt.Errorf("No tag value") -} - -func SetAddDebugInfo(ctx context.Context) context.Context { - return context.WithValue(ctx, ctxKeyAddDebug, true) -} diff --git a/third_party/httpsig-go/verify_test.go b/third_party/httpsig-go/verify_test.go deleted file mode 100644 index 4c0dfe3489..0000000000 --- a/third_party/httpsig-go/verify_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package httpsig_test - -import ( - "fmt" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/remitly-oss/httpsig-go" - "github.com/remitly-oss/httpsig-go/keyman" - "github.com/remitly-oss/httpsig-go/keyutil" - "github.com/remitly-oss/httpsig-go/sigtest" -) - -func TestVerify(t *testing.T) { - testcases := []struct { - Name string - RequestFile string - Label string - AddDebugInfo bool - Keys httpsig.KeyFetcher - Expected httpsig.VerifyResult - }{ - { - Name: "OneValid", - Label: "sig-b21", - RequestFile: "verify_request1.txt", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-rsa-pss": { - KeyID: "test-key-rsa-pss", - Algo: httpsig.Algo_RSA_PSS_SHA512, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-rsa-pss.pub"), - }, - }), - Expected: httpsig.VerifyResult{ - Verified: true, - Label: "sig-b21", - MetadataProvider: &fixedMetadataProvider{map[httpsig.Metadata]any{ - httpsig.MetaKeyID: "test-key-rsa-pss", - httpsig.MetaCreated: int64(1618884473), - httpsig.MetaNonce: "b3k2pp5k7z-50gnwp.yemd", - }}, - KeySpecer: httpsig.KeySpec{ - KeyID: "test-key-rsa-pss", - Algo: httpsig.Algo_RSA_PSS_SHA512, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-rsa-pss.pub"), - }, - }, - }, - { - Name: "OneValidDebug", - Label: "sig-b21", - RequestFile: "verify_request1.txt", - AddDebugInfo: true, - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-rsa-pss": { - KeyID: "test-key-rsa-pss", - Algo: httpsig.Algo_RSA_PSS_SHA512, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-rsa-pss.pub"), - }, - }), - Expected: httpsig.VerifyResult{ - Verified: true, - Label: "sig-b21", - MetadataProvider: &fixedMetadataProvider{map[httpsig.Metadata]any{ - httpsig.MetaKeyID: "test-key-rsa-pss", - httpsig.MetaCreated: int64(1618884473), - httpsig.MetaNonce: "b3k2pp5k7z-50gnwp.yemd", - }}, - KeySpecer: httpsig.KeySpec{ - KeyID: "test-key-rsa-pss", - Algo: httpsig.Algo_RSA_PSS_SHA512, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-rsa-pss.pub"), - }, - DebugInfo: httpsig.VerifyDebugInfo{ - SignatureBase: `"@signature-params": ();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd"`, - }, - }, - }, - } - - for _, tc := range testcases { - t.Run(tc.Name, func(t *testing.T) { - req := sigtest.ReadRequest(t, tc.RequestFile) - if tc.AddDebugInfo { - req = req.WithContext(httpsig.SetAddDebugInfo(req.Context())) - } - actual, err := httpsig.Verify(req, tc.Keys, httpsig.VerifyProfile{SignatureLabel: tc.Label}) - if err != nil { - t.Fatal(err) - } - - // VerifyResult is returned even when error is also returned. - // Because VerifryResult embed Metadataprovider we first need diff ignoring the MetadataProvider - sigtest.Diff(t, tc.Expected, actual, "Did not match", - cmp.FilterPath(func(p cmp.Path) bool { - return p.String() == "MetadataProvider" - }, cmp.Ignore())) - - // Then diff the metadata provider - sigtest.Diff(t, tc.Expected, actual, "Did not match", getCmdOpts()...) - }) - } -} - -func TestVerifyInvalid(t *testing.T) { - testcases := []struct { - Name string - RequestFile string - Label string - Keys httpsig.KeyFetcher - Expected httpsig.ErrCode - }{ - { - Name: "SignatureVerificationFailure", - RequestFile: "verify_request2.txt", - Label: "bad-sig", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{ - "test-key-rsa-pss": { - KeyID: "test-key-rsa-pss", - Algo: httpsig.Algo_RSA_PSS_SHA512, - PubKey: keyutil.MustReadPublicKeyFile("testdata/test-key-rsa-pss.pub"), - }, - }), - Expected: httpsig.ErrSigVerification, - }, - { - Name: "KeyFetchError", - RequestFile: "verify_request2.txt", - Label: "sig-b21", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{}), - Expected: httpsig.ErrSigKeyFetch, - }, - { - Name: "KeyFetchError2", - RequestFile: "verify_request2.txt", - Label: "bad-sig", - Keys: keyman.NewKeyFetchInMemory(map[string]httpsig.KeySpec{}), - Expected: httpsig.ErrSigKeyFetch, - }, - } - - for _, tc := range testcases { - t.Run(tc.Name, func(t *testing.T) { - _, err := httpsig.Verify(sigtest.ReadRequest(t, tc.RequestFile), tc.Keys, httpsig.VerifyProfile{SignatureLabel: tc.Label}) - - if err == nil { - t.Fatal("Expected err") - } - if sigerr, ok := err.(*httpsig.SignatureError); ok { - sigtest.Diff(t, tc.Expected, sigerr.Code, "Did not match") - } else { - sigtest.Diff(t, tc.Expected, sigerr, "Did not match") - } - }) - } -} - -type fixedMetadataProvider struct { - values map[httpsig.Metadata]any -} - -func (fmp fixedMetadataProvider) Created() (int, error) { - if val, ok := fmp.values[httpsig.MetaCreated]; ok { - return int(val.(int64)), nil - } - return 0, fmt.Errorf("No created value") -} - -func (fmp fixedMetadataProvider) Expires() (int, error) { - if val, ok := fmp.values[httpsig.MetaExpires]; ok { - return int(val.(int64)), nil - } - return 0, fmt.Errorf("No expires value") -} - -func (fmp fixedMetadataProvider) Nonce() (string, error) { - if val, ok := fmp.values[httpsig.MetaNonce]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No nonce value") -} - -func (fmp fixedMetadataProvider) Alg() (string, error) { - if val, ok := fmp.values[httpsig.MetaAlgorithm]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No alg value") -} - -func (fmp fixedMetadataProvider) KeyID() (string, error) { - if val, ok := fmp.values[httpsig.MetaKeyID]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No keyid value") -} - -func (fmp fixedMetadataProvider) Tag() (string, error) { - if val, ok := fmp.values[httpsig.MetaTag]; ok { - return val.(string), nil - } - return "", fmt.Errorf("No tag value") -} - -func metaVal[E comparable](f1 func() (E, error)) any { - val, err := f1() - if err != nil { - return err.Error() - } - return val -} - -func getCmdOpts() []cmp.Option { - return []cmp.Option{ - // This gets used for *ANY* struct assignable to MetadataProvider including other structres - // that embed it! - cmp.Transformer("MetadataProvider", TransformMeta), - } -} - -func TransformMeta(md httpsig.MetadataProvider) map[string]any { - out := map[string]any{} - - if md == nil { - return out - } - out[string(httpsig.MetaCreated)] = metaVal(md.Created) - out[string(httpsig.MetaExpires)] = metaVal(md.Expires) - out[string(httpsig.MetaNonce)] = metaVal(md.Nonce) - out[string(httpsig.MetaAlgorithm)] = metaVal(md.Alg) - out[string(httpsig.MetaKeyID)] = metaVal(md.KeyID) - out[string(httpsig.MetaTag)] = metaVal(md.Tag) - return out -}