diff --git a/LICENSE b/LICENSE index 3b4e6e9..d9228fd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ BSD 3-Clause License Copyright (c) 2021, James Bowes +Copyright (c) 2023, Alexander Taraymovich, OffBlocks All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index f2b9c29..323bd88 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,37 @@ and the request path. ## Usage +### Standalone Signing and Verification + +To sign a request, first instantiate a `Signer` using your preferred key and signing algorithm: + +```go +// Create a signer +signer := httpsig.NewSigner(httpsig.WithSignEcdsaP256Sha256("key1", privKey)) + +// Create a request +req, _ := http.NewRequest("GET", "https://some-url.com", nil) + +// Sign the request +header, _ := signer.Sign(httpsig.MessageFromRequest(req)) + +// Add the signature to the request +req.Header = header +``` + +To verify a response, instantiate a `Verifier` using your preferred key and signing algorithm: + +```go +// Receive a response from server +resp, _ := client.Post("https://some-url.com", "application/json", &buf) + +// Create a verifier +verifier := httpsig.NewVerifier(httpsig.WithVerifyEcdsaP256Sha256("key1", pubKey)) + +// Verify the response +err := verifier.Verify(httpsig.MessageFromResponse(resp)) +``` + ### Signing HTTP Requests in Clients To sign HTTP requests from a client, wrap an `http.Client`'s transport with diff --git a/base.go b/base.go index d6e1d97..d922106 100644 --- a/base.go +++ b/base.go @@ -1,3 +1,34 @@ +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + package httpsig import ( @@ -59,7 +90,7 @@ func createSigningParameters(config *SignConfig) *httpsfv.Params { } if slices.Contains(params, ParamKeyID) { - // attempt to obtain an overriden key id, otherwise use the one supplied by the key + // attempt to obtain an overridden key id, otherwise use the one supplied by the key var keyID *string if config.ParamValues != nil && config.ParamValues.KeyID != nil { keyID = config.ParamValues.KeyID @@ -71,7 +102,7 @@ func createSigningParameters(config *SignConfig) *httpsfv.Params { } if slices.Contains(params, ParamAlg) { - // attempt to obtain an overriden algorithm, otherwise use the one supplied by the key + // attempt to obtain an overridden algorithm, otherwise use the one supplied by the key var alg *Algorithm if config.ParamValues != nil && config.ParamValues.Alg != nil { alg = config.ParamValues.Alg diff --git a/canonicalise.go b/canonicalise.go index 1b3a671..ea4caca 100644 --- a/canonicalise.go +++ b/canonicalise.go @@ -1,6 +1,33 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package httpsig diff --git a/digest.go b/digest.go index f8d4542..5b7ac58 100644 --- a/digest.go +++ b/digest.go @@ -1,3 +1,34 @@ +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + package httpsig import ( @@ -15,6 +46,30 @@ type DigestConfig struct { Algorithms []DigestAlgorithm } +type Digestor struct { + *digestor +} + +// NewDigestor creates a new digestor with the given options +func NewDigestor(opts ...digestOption) *Digestor { + d := digestor{} + + for _, o := range opts { + o.configureDigest(&d) + } + + if len(d.config.Algorithms) == 0 { + d.config.Algorithms = []DigestAlgorithm{DigestAlgorithmSha256} + } + + return &Digestor{&d} +} + +// Digest creates a digest header for the given body +func (d *Digestor) Digest(body []byte) (http.Header, error) { + return d.digestor.Digest(body) +} + type digestor struct { config DigestConfig } diff --git a/digest_test.go b/digest_test.go index a971a82..9d08b93 100644 --- a/digest_test.go +++ b/digest_test.go @@ -1,3 +1,34 @@ +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + package httpsig import ( diff --git a/doc.go b/doc.go index 1fa2691..c8e3a1a 100644 --- a/doc.go +++ b/doc.go @@ -1,6 +1,33 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Package httpsig signs and verifies HTTP requests (with body digests) according diff --git a/example_test.go b/example_test.go index 3092cf6..9d95b55 100644 --- a/example_test.go +++ b/example_test.go @@ -1,6 +1,33 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package httpsig_test diff --git a/httpsig.go b/httpsig.go index 38f0764..aa26869 100644 --- a/httpsig.go +++ b/httpsig.go @@ -1,13 +1,35 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// BSD 3-Clause License -package httpsig +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. -import ( - "net/http" - "time" -) +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package httpsig const ( SignatureHeader = "Signature" @@ -15,19 +37,13 @@ const ( ContentDigestHeader = "Content-Digest" ) -type Param string - -const ( - ParamKeyID Param = "keyid" - ParamAlg Param = "alg" - ParamCreated Param = "created" - ParamExpires Param = "expires" - ParamNonce Param = "nonce" - ParamTag Param = "tag" -) - -var defaultParams = []Param{ParamKeyID, ParamAlg, ParamCreated} - +// Algorithm is the signature algorithm to use. Available algorithms are: +// - RSASSA-PKCS1-v1_5 using SHA-256 (rsa-v1_5-sha256) +// - RSASSA-PSS using SHA-512 (rsa-pss-sha512) +// - ECDSA using curve P-256 DSS and SHA-256 (ecdsa-p256-sha256) +// - ECDSA using curve P-384 DSS and SHA-384 (ecdsa-p384-sha384) +// - EdDSA using curve edwards25519 (ed25519) +// - HMAC using SHA-256 (hmac-sha256) type Algorithm string const ( @@ -39,112 +55,12 @@ const ( AlgorithmHmacSha256 Algorithm = "hmac-sha256" ) +// DigestAlgorithm is the digest algorithm to use. Available algorithms are: +// - SHA-256 (sha-256) +// - SHA-512 (sha-512) type DigestAlgorithm string const ( DigestAlgorithmSha256 DigestAlgorithm = "sha-256" DigestAlgorithmSha512 DigestAlgorithm = "sha-512" ) - -type SigningKey interface { - Sign(data []byte) ([]byte, error) - GetKeyID() string - GetAlgorithm() Algorithm -} - -// The signature parameters to include in signing -type SignatureParameters struct { - // The created time for the signature. `nil` indicates not to populate the `created` time - // default: time.Now() - Created *time.Time - - // The time the signature should be deemed to have expired - // default: time.Now() + 5 mins - Expires *time.Time - - // A nonce for the request - Nonce *string - - // The algorithm the signature is signed with (overrides the alg provided by the signing key) - Alg *Algorithm - - // The key id the signature is signed with (overrides the keyid provided by the signing key) - KeyID *string - - // A tag parameter for the signature - Tag *string -} - -type Signer struct { - *signer -} - -func NewSigner(opts ...signOption) *Signer { - s := signer{} - - for _, o := range opts { - o.configureSign(&s) - } - - if len(s.config.Params) == 0 { - s.config.Params = defaultParams[:] - } - - return &Signer{&s} -} - -func (s *Signer) Sign(m *Message) (http.Header, error) { - return s.signer.Sign(m) -} - -type VerifyingKey interface { - Verify(data []byte, signature []byte) error - GetKeyID() string - GetAlgorithm() Algorithm -} - -type VerifyingKeyResolver interface { - Resolve(keyID string) (VerifyingKey, error) -} - -type Verifier struct { - *verifier -} - -func NewVerifier(opts ...verifyOption) *Verifier { - v := verifier{} - - v.config.Keys = make(map[string]VerifyingKey) - - for _, o := range opts { - o.configureVerify(&v) - } - - return &Verifier{&v} -} - -func (v *Verifier) Verify(m *Message) error { - err := v.verifier.Verify(m) - if err != nil { - return err - } - return nil -} - -type Digestor struct { - *digestor -} - -func NewDigestor(opts ...digestOption) *Digestor { - d := digestor{} - - for _, o := range opts { - o.configureDigest(&d) - } - - if len(d.config.Algorithms) == 0 { - d.config.Algorithms = []DigestAlgorithm{DigestAlgorithmSha256} - } - - return &Digestor{&d} -} diff --git a/middleware.go b/middleware.go index 479e161..44370a4 100644 --- a/middleware.go +++ b/middleware.go @@ -1,3 +1,34 @@ +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + package httpsig import "net/http" @@ -5,8 +36,8 @@ import "net/http" // NewVerifyMiddleware returns a configured http server middleware that can be used to wrap // multiple handlers for http message signature and digest verification. // -// Use the `WithVerify*` option funcs to configure signature verification algorithms that map -// to their provided key ids. +// Use the `WithVerify*` option funcs to configure signature verification algorithms and verification +// parameters. // // Requests with missing signatures, malformed signature headers, expired signatures, or // invalid signatures are rejected with a `400` response. Only one valid signature is required diff --git a/option.go b/option.go index b2d1745..ad99dfd 100644 --- a/option.go +++ b/option.go @@ -1,3 +1,34 @@ +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + package httpsig import ( diff --git a/signer.go b/signer.go index dd6e832..1cd76f6 100644 --- a/signer.go +++ b/signer.go @@ -1,6 +1,33 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package httpsig @@ -17,10 +44,25 @@ import ( "errors" "fmt" "net/http" + "time" "github.com/dunglas/httpsfv" ) +type Param string + +const ( + ParamKeyID Param = "keyid" + ParamAlg Param = "alg" + ParamCreated Param = "created" + ParamExpires Param = "expires" + ParamNonce Param = "nonce" + ParamTag Param = "tag" +) + +var defaultParams = []Param{ParamKeyID, ParamAlg, ParamCreated} + +// SignConfig is the configuration for a signer type SignConfig struct { // The key to use for signing Key SigningKey @@ -43,6 +85,62 @@ type SignConfig struct { ParamValues *SignatureParameters } +// The key to use for signing +type SigningKey interface { + Sign(data []byte) ([]byte, error) + GetKeyID() string + GetAlgorithm() Algorithm +} + +// The signature parameters to include in signing +type SignatureParameters struct { + // The created time for the signature. `nil` indicates not to populate the `created` time + // default: time.Now() + Created *time.Time + + // The time the signature should be deemed to have expired + // default: time.Now() + 5 mins + Expires *time.Time + + // A nonce for the request + Nonce *string + + // The algorithm the signature is signed with (overrides the alg provided by the signing key) + Alg *Algorithm + + // The key id the signature is signed with (overrides the keyid provided by the signing key) + KeyID *string + + // A tag parameter for the signature + Tag *string +} + +type Signer struct { + *signer +} + +// NewSigner creates a new signer with the given options +// +// Use the `WithSign*` option funcs to configure signing algorithms and parameters. +func NewSigner(opts ...signOption) *Signer { + s := signer{} + + for _, o := range opts { + o.configureSign(&s) + } + + if len(s.config.Params) == 0 { + s.config.Params = defaultParams[:] + } + + return &Signer{&s} +} + +// Sign signs the given message and returns updated request headers +func (s *Signer) Sign(m *Message) (http.Header, error) { + return s.signer.Sign(m) +} + type signer struct { config SignConfig } diff --git a/spec_test.go b/spec_test.go index 7e72962..666b168 100644 --- a/spec_test.go +++ b/spec_test.go @@ -1,6 +1,33 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package httpsig diff --git a/transport.go b/transport.go index 380db79..c0c3880 100644 --- a/transport.go +++ b/transport.go @@ -1,3 +1,34 @@ +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + package httpsig import "net/http" diff --git a/verifier.go b/verifier.go index 13a638e..c6a737b 100644 --- a/verifier.go +++ b/verifier.go @@ -1,6 +1,33 @@ -// Copyright (c) 2021 James Bowes. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// BSD 3-Clause License + +// Copyright (c) 2021, James Bowes +// Copyright (c) 2023, Alexander Taraymovich, OffBlocks +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package httpsig @@ -20,6 +47,7 @@ import ( "github.com/dunglas/httpsfv" ) +// VerifyConfig is the configuration for a verifier type VerifyConfig struct { // The keys to use for signing Keys map[string]VerifyingKey @@ -53,6 +81,43 @@ type VerifyConfig struct { All bool } +// VerifyingKey is the key to use for verifying a signature +type VerifyingKey interface { + Verify(data []byte, signature []byte) error + GetKeyID() string + GetAlgorithm() Algorithm +} + +// VerifyingKeyResolver is used to resolve a key id to a verifying key +type VerifyingKeyResolver interface { + Resolve(keyID string) (VerifyingKey, error) +} + +type Verifier struct { + *verifier +} + +// NewVerifier creates a new verifier with the given options +// +// Use the `WithVerify*` option funcs to configure signature verification algorithms and verification +// parameters. +func NewVerifier(opts ...verifyOption) *Verifier { + v := verifier{} + + v.config.Keys = make(map[string]VerifyingKey) + + for _, o := range opts { + o.configureVerify(&v) + } + + return &Verifier{&v} +} + +// Verify verifies the given message +func (v *Verifier) Verify(m *Message) error { + return v.verifier.Verify(m) +} + type clock interface { Now() time.Time }