Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ECDSA and Denom dependencies #7

Merged
merged 5 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.21
require (
github.com/bwesterb/go-ristretto v1.2.3
github.com/coinbase/kryptology v1.8.0
github.com/ethereum/go-ethereum v1.13.15
github.com/gtank/merlin v0.1.1
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.27.0
Expand All @@ -16,6 +15,7 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
7 changes: 3 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo=
github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU=
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand All @@ -24,10 +21,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sei-protocol/coinbase-kryptology v0.0.0-20241015231206-08f61b7965cd h1:R/g4pa6pgegLAAt1NTrO1qVJ3uZH9hfcMcc4yLz1cgg=
Expand Down
28 changes: 8 additions & 20 deletions pkg/encryption/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package encryption
import (
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
Expand All @@ -12,33 +11,22 @@ import (
"io"
"math/big"

"github.com/ethereum/go-ethereum/crypto/secp256k1"

"golang.org/x/crypto/hkdf"
)

// GenerateKey generates a new ECDSA private key using the secp256k1 curve.
func GenerateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
}

// GetAESKey derives a 32-byte AES key using the provided ECDSA private key and denomination string.
// It employs HKDF with SHA-256, using the ECDSA private key bytes and a SHA-256 hash of the denom as salt.
func GetAESKey(privKey ecdsa.PrivateKey, denom string) ([]byte, error) {
if privKey.D == nil {
return nil, fmt.Errorf("private key D is nil")
}
if len(denom) == 0 {
return nil, fmt.Errorf("denom is empty")
// GetAESKey derives a 32-byte AES key using the provided bytes.
// The bytes can be anything, but we strongly suggest using something that is private to the use, such as the ecdas Private Key or a signed message.
// It employs HKDF with SHA-256, using the private key bytes.
func GetAESKey(privateBytes []byte) ([]byte, error) {
if len(privateBytes) == 0 {
return nil, fmt.Errorf("bytes is empty")
}
// Convert the ECDSA private key to bytes
privKeyBytes := privKey.D.Bytes()

// Use a SHA-256 hash of the denom string as the salt
salt := sha256.Sum256([]byte(denom))
salt := sha256.Sum256([]byte("aes key derivation salt"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, if making salt effectively global, makes key generation less secure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this actually doesn't actually add any security. Replaced to nil.

In our case we are going to pass the hashed, then signed denom as the privateBytes, so I think it should be good enough.

Added a comment to explain that the user is responsible for ensuring that the secret passed in (privateBytes) are salted or hashed beforehand.


// Create an HKDF reader using SHA-256
hkdf := hkdf.New(sha256.New, privKeyBytes, salt[:], []byte("aes key derivation"))
hkdf := hkdf.New(sha256.New, privateBytes, salt[:], []byte("aes key derivation"))

// Allocate a 32-byte array for the AES key
aesKey := make([]byte, 32)
Expand Down
55 changes: 13 additions & 42 deletions pkg/encryption/aes_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package encryption

import (
"crypto/ecdsa"
"math/big"
"testing"
"time"

"github.com/stretchr/testify/require"
)
Expand All @@ -16,57 +16,37 @@ const (
func TestGetAESKey(t *testing.T) {
tests := []struct {
name string
privateKey *ecdsa.PrivateKey
privateKey []byte
denom string
expectEqual bool
anotherKey *ecdsa.PrivateKey
anotherKey []byte
anotherDenom string
}{
{
name: "Deterministic Key Generation",
privateKey: generateTestKey(t),
denom: TestDenom,
privateKey: generateTestKey(),
expectEqual: true,
},
{
name: "Different Denom (Salt) Generates Different Key",
privateKey: generateTestKey(t),
denom: TestDenom,
anotherDenom: TestDenom + "1",
expectEqual: false,
},
{
name: "Different Denom (Salt) of same length Generates Different Key",
privateKey: generateTestKey(t),
denom: TestDenom + "1",
anotherDenom: TestDenom + "2",
expectEqual: false,
},
{
name: "Different PrivateKey Generates Different Key",
privateKey: generateTestKey(t),
denom: TestDenom + "N",
anotherKey: generateTestKey(t),
privateKey: generateTestKey(),
anotherKey: generateTestKey(),
expectEqual: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
aesPK, err := GetAESKey(*tt.privateKey, tt.denom)
aesPK, err := GetAESKey(tt.privateKey)
require.Nil(t, err, "Should not have error here")

if tt.anotherKey != nil {
aesPKDiff, err := GetAESKey(*tt.anotherKey, tt.denom)
aesPKDiff, err := GetAESKey(tt.anotherKey)
require.Nil(t, err)
require.NotEqual(t, aesPK, aesPKDiff, "PK should be different for different private keys")
} else if tt.anotherDenom != "" {
aesPKDiff, err := GetAESKey(*tt.privateKey, tt.anotherDenom)
require.Nil(t, err)
require.NotEqual(t, aesPK, aesPKDiff, "PK should be different for different salts")
} else {

aesPKAgain, err := GetAESKey(*tt.privateKey, tt.denom)
aesPKAgain, err := GetAESKey(tt.privateKey)
require.Nil(t, err, "Should not have error here")
if tt.expectEqual {
require.Equal(t, aesPK, aesPKAgain, "PK should be deterministically generated")
Expand All @@ -80,16 +60,8 @@ func TestGetAESKey(t *testing.T) {

func TestGetAESKey_InvalidInput(t *testing.T) {
// Nil private key
_, err := GetAESKey(*new(ecdsa.PrivateKey), TestDenom)
_, err := GetAESKey([]byte{})
require.Error(t, err, "Should return error for nil private key")

invalidPrivateKey := &ecdsa.PrivateKey{ /* Invalid key data */ }
_, err = GetAESKey(*invalidPrivateKey, TestDenom)
require.Error(t, err, "Should return error for invalid private key")

validPrivateKey := generateTestKey(t)
_, err = GetAESKey(*validPrivateKey, "")
require.Error(t, err, "Should not allow empty denom(salt)")
}

func TestAESEncryptionDecryption(t *testing.T) {
Expand Down Expand Up @@ -218,8 +190,7 @@ func TestDecryptAESGCM_InvalidCiphertext(t *testing.T) {
}

// Helper function to generate a test private key
func generateTestKey(t *testing.T) *ecdsa.PrivateKey {
privateKey, err := GenerateKey()
require.Nil(t, err, "Failed to generate private key")
return privateKey
func generateTestKey() []byte {
randomString := time.Now()
return []byte(randomString.String())
}
17 changes: 6 additions & 11 deletions pkg/encryption/elgamal/common.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package elgamal

import (
"crypto/ecdsa"
"crypto/sha256"
"io"

Expand All @@ -13,11 +12,12 @@ import (
const H_STRING = "gPt25pi0eDphSiXWu0BIeIvyVATCtwhslTqfqvNhW2c"

// KeyGen generates a new key pair for the Twisted ElGamal encryption scheme.
func (teg TwistedElGamal) KeyGen(privateKey ecdsa.PrivateKey, denom string) (*KeyPair, error) {
// The private key is derived from the provided privateBytes and denom string. Ensure that the privateBytes passed is not exposed.
func (teg TwistedElGamal) KeyGen(privateBytes []byte) (*KeyPair, error) {
// Fixed base point H
H := teg.GetH()

s, err := teg.getPrivateKey(privateKey, denom)
s, err := teg.getPrivateKeyFromBytes(privateBytes)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -47,17 +47,12 @@ func (teg TwistedElGamal) GetH() curves.Point {
return teg.curve.Point.Hash(bytes)
}

// getPrivateKey derives a private key for the ElGamal cryptosystem.
// It takes an ECDSA private key and a denomination string to generate the scalar.
func (teg TwistedElGamal) getPrivateKey(privateKey ecdsa.PrivateKey, denom string) (curves.Scalar, error) {
// Convert the ECDSA private key to bytes
privKeyBytes := privateKey.D.Bytes()

func (teg TwistedElGamal) getPrivateKeyFromBytes(privateBytes []byte) (curves.Scalar, error) {
// Hash the denom to get a salt.
salt := sha256.Sum256([]byte(denom))
salt := sha256.Sum256([]byte("elgamal scalar derivation salt"))

// Create an HKDF reader using SHA-256
hkdf := hkdf.New(sha256.New, privKeyBytes, salt[:], []byte("elgamal scalar derivation"))
hkdf := hkdf.New(sha256.New, privateBytes, salt[:], []byte("elgamal scalar derivation"))

// Generate 64 bytes of randomness from HKDF output
var scalarBytes [64]byte
Expand Down
Loading
Loading