crypto/ecdsa.go

199 lines
5.1 KiB
Go

package crypto
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"errors"
"io"
"apigo.cc/go/safe"
"golang.org/x/crypto/hkdf"
)
type ECDSAAlgorithm struct {
UseGCM bool
KdfInfo []byte
KdfSalt []byte
Hash crypto.Hash
}
var (
ECDSAGCM = &ECDSAAlgorithm{UseGCM: true, Hash: crypto.SHA256}
ECDSACBC = &ECDSAAlgorithm{UseGCM: false, Hash: crypto.SHA256}
)
func NewECDSA(safePrivateKeyBuf, safePublicKeyBuf *safe.SafeBuf) (*Asymmetric, error) {
return NewAsymmetric(ECDSAGCM, safePrivateKeyBuf, safePublicKeyBuf)
}
func NewECDSAndEraseKey(safePrivateKeyBuf, safePublicKeyBuf []byte) (*Asymmetric, error) {
return NewAsymmetricAndEraseKey(ECDSAGCM, safePrivateKeyBuf, safePublicKeyBuf)
}
func NewECDSAWithOutEraseKey(safePrivateKeyBuf, safePublicKeyBuf []byte) (*Asymmetric, error) {
return NewAsymmetricWithoutEraseKey(ECDSAGCM, safePrivateKeyBuf, safePublicKeyBuf, false)
}
func NewECDSAAlgorithm(useGCM bool, hash crypto.Hash, kdfInfo, kdfSalt []byte) *ECDSAAlgorithm {
return &ECDSAAlgorithm{UseGCM: useGCM, Hash: hash, KdfInfo: kdfInfo, KdfSalt: kdfSalt}
}
func GenerateECDSAKeyPair(bitSize int) ([]byte, []byte, error) {
var curve elliptic.Curve
switch bitSize {
case 256:
curve = elliptic.P256()
case 384:
curve = elliptic.P384()
default:
curve = elliptic.P521()
}
priKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, err
}
privateKey, err := x509.MarshalECPrivateKey(priKey)
if err != nil {
return nil, nil, err
}
publicKey, err := x509.MarshalPKIXPublicKey(&priKey.PublicKey)
if err != nil {
return nil, nil, err
}
return privateKey, publicKey, nil
}
func (e *ECDSAAlgorithm) ParsePrivateKey(der []byte) (any, error) {
return x509.ParseECPrivateKey(der)
}
func (e *ECDSAAlgorithm) ParsePublicKey(der []byte) (any, error) {
pubKeyAny, err := x509.ParsePKIXPublicKey(der)
if err != nil {
return nil, err
}
pubKey, ok := pubKeyAny.(*ecdsa.PublicKey)
if !ok {
return nil, errors.New("not an ECDSA public key")
}
return pubKey, nil
}
func (e *ECDSAAlgorithm) Sign(privateKeyObj any, data []byte, hash ...crypto.Hash) ([]byte, error) {
privKey, ok := privateKeyObj.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("invalid private key")
}
hFunc := e.Hash
if len(hash) > 0 {
hFunc = hash[0]
}
if hFunc == 0 {
hFunc = crypto.SHA256
}
hasher := hFunc.New()
hasher.Write(data)
return ecdsa.SignASN1(rand.Reader, privKey, hasher.Sum(nil))
}
func (e *ECDSAAlgorithm) Verify(publicKeyObj any, data []byte, signature []byte, hash ...crypto.Hash) (bool, error) {
pubKey, ok := publicKeyObj.(*ecdsa.PublicKey)
if !ok {
return false, errors.New("invalid public key")
}
hFunc := e.Hash
if len(hash) > 0 {
hFunc = hash[0]
}
if hFunc == 0 {
hFunc = crypto.SHA256
}
hasher := hFunc.New()
hasher.Write(data)
return ecdsa.VerifyASN1(pubKey, hasher.Sum(nil), signature), nil
}
func (e *ECDSAAlgorithm) Encrypt(publicKeyObj any, data []byte) ([]byte, error) {
ecdsaPub, ok := publicKeyObj.(*ecdsa.PublicKey)
if !ok {
return nil, errors.New("invalid public key type for ECDSA")
}
ecdhPub, err := ecdsaPub.ECDH()
if err != nil {
return nil, err
}
ephemeralPriv, err := ecdhPub.Curve().GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
sharedSecret, err := ephemeralPriv.ECDH(ecdhPub)
if err != nil {
return nil, err
}
defer safe.ZeroMemory(sharedSecret)
hkdfReader := hkdf.New(e.Hash.New, sharedSecret, e.KdfSalt, e.KdfInfo)
aesKey := make([]byte, 32)
if _, err := io.ReadFull(hkdfReader, aesKey); err != nil {
return nil, err
}
defer safe.ZeroMemory(aesKey)
cipherAlgo := &AESCipher{useGCM: e.UseGCM}
ivLen := 16
if e.UseGCM {
ivLen = 12
}
iv := safe.MakeSafeToken(ivLen)
cipherText, err := cipherAlgo.Encrypt(data, aesKey, iv)
if err != nil {
return nil, err
}
pubBytes := ephemeralPriv.PublicKey().Bytes()
out := make([]byte, 0, len(pubBytes)+len(iv)+len(cipherText))
out = append(out, pubBytes...)
out = append(out, iv...)
out = append(out, cipherText...)
return out, nil
}
func (e *ECDSAAlgorithm) Decrypt(privateKeyObj any, data []byte) ([]byte, error) {
ecdsaPriv, ok := privateKeyObj.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("invalid private key type for ECDSA")
}
ecdhPriv, err := ecdsaPriv.ECDH()
if err != nil {
return nil, err
}
pubKeyLen := len(ecdhPriv.PublicKey().Bytes())
ivLen := 16
if e.UseGCM {
ivLen = 12
}
if len(data) < pubKeyLen+ivLen {
return nil, errors.New("invalid ciphertext package size")
}
ephemeralPubBytes := data[:pubKeyLen]
iv := data[pubKeyLen : pubKeyLen+ivLen]
cipherText := data[pubKeyLen+ivLen:]
ephemeralPub, err := ecdhPriv.Curve().NewPublicKey(ephemeralPubBytes)
if err != nil {
return nil, err
}
sharedSecret, err := ecdhPriv.ECDH(ephemeralPub)
if err != nil {
return nil, err
}
defer safe.ZeroMemory(sharedSecret)
hkdfReader := hkdf.New(e.Hash.New, sharedSecret, e.KdfSalt, e.KdfInfo)
aesKey := make([]byte, 32)
if _, err := io.ReadFull(hkdfReader, aesKey); err != nil {
return nil, err
}
defer safe.ZeroMemory(aesKey)
cipherAlgo := &AESCipher{useGCM: e.UseGCM}
return cipherAlgo.Decrypt(cipherText, aesKey, iv)
}