storj/pkg/pkcrypto/signing.go

172 lines
5.2 KiB
Go
Raw Normal View History

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package pkcrypto
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"math/big"
"reflect"
)
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,
}
)
// GeneratePrivateKey returns a new PrivateKey for signing messages
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)
}
// 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)
}
return ErrUnsupportedKey.New("%T", pubKey)
}
func verifyECDSASignatureWithoutHashing(pubKey *ecdsa.PublicKey, digest, signatureBytes []byte) error {
r, s, err := unmarshalECDSASignature(signatureBytes)
if err != nil {
return ErrVerifySignature.New("unable to unmarshal ecdsa signature: %v", err)
}
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 {
return ErrVerifySignature.New("signature is not valid")
}
return nil
}
// 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)
}
return nil, ErrUnsupportedKey.New("%T", privKey)
}
func signECDSAWithoutHashing(privKey *ecdsa.PrivateKey, digest []byte) ([]byte, error) {
r, s, err := ecdsa.Sign(rand.Reader, privKey, digest)
if err != nil {
return nil, ErrSign.Wrap(err)
}
return marshalECDSASignature(r, s)
}
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
// signature.
func HashAndSign(key crypto.PrivateKey, data []byte) ([]byte, error) {
digest := SHA256Hash(data)
signature, err := SignWithoutHashing(key, digest)
if err != nil {
return nil, ErrSign.Wrap(err)
}
return signature, nil
}
// 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
}