summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-fed/httpsig/algorithms.go
diff options
context:
space:
mode:
authorLibravatar Tobi Smethurst <31960611+tsmethurst@users.noreply.github.com>2021-08-12 21:03:24 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-12 21:03:24 +0200
commit98263a7de64269898a2f81207e38943b5c8e8653 (patch)
tree743c90f109a6c5d27832d1dcef2388d939f0f77a /vendor/github.com/go-fed/httpsig/algorithms.go
parentText duplication fix (#137) (diff)
downloadgotosocial-98263a7de64269898a2f81207e38943b5c8e8653.tar.xz
Grand test fixup (#138)
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
Diffstat (limited to 'vendor/github.com/go-fed/httpsig/algorithms.go')
-rw-r--r--vendor/github.com/go-fed/httpsig/algorithms.go532
1 files changed, 532 insertions, 0 deletions
diff --git a/vendor/github.com/go-fed/httpsig/algorithms.go b/vendor/github.com/go-fed/httpsig/algorithms.go
new file mode 100644
index 000000000..9595941be
--- /dev/null
+++ b/vendor/github.com/go-fed/httpsig/algorithms.go
@@ -0,0 +1,532 @@
+package httpsig
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/hmac"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
+ "crypto/subtle" // Use should trigger great care
+ "encoding/asn1"
+ "errors"
+ "fmt"
+ "hash"
+ "io"
+ "math/big"
+ "strings"
+
+ "golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/blake2s"
+ "golang.org/x/crypto/ed25519"
+ "golang.org/x/crypto/ripemd160"
+ "golang.org/x/crypto/sha3"
+ "golang.org/x/crypto/ssh"
+)
+
+const (
+ hmacPrefix = "hmac"
+ rsaPrefix = "rsa"
+ sshPrefix = "ssh"
+ ecdsaPrefix = "ecdsa"
+ ed25519Prefix = "ed25519"
+ md4String = "md4"
+ md5String = "md5"
+ sha1String = "sha1"
+ sha224String = "sha224"
+ sha256String = "sha256"
+ sha384String = "sha384"
+ sha512String = "sha512"
+ md5sha1String = "md5sha1"
+ ripemd160String = "ripemd160"
+ sha3_224String = "sha3-224"
+ sha3_256String = "sha3-256"
+ sha3_384String = "sha3-384"
+ sha3_512String = "sha3-512"
+ sha512_224String = "sha512-224"
+ sha512_256String = "sha512-256"
+ blake2s_256String = "blake2s-256"
+ blake2b_256String = "blake2b-256"
+ blake2b_384String = "blake2b-384"
+ blake2b_512String = "blake2b-512"
+)
+
+var blake2Algorithms = map[crypto.Hash]bool{
+ crypto.BLAKE2s_256: true,
+ crypto.BLAKE2b_256: true,
+ crypto.BLAKE2b_384: true,
+ crypto.BLAKE2b_512: true,
+}
+
+var hashToDef = map[crypto.Hash]struct {
+ name string
+ new func(key []byte) (hash.Hash, error) // Only MACers will accept a key
+}{
+ // Which standard names these?
+ // The spec lists the following as a canonical reference, which is dead:
+ // http://www.iana.org/assignments/signature-algorithms
+ //
+ // Note that the forbidden hashes have an invalid 'new' function.
+ crypto.MD4: {md4String, func(key []byte) (hash.Hash, error) { return nil, nil }},
+ crypto.MD5: {md5String, func(key []byte) (hash.Hash, error) { return nil, nil }},
+ // Temporarily enable SHA1 because of issue https://github.com/golang/go/issues/37278
+ crypto.SHA1: {sha1String, func(key []byte) (hash.Hash, error) { return sha1.New(), nil }},
+ crypto.SHA224: {sha224String, func(key []byte) (hash.Hash, error) { return sha256.New224(), nil }},
+ crypto.SHA256: {sha256String, func(key []byte) (hash.Hash, error) { return sha256.New(), nil }},
+ crypto.SHA384: {sha384String, func(key []byte) (hash.Hash, error) { return sha512.New384(), nil }},
+ crypto.SHA512: {sha512String, func(key []byte) (hash.Hash, error) { return sha512.New(), nil }},
+ crypto.MD5SHA1: {md5sha1String, func(key []byte) (hash.Hash, error) { return nil, nil }},
+ crypto.RIPEMD160: {ripemd160String, func(key []byte) (hash.Hash, error) { return ripemd160.New(), nil }},
+ crypto.SHA3_224: {sha3_224String, func(key []byte) (hash.Hash, error) { return sha3.New224(), nil }},
+ crypto.SHA3_256: {sha3_256String, func(key []byte) (hash.Hash, error) { return sha3.New256(), nil }},
+ crypto.SHA3_384: {sha3_384String, func(key []byte) (hash.Hash, error) { return sha3.New384(), nil }},
+ crypto.SHA3_512: {sha3_512String, func(key []byte) (hash.Hash, error) { return sha3.New512(), nil }},
+ crypto.SHA512_224: {sha512_224String, func(key []byte) (hash.Hash, error) { return sha512.New512_224(), nil }},
+ crypto.SHA512_256: {sha512_256String, func(key []byte) (hash.Hash, error) { return sha512.New512_256(), nil }},
+ crypto.BLAKE2s_256: {blake2s_256String, func(key []byte) (hash.Hash, error) { return blake2s.New256(key) }},
+ crypto.BLAKE2b_256: {blake2b_256String, func(key []byte) (hash.Hash, error) { return blake2b.New256(key) }},
+ crypto.BLAKE2b_384: {blake2b_384String, func(key []byte) (hash.Hash, error) { return blake2b.New384(key) }},
+ crypto.BLAKE2b_512: {blake2b_512String, func(key []byte) (hash.Hash, error) { return blake2b.New512(key) }},
+}
+
+var stringToHash map[string]crypto.Hash
+
+const (
+ defaultAlgorithm = RSA_SHA256
+ defaultAlgorithmHashing = sha256String
+)
+
+func init() {
+ stringToHash = make(map[string]crypto.Hash, len(hashToDef))
+ for k, v := range hashToDef {
+ stringToHash[v.name] = k
+ }
+ // 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(string(defaultAlgorithmHashing)); err != nil {
+ panic(err)
+ } else if !ok {
+ panic(fmt.Sprintf("the default httpsig algorithm is unavailable: %q", defaultAlgorithm))
+ }
+}
+
+func isForbiddenHash(h crypto.Hash) bool {
+ switch h {
+ // Not actually cryptographically secure
+ case crypto.MD4:
+ fallthrough
+ case crypto.MD5:
+ fallthrough
+ case crypto.MD5SHA1: // shorthand for crypto/tls, not actually implemented
+ return true
+ }
+ // Still cryptographically secure
+ return false
+}
+
+// signer is an internally public type.
+type signer interface {
+ Sign(rand io.Reader, p crypto.PrivateKey, sig []byte) ([]byte, error)
+ Verify(pub crypto.PublicKey, toHash, signature []byte) error
+ String() string
+}
+
+// macer is an internally public type.
+type macer interface {
+ Sign(sig, key []byte) ([]byte, error)
+ Equal(sig, actualMAC, key []byte) (bool, error)
+ String() string
+}
+
+var _ macer = &hmacAlgorithm{}
+
+type hmacAlgorithm struct {
+ fn func(key []byte) (hash.Hash, error)
+ kind crypto.Hash
+}
+
+func (h *hmacAlgorithm) Sign(sig, key []byte) ([]byte, error) {
+ hs, err := h.fn(key)
+ if err = setSig(hs, sig); err != nil {
+ return nil, err
+ }
+ return hs.Sum(nil), nil
+}
+
+func (h *hmacAlgorithm) Equal(sig, actualMAC, key []byte) (bool, error) {
+ hs, err := h.fn(key)
+ if err != nil {
+ return false, err
+ }
+ defer hs.Reset()
+ err = setSig(hs, sig)
+ if err != nil {
+ return false, err
+ }
+ expected := hs.Sum(nil)
+ return hmac.Equal(actualMAC, expected), nil
+}
+
+func (h *hmacAlgorithm) String() string {
+ return fmt.Sprintf("%s-%s", hmacPrefix, hashToDef[h.kind].name)
+}
+
+var _ signer = &rsaAlgorithm{}
+
+type rsaAlgorithm struct {
+ hash.Hash
+ kind crypto.Hash
+ sshSigner ssh.Signer
+}
+
+func (r *rsaAlgorithm) setSig(b []byte) error {
+ n, err := r.Write(b)
+ if err != nil {
+ r.Reset()
+ return err
+ } else if n != len(b) {
+ r.Reset()
+ return fmt.Errorf("could only write %d of %d bytes of signature to hash", n, len(b))
+ }
+ return nil
+}
+
+func (r *rsaAlgorithm) Sign(rand io.Reader, p crypto.PrivateKey, sig []byte) ([]byte, error) {
+ if r.sshSigner != nil {
+ sshsig, err := r.sshSigner.Sign(rand, sig)
+ if err != nil {
+ return nil, err
+ }
+
+ return sshsig.Blob, nil
+ }
+ defer r.Reset()
+
+ if err := r.setSig(sig); err != nil {
+ return nil, err
+ }
+ rsaK, ok := p.(*rsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("crypto.PrivateKey is not *rsa.PrivateKey")
+ }
+ return rsa.SignPKCS1v15(rand, rsaK, r.kind, r.Sum(nil))
+}
+
+func (r *rsaAlgorithm) Verify(pub crypto.PublicKey, toHash, signature []byte) error {
+ defer r.Reset()
+ rsaK, ok := pub.(*rsa.PublicKey)
+ if !ok {
+ return errors.New("crypto.PublicKey is not *rsa.PublicKey")
+ }
+ if err := r.setSig(toHash); err != nil {
+ return err
+ }
+ return rsa.VerifyPKCS1v15(rsaK, r.kind, r.Sum(nil), signature)
+}
+
+func (r *rsaAlgorithm) String() string {
+ return fmt.Sprintf("%s-%s", rsaPrefix, hashToDef[r.kind].name)
+}
+
+var _ signer = &ed25519Algorithm{}
+
+type ed25519Algorithm struct {
+ sshSigner ssh.Signer
+}
+
+func (r *ed25519Algorithm) Sign(rand io.Reader, p crypto.PrivateKey, sig []byte) ([]byte, error) {
+ if r.sshSigner != nil {
+ sshsig, err := r.sshSigner.Sign(rand, sig)
+ if err != nil {
+ return nil, err
+ }
+
+ return sshsig.Blob, nil
+ }
+ ed25519K, ok := p.(ed25519.PrivateKey)
+ if !ok {
+ return nil, errors.New("crypto.PrivateKey is not ed25519.PrivateKey")
+ }
+ return ed25519.Sign(ed25519K, sig), nil
+}
+
+func (r *ed25519Algorithm) Verify(pub crypto.PublicKey, toHash, signature []byte) error {
+ ed25519K, ok := pub.(ed25519.PublicKey)
+ if !ok {
+ return errors.New("crypto.PublicKey is not ed25519.PublicKey")
+ }
+
+ if ed25519.Verify(ed25519K, toHash, signature) {
+ return nil
+ }
+
+ return errors.New("ed25519 verify failed")
+}
+
+func (r *ed25519Algorithm) String() string {
+ return fmt.Sprintf("%s", ed25519Prefix)
+}
+
+var _ signer = &ecdsaAlgorithm{}
+
+type ecdsaAlgorithm struct {
+ hash.Hash
+ kind crypto.Hash
+}
+
+func (r *ecdsaAlgorithm) setSig(b []byte) error {
+ n, err := r.Write(b)
+ if err != nil {
+ r.Reset()
+ return err
+ } else if n != len(b) {
+ r.Reset()
+ return fmt.Errorf("could only write %d of %d bytes of signature to hash", n, len(b))
+ }
+ return nil
+}
+
+type ECDSASignature struct {
+ R, S *big.Int
+}
+
+func (r *ecdsaAlgorithm) Sign(rand io.Reader, p crypto.PrivateKey, sig []byte) ([]byte, error) {
+ defer r.Reset()
+ if err := r.setSig(sig); err != nil {
+ return nil, err
+ }
+ ecdsaK, ok := p.(*ecdsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("crypto.PrivateKey is not *ecdsa.PrivateKey")
+ }
+ R, S, err := ecdsa.Sign(rand, ecdsaK, r.Sum(nil))
+ if err != nil {
+ return nil, err
+ }
+
+ signature := ECDSASignature{R: R, S: S}
+ bytes, err := asn1.Marshal(signature)
+
+ return bytes, err
+}
+
+func (r *ecdsaAlgorithm) Verify(pub crypto.PublicKey, toHash, signature []byte) error {
+ defer r.Reset()
+ ecdsaK, ok := pub.(*ecdsa.PublicKey)
+ if !ok {
+ return errors.New("crypto.PublicKey is not *ecdsa.PublicKey")
+ }
+ if err := r.setSig(toHash); err != nil {
+ return err
+ }
+
+ sig := new(ECDSASignature)
+ _, err := asn1.Unmarshal(signature, sig)
+ if err != nil {
+ return err
+ }
+
+ if ecdsa.Verify(ecdsaK, r.Sum(nil), sig.R, sig.S) {
+ return nil
+ } else {
+ return errors.New("Invalid signature")
+ }
+}
+
+func (r *ecdsaAlgorithm) String() string {
+ return fmt.Sprintf("%s-%s", ecdsaPrefix, hashToDef[r.kind].name)
+}
+
+var _ macer = &blakeMacAlgorithm{}
+
+type blakeMacAlgorithm struct {
+ fn func(key []byte) (hash.Hash, error)
+ kind crypto.Hash
+}
+
+func (r *blakeMacAlgorithm) Sign(sig, key []byte) ([]byte, error) {
+ hs, err := r.fn(key)
+ if err != nil {
+ return nil, err
+ }
+ if err = setSig(hs, sig); err != nil {
+ return nil, err
+ }
+ return hs.Sum(nil), nil
+}
+
+func (r *blakeMacAlgorithm) Equal(sig, actualMAC, key []byte) (bool, error) {
+ hs, err := r.fn(key)
+ if err != nil {
+ return false, err
+ }
+ defer hs.Reset()
+ err = setSig(hs, sig)
+ if err != nil {
+ return false, err
+ }
+ expected := hs.Sum(nil)
+ return subtle.ConstantTimeCompare(actualMAC, expected) == 1, nil
+}
+
+func (r *blakeMacAlgorithm) String() string {
+ return fmt.Sprintf("%s", hashToDef[r.kind].name)
+}
+
+func setSig(a hash.Hash, b []byte) error {
+ n, err := a.Write(b)
+ if err != nil {
+ a.Reset()
+ return err
+ } else if n != len(b) {
+ a.Reset()
+ return fmt.Errorf("could only write %d of %d bytes of signature to hash", n, len(b))
+ }
+ return nil
+}
+
+// IsSupportedHttpSigAlgorithm returns true if the string is supported by this
+// library, is not a hash known to be weak, and is supported by the hardware.
+func IsSupportedHttpSigAlgorithm(algo string) bool {
+ a, err := isAvailable(algo)
+ return a && err == nil
+}
+
+// isAvailable is an internally public function
+func isAvailable(algo string) (bool, error) {
+ c, ok := stringToHash[algo]
+ if !ok {
+ return false, fmt.Errorf("no match for %q", algo)
+ }
+ if isForbiddenHash(c) {
+ return false, fmt.Errorf("forbidden hash type in %q", algo)
+ }
+ return c.Available(), nil
+}
+
+func newAlgorithmConstructor(algo string) (fn func(k []byte) (hash.Hash, error), c crypto.Hash, e error) {
+ ok := false
+ c, ok = stringToHash[algo]
+ if !ok {
+ e = fmt.Errorf("no match for %q", algo)
+ return
+ }
+ if isForbiddenHash(c) {
+ e = fmt.Errorf("forbidden hash type in %q", algo)
+ return
+ }
+ algoDef, ok := hashToDef[c]
+ if !ok {
+ e = fmt.Errorf("have crypto.Hash %v but no definition", c)
+ return
+ }
+ fn = func(key []byte) (hash.Hash, error) {
+ h, err := algoDef.new(key)
+ if err != nil {
+ return nil, err
+ }
+ return h, nil
+ }
+ return
+}
+
+func newAlgorithm(algo string, key []byte) (hash.Hash, crypto.Hash, error) {
+ fn, c, err := newAlgorithmConstructor(algo)
+ if err != nil {
+ return nil, c, err
+ }
+ h, err := fn(key)
+ return h, c, err
+}
+
+func signerFromSSHSigner(sshSigner ssh.Signer, s string) (signer, error) {
+ switch {
+ case strings.HasPrefix(s, rsaPrefix):
+ return &rsaAlgorithm{
+ sshSigner: sshSigner,
+ }, nil
+ case strings.HasPrefix(s, ed25519Prefix):
+ return &ed25519Algorithm{
+ sshSigner: sshSigner,
+ }, nil
+ default:
+ return nil, fmt.Errorf("no signer matching %q", s)
+ }
+}
+
+// signerFromString is an internally public method constructor
+func signerFromString(s string) (signer, error) {
+ s = strings.ToLower(s)
+ isEcdsa := false
+ isEd25519 := false
+ var algo string = ""
+ if strings.HasPrefix(s, ecdsaPrefix) {
+ algo = strings.TrimPrefix(s, ecdsaPrefix+"-")
+ isEcdsa = true
+ } else if strings.HasPrefix(s, rsaPrefix) {
+ algo = strings.TrimPrefix(s, rsaPrefix+"-")
+ } else if strings.HasPrefix(s, ed25519Prefix) {
+ isEd25519 = true
+ algo = "sha512"
+ } else {
+ return nil, fmt.Errorf("no signer matching %q", s)
+ }
+ hash, cHash, err := newAlgorithm(algo, nil)
+ if err != nil {
+ return nil, err
+ }
+ if isEd25519 {
+ return &ed25519Algorithm{}, nil
+ }
+ if isEcdsa {
+ return &ecdsaAlgorithm{
+ Hash: hash,
+ kind: cHash,
+ }, nil
+ }
+ return &rsaAlgorithm{
+ Hash: hash,
+ kind: cHash,
+ }, nil
+}
+
+// macerFromString is an internally public method constructor
+func macerFromString(s string) (macer, error) {
+ s = strings.ToLower(s)
+ if strings.HasPrefix(s, hmacPrefix) {
+ algo := strings.TrimPrefix(s, hmacPrefix+"-")
+ hashFn, cHash, err := newAlgorithmConstructor(algo)
+ if err != nil {
+ return nil, err
+ }
+ // Ensure below does not panic
+ _, err = hashFn(nil)
+ if err != nil {
+ return nil, err
+ }
+ return &hmacAlgorithm{
+ fn: func(key []byte) (hash.Hash, error) {
+ return hmac.New(func() hash.Hash {
+ h, e := hashFn(nil)
+ if e != nil {
+ panic(e)
+ }
+ return h
+ }, key), nil
+ },
+ kind: cHash,
+ }, nil
+ } else if bl, ok := stringToHash[s]; ok && blake2Algorithms[bl] {
+ hashFn, cHash, err := newAlgorithmConstructor(s)
+ if err != nil {
+ return nil, err
+ }
+ return &blakeMacAlgorithm{
+ fn: hashFn,
+ kind: cHash,
+ }, nil
+ } else {
+ return nil, fmt.Errorf("no MACer matching %q", s)
+ }
+}