136 行
5.0 KiB
Go
136 行
5.0 KiB
Go
|
// Implements HTTP request and response signing and verification. Supports the
|
||
|
// major MAC and asymmetric key signature algorithms. It has several safety
|
||
|
// restrictions: One, none of the widely known non-cryptographically safe
|
||
|
// algorithms are permitted; Two, the RSA SHA256 algorithms must be available in
|
||
|
// the binary (and it should, barring export restrictions); Finally, the library
|
||
|
// assumes either the 'Authorizationn' or 'Signature' headers are to be set (but
|
||
|
// not both).
|
||
|
package httpsig
|
||
|
|
||
|
import (
|
||
|
"crypto"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
)
|
||
|
|
||
|
// Algorithm specifies a cryptography secure algorithm for signing HTTP requests
|
||
|
// and responses.
|
||
|
type Algorithm string
|
||
|
|
||
|
const (
|
||
|
defaultAlgorithm = RSA_SHA256
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// This should guarantee that at runtime the defaultAlgorithm will not
|
||
|
// result in errors when fetching a macer or signer (see algorithms.go)
|
||
|
if ok, err := isAvailable(defaultAlgorithm); err != nil {
|
||
|
panic(err)
|
||
|
} else if !ok {
|
||
|
panic(fmt.Sprintf("the default httpsig algorithm is unavailable: %q", defaultAlgorithm))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
HMAC_SHA224 Algorithm = hmacPrefix + "-" + sha224String
|
||
|
HMAC_SHA256 = hmacPrefix + "-" + sha256String
|
||
|
HMAC_SHA384 = hmacPrefix + "-" + sha384String
|
||
|
HMAC_SHA512 = hmacPrefix + "-" + sha512String
|
||
|
HMAC_RIPEMD160 = hmacPrefix + "-" + ripemd160String
|
||
|
HMAC_SHA3_224 = hmacPrefix + "-" + sha3_224String
|
||
|
HMAC_SHA3_256 = hmacPrefix + "-" + sha3_256String
|
||
|
HMAC_SHA3_384 = hmacPrefix + "-" + sha3_384String
|
||
|
HMAC_SHA3_512 = hmacPrefix + "-" + sha3_512String
|
||
|
HMAC_SHA512_224 = hmacPrefix + "-" + sha512_224String
|
||
|
HMAC_SHA512_256 = hmacPrefix + "-" + sha512_256String
|
||
|
HMAC_BLAKE2S_256 = hmacPrefix + "-" + blake2s_256String
|
||
|
HMAC_BLAKE2B_256 = hmacPrefix + "-" + blake2b_256String
|
||
|
HMAC_BLAKE2B_384 = hmacPrefix + "-" + blake2b_384String
|
||
|
HMAC_BLAKE2B_512 = hmacPrefix + "-" + blake2b_512String
|
||
|
BLAKE2S_256 = blake2s_256String
|
||
|
BLAKE2B_256 = blake2b_256String
|
||
|
BLAKE2B_384 = blake2b_384String
|
||
|
BLAKE2B_512 = blake2b_512String
|
||
|
RSA_SHA224 = rsaPrefix + "-" + sha224String
|
||
|
// RSA_SHA256 is the default algorithm.
|
||
|
RSA_SHA256 = rsaPrefix + "-" + sha256String
|
||
|
RSA_SHA384 = rsaPrefix + "-" + sha384String
|
||
|
RSA_SHA512 = rsaPrefix + "-" + sha512String
|
||
|
RSA_RIPEMD160 = rsaPrefix + "-" + ripemd160String
|
||
|
RSA_SHA3_224 = rsaPrefix + "-" + sha3_224String
|
||
|
RSA_SHA3_256 = rsaPrefix + "-" + sha3_256String
|
||
|
RSA_SHA3_384 = rsaPrefix + "-" + sha3_384String
|
||
|
RSA_SHA3_512 = rsaPrefix + "-" + sha3_512String
|
||
|
RSA_SHA512_224 = rsaPrefix + "-" + sha512_224String
|
||
|
RSA_SHA512_256 = rsaPrefix + "-" + sha512_256String
|
||
|
RSA_BLAKE2S_256 = rsaPrefix + "-" + blake2s_256String
|
||
|
RSA_BLAKE2B_256 = rsaPrefix + "-" + blake2b_256String
|
||
|
RSA_BLAKE2B_384 = rsaPrefix + "-" + blake2b_384String
|
||
|
RSA_BLAKE2B_512 = rsaPrefix + "-" + blake2b_512String
|
||
|
)
|
||
|
|
||
|
// HTTP Signatures can be applied to either the "Authorization" or "Signature"
|
||
|
// HTTP header
|
||
|
type SignatureScheme string
|
||
|
|
||
|
const (
|
||
|
Signature SignatureScheme = "Signature"
|
||
|
Authorization = "Authorization"
|
||
|
)
|
||
|
|
||
|
// Signers will sign HTTP requests or responses based on the algorithms and
|
||
|
// headers selected at creation time.
|
||
|
type Signer interface {
|
||
|
SignRequest(pKey crypto.PrivateKey, pubKeyId string, r *http.Request) error
|
||
|
SignResponse(pKey crypto.PrivateKey, pubKeyId string, r http.ResponseWriter) error
|
||
|
}
|
||
|
|
||
|
// NewSigner creates a new Signer with the provided algorithm preferences to
|
||
|
// make http signatures. Only the first available algorithm will be used, which
|
||
|
// is returned by this function along with the Signer. If none of the preferred
|
||
|
// algorithms were available, then the default algorithm is used. The headers
|
||
|
// specified will be included into the HTTP signatures.
|
||
|
//
|
||
|
// The provided scheme determines which header is populated with the HTTP
|
||
|
// Signature.
|
||
|
//
|
||
|
// An error is returned if an unknown or a known cryptographically insecure
|
||
|
// Algorithm is provided.
|
||
|
func NewSigner(prefs []Algorithm, headers []string, scheme SignatureScheme) (Signer, Algorithm, error) {
|
||
|
for _, pref := range prefs {
|
||
|
if ok, err := isAvailable(string(pref)); err != nil {
|
||
|
return nil, "", err
|
||
|
} else if !ok {
|
||
|
continue
|
||
|
}
|
||
|
s, err := newSigner(pref, headers, scheme)
|
||
|
return s, pref, err
|
||
|
}
|
||
|
s, err := newSigner(defaultAlgorithm, headers, scheme)
|
||
|
return s, defaultAlgorithm, err
|
||
|
}
|
||
|
|
||
|
// TODO: Verification
|
||
|
|
||
|
func newSigner(algo Algorithm, headers []string, scheme SignatureScheme) (Signer, error) {
|
||
|
s, err := signerFromString(string(algo))
|
||
|
if err == nil {
|
||
|
a := &asymmSigner{
|
||
|
s: s,
|
||
|
headers: headers,
|
||
|
targetHeader: scheme,
|
||
|
}
|
||
|
return a, nil
|
||
|
}
|
||
|
m, err := macerFromString(string(algo))
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("no crypto implementation available for %q", algo)
|
||
|
}
|
||
|
c := &macSigner{
|
||
|
m: m,
|
||
|
headers: headers,
|
||
|
targetHeader: scheme,
|
||
|
}
|
||
|
return c, nil
|
||
|
}
|