2019-02-07 09:04:29 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package pkcrypto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto"
|
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
2019-02-07 20:39:20 +00:00
|
|
|
"crypto/rsa"
|
|
|
|
"math/big"
|
|
|
|
"reflect"
|
2019-02-07 09:04:29 +00:00
|
|
|
)
|
|
|
|
|
2019-02-07 20:39:20 +00:00
|
|
|
const (
|
|
|
|
// StorjPSSSaltLength holds the correct value for the PSS salt length
|
|
|
|
// when signing with RSA in Storj code and verifying RSA signatures
|
|
|
|
// from Storj.
|
|
|
|
StorjPSSSaltLength = rsa.PSSSaltLengthAuto
|
|
|
|
|
|
|
|
// StorjRSAKeyBits holds the number of bits to use for new RSA keys
|
|
|
|
// by default.
|
|
|
|
StorjRSAKeyBits = 2048
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
authECCurve = elliptic.P256()
|
|
|
|
|
|
|
|
pssParams = rsa.PSSOptions{
|
|
|
|
SaltLength: StorjPSSSaltLength,
|
|
|
|
Hash: crypto.SHA256,
|
|
|
|
}
|
|
|
|
)
|
2019-02-07 09:04:29 +00:00
|
|
|
|
|
|
|
// GeneratePrivateKey returns a new PrivateKey for signing messages
|
2019-02-07 20:39:20 +00:00
|
|
|
func GeneratePrivateKey() (crypto.PrivateKey, error) {
|
|
|
|
return GeneratePrivateECDSAKey(authECCurve)
|
|
|
|
// return GeneratePrivateRSAKey(StorjRSAKeyBits)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GeneratePrivateECDSAKey returns a new private ECDSA key for signing messages
|
|
|
|
func GeneratePrivateECDSAKey(curve elliptic.Curve) (*ecdsa.PrivateKey, error) {
|
|
|
|
return ecdsa.GenerateKey(curve, rand.Reader)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GeneratePrivateRSAKey returns a new private RSA key for signing messages
|
|
|
|
func GeneratePrivateRSAKey(bits int) (*rsa.PrivateKey, error) {
|
|
|
|
return rsa.GenerateKey(rand.Reader, bits)
|
2019-02-07 09:04:29 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 20:39:20 +00:00
|
|
|
// HashAndVerifySignature checks that signature was made by the private key
|
|
|
|
// corresponding to the given public key, over a SHA-256 digest of the given
|
|
|
|
// data. It returns an error if verification fails, or nil otherwise.
|
|
|
|
func HashAndVerifySignature(key crypto.PublicKey, data, signature []byte) error {
|
|
|
|
digest := SHA256Hash(data)
|
|
|
|
return VerifySignatureWithoutHashing(key, digest, signature)
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifySignatureWithoutHashing checks the signature against the passed data
|
|
|
|
// (which is normally a digest) and public key. It returns an error if
|
|
|
|
// verification fails, or nil otherwise.
|
|
|
|
func VerifySignatureWithoutHashing(pubKey crypto.PublicKey, digest, signature []byte) error {
|
|
|
|
switch key := pubKey.(type) {
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
return verifyECDSASignatureWithoutHashing(key, digest, signature)
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
return verifyRSASignatureWithoutHashing(key, digest, signature)
|
2019-02-07 09:04:29 +00:00
|
|
|
}
|
2019-02-07 20:39:20 +00:00
|
|
|
return ErrUnsupportedKey.New("%T", pubKey)
|
|
|
|
}
|
2019-02-07 09:04:29 +00:00
|
|
|
|
2019-02-07 20:39:20 +00:00
|
|
|
func verifyECDSASignatureWithoutHashing(pubKey *ecdsa.PublicKey, digest, signatureBytes []byte) error {
|
|
|
|
r, s, err := unmarshalECDSASignature(signatureBytes)
|
2019-02-07 18:40:28 +00:00
|
|
|
if err != nil {
|
2019-02-07 09:04:29 +00:00
|
|
|
return ErrVerifySignature.New("unable to unmarshal ecdsa signature: %v", err)
|
|
|
|
}
|
2019-02-07 20:39:20 +00:00
|
|
|
if !ecdsa.Verify(pubKey, digest, r, s) {
|
|
|
|
return ErrVerifySignature.New("signature is not valid")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func verifyRSASignatureWithoutHashing(pubKey *rsa.PublicKey, digest, signatureBytes []byte) error {
|
|
|
|
err := rsa.VerifyPSS(pubKey, pssParams.Hash, digest, signatureBytes, &pssParams)
|
|
|
|
if err != nil {
|
2019-02-07 09:04:29 +00:00
|
|
|
return ErrVerifySignature.New("signature is not valid")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-02-07 20:39:20 +00:00
|
|
|
// PublicKeyFromPrivate returns the public key corresponding to a given private
|
|
|
|
// key.
|
|
|
|
func PublicKeyFromPrivate(privKey crypto.PrivateKey) crypto.PublicKey {
|
|
|
|
switch key := privKey.(type) {
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
return key.Public()
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
return key.Public()
|
|
|
|
}
|
|
|
|
return ErrUnsupportedKey.New("%T", privKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SignWithoutHashing signs the given digest with the private key and returns
|
|
|
|
// the new signature.
|
|
|
|
func SignWithoutHashing(privKey crypto.PrivateKey, digest []byte) ([]byte, error) {
|
|
|
|
switch key := privKey.(type) {
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
return signECDSAWithoutHashing(key, digest)
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
return signRSAWithoutHashing(key, digest)
|
2019-02-07 09:04:29 +00:00
|
|
|
}
|
2019-02-07 20:39:20 +00:00
|
|
|
return nil, ErrUnsupportedKey.New("%T", privKey)
|
|
|
|
}
|
2019-02-07 09:04:29 +00:00
|
|
|
|
2019-02-07 20:39:20 +00:00
|
|
|
func signECDSAWithoutHashing(privKey *ecdsa.PrivateKey, digest []byte) ([]byte, error) {
|
|
|
|
r, s, err := ecdsa.Sign(rand.Reader, privKey, digest)
|
2019-02-07 09:04:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrSign.Wrap(err)
|
|
|
|
}
|
2019-02-07 18:40:28 +00:00
|
|
|
return marshalECDSASignature(r, s)
|
2019-02-07 09:04:29 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 20:39:20 +00:00
|
|
|
func signRSAWithoutHashing(privKey *rsa.PrivateKey, digest []byte) ([]byte, error) {
|
|
|
|
return privKey.Sign(rand.Reader, digest, &pssParams)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HashAndSign signs a SHA-256 digest of the given data and returns the new
|
2019-02-07 09:04:29 +00:00
|
|
|
// signature.
|
2019-02-07 20:39:20 +00:00
|
|
|
func HashAndSign(key crypto.PrivateKey, data []byte) ([]byte, error) {
|
|
|
|
digest := SHA256Hash(data)
|
|
|
|
signature, err := SignWithoutHashing(key, digest)
|
2019-02-07 09:04:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrSign.Wrap(err)
|
|
|
|
}
|
|
|
|
return signature, nil
|
|
|
|
}
|
2019-02-07 20:39:20 +00:00
|
|
|
|
|
|
|
// PublicKeyEqual returns true if two public keys are the same.
|
|
|
|
func PublicKeyEqual(a, b crypto.PublicKey) bool {
|
|
|
|
switch aConcrete := a.(type) {
|
|
|
|
case *ecdsa.PublicKey:
|
|
|
|
bConcrete, ok := b.(*ecdsa.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return publicECDSAKeyEqual(aConcrete, bConcrete)
|
|
|
|
case *rsa.PublicKey:
|
|
|
|
bConcrete, ok := b.(*rsa.PublicKey)
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return publicRSAKeyEqual(aConcrete, bConcrete)
|
|
|
|
}
|
|
|
|
// a best-effort here is probably better than adding an err return
|
|
|
|
return reflect.DeepEqual(a, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// publicECDSAKeyEqual returns true if two ECDSA public keys are the same.
|
|
|
|
func publicECDSAKeyEqual(a, b *ecdsa.PublicKey) bool {
|
|
|
|
return a.Curve == b.Curve && bigIntEq(a.X, b.X) && bigIntEq(a.Y, b.Y)
|
|
|
|
}
|
|
|
|
|
|
|
|
// publicRSAKeyEqual returns true if two RSA public keys are the same.
|
|
|
|
func publicRSAKeyEqual(a, b *rsa.PublicKey) bool {
|
|
|
|
return bigIntEq(a.N, b.N) && a.E == b.E
|
|
|
|
}
|
|
|
|
|
|
|
|
func bigIntEq(a, b *big.Int) bool {
|
|
|
|
return a.Cmp(b) == 0
|
|
|
|
}
|