Skip to content

Commit 18d54f9

Browse files
committed
update ecies to use binary, add helpers for strings, fix lint issues
1 parent c8343f2 commit 18d54f9

File tree

4 files changed

+72
-155
lines changed

4 files changed

+72
-155
lines changed

compat/ecies/ecies.go

Lines changed: 54 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package compat
22

33
import (
44
"crypto/hmac"
5-
"log"
5+
"encoding/base64"
66
"math/big"
77
"reflect"
88

9-
"encoding/base64"
109
"errors"
1110

1211
ecies "github.com/bitcoin-sv/go-sdk/primitives/aescbc"
@@ -20,11 +19,20 @@ import (
2019

2120
func EncryptSingle(message string, privateKey *ec.PrivateKey) (string, error) {
2221
messageBytes := []byte(message)
23-
return ElectrumEncrypt(messageBytes, privateKey.PubKey(), privateKey, false)
22+
23+
decryptedBytes, err := ElectrumEncrypt(messageBytes, privateKey.PubKey(), privateKey, false)
24+
if err != nil {
25+
return "", err
26+
}
27+
return base64.StdEncoding.EncodeToString(decryptedBytes), nil
2428
}
2529

2630
func DecryptSingle(encryptedData string, privateKey *ec.PrivateKey) (string, error) {
27-
plainBytes, err := ElectrumDecrypt(encryptedData, privateKey, nil)
31+
encryptedBytes, err := base64.StdEncoding.DecodeString(encryptedData)
32+
if err != nil {
33+
return "", err
34+
}
35+
plainBytes, err := ElectrumDecrypt(encryptedBytes, privateKey, nil)
2836
if err != nil {
2937
return "", err
3038
}
@@ -33,18 +41,30 @@ func DecryptSingle(encryptedData string, privateKey *ec.PrivateKey) (string, err
3341

3442
func EncryptShared(message string, toPublicKey *ec.PublicKey, fromPrivateKey *ec.PrivateKey) (string, error) {
3543
messageBytes := []byte(message)
36-
return ElectrumEncrypt(messageBytes, toPublicKey, nil, false)
44+
decryptedBytes, err := ElectrumEncrypt(messageBytes, toPublicKey, nil, false)
45+
if err != nil {
46+
return "", err
47+
}
48+
return base64.StdEncoding.EncodeToString(decryptedBytes), nil
3749
}
3850

3951
func DecryptShared(encryptedData string, toPrivateKey *ec.PrivateKey, fromPublicKey *ec.PublicKey) (string, error) {
40-
plainBytes, err := ElectrumDecrypt(encryptedData, toPrivateKey, fromPublicKey)
52+
encryptedBytes, err := base64.StdEncoding.DecodeString(encryptedData)
53+
if err != nil {
54+
return "", err
55+
}
56+
plainBytes, err := ElectrumDecrypt(encryptedBytes, toPrivateKey, fromPublicKey)
4157
if err != nil {
4258
return "", err
4359
}
4460
return string(plainBytes), nil
4561
}
4662

47-
func ElectrumEncrypt(message []byte, toPublicKey *ec.PublicKey, fromPrivateKey *ec.PrivateKey, noKey bool) (string, error) {
63+
func ElectrumEncrypt(message []byte,
64+
toPublicKey *ec.PublicKey,
65+
fromPrivateKey *ec.PrivateKey,
66+
noKey bool,
67+
) ([]byte, error) {
4868
// Generate an ephemeral EC private key if fromPrivateKey is nil
4969
var ephemeralPrivateKey *ec.PrivateKey
5070
if fromPrivateKey == nil {
@@ -64,7 +84,7 @@ func ElectrumEncrypt(message []byte, toPublicKey *ec.PublicKey, fromPrivateKey *
6484
// AES encryption
6585
cipherText, err := ecies.AESCBCEncrypt(message, keyE, iv, false)
6686
if err != nil {
67-
return "", err
87+
return nil, err
6888
}
6989

7090
ephemeralPublicKey := ephemeralPrivateKey.PubKey()
@@ -79,52 +99,48 @@ func ElectrumEncrypt(message []byte, toPublicKey *ec.PublicKey, fromPrivateKey *
7999

80100
mac := c.Sha256HMAC(encrypted, keyM)
81101

82-
return base64.StdEncoding.EncodeToString(append(encrypted, mac...)), nil
102+
return append(encrypted, mac...), nil
83103
}
84-
func ElectrumDecrypt(encryptedData string, toPrivateKey *ec.PrivateKey, fromPublicKey *ec.PublicKey) ([]byte, error) {
85-
encrypted, err := base64.StdEncoding.DecodeString(encryptedData)
86-
if err != nil {
87-
return nil, err
88-
}
89-
if len(encrypted) < 52 { // Minimum length: 4 (magic) + 16 (min cipher) + 32 (mac)
104+
func ElectrumDecrypt(encryptedData []byte, toPrivateKey *ec.PrivateKey, fromPublicKey *ec.PublicKey) ([]byte, error) {
105+
106+
if len(encryptedData) < 52 { // Minimum length: 4 (magic) + 16 (min cipher) + 32 (mac)
90107
return nil, errors.New("invalid encrypted text: length")
91108
}
92-
magic := encrypted[:4]
109+
magic := encryptedData[:4]
93110
if string(magic) != "BIE1" {
94111
return nil, errors.New("invalid cipher text: invalid magic bytes")
95112
}
96113

97114
var sharedSecret []byte
98115
var cipherText []byte
99-
var ephemeralPublicKey *ec.PublicKey
100116

101117
if fromPublicKey != nil {
102118
// Use counterparty public key to derive shared secret
103119
x, y := toPrivateKey.Curve.ScalarMult(fromPublicKey.X, fromPublicKey.Y, toPrivateKey.D.Bytes())
104120
sharedSecret = (&ec.PublicKey{X: x, Y: y}).SerializeCompressed()
105-
if len(encrypted) > 69 { // 4 (magic) + 33 (pubkey) + 32 (mac)
106-
cipherText = encrypted[37 : len(encrypted)-32]
121+
if len(encryptedData) > 69 { // 4 (magic) + 33 (pubkey) + 32 (mac)
122+
cipherText = encryptedData[37 : len(encryptedData)-32]
107123
} else {
108-
cipherText = encrypted[4 : len(encrypted)-32]
124+
cipherText = encryptedData[4 : len(encryptedData)-32]
109125
}
110126
} else {
111127
// Use ephemeral public key to derive shared secret
112-
ephemeralPublicKey, err = ec.ParsePubKey(encrypted[4:37])
128+
ephemeralPublicKey, err := ec.ParsePubKey(encryptedData[4:37])
113129
if err != nil {
114130
return nil, err
115131
}
116132
x, y := ephemeralPublicKey.Curve.ScalarMult(ephemeralPublicKey.X, ephemeralPublicKey.Y, toPrivateKey.D.Bytes())
117133
sharedSecret = (&ec.PublicKey{X: x, Y: y}).SerializeCompressed()
118-
cipherText = encrypted[37 : len(encrypted)-32]
134+
cipherText = encryptedData[37 : len(encryptedData)-32]
119135
}
120136

121137
// Derive key_e, iv and key_m
122138
key := c.Sha512(sharedSecret)
123139
iv, keyE, keyM := key[0:16], key[16:32], key[32:]
124140

125141
// Verify mac
126-
mac := encrypted[len(encrypted)-32:]
127-
macRecalculated := c.Sha256HMAC(encrypted[:len(encrypted)-32], keyM)
142+
mac := encryptedData[len(encryptedData)-32:]
143+
macRecalculated := c.Sha256HMAC(encryptedData[:len(encryptedData)-32], keyM)
128144
if !reflect.DeepEqual(mac, macRecalculated) {
129145
return nil, errors.New("incorrect password")
130146
}
@@ -134,69 +150,17 @@ func ElectrumDecrypt(encryptedData string, toPrivateKey *ec.PrivateKey, fromPubl
134150
if err != nil {
135151
return nil, err
136152
}
137-
log.Printf("IV: %x, plain text: %x", iv, plain)
138153
return plain, nil
139154
}
140155

141-
// func BitcoreEncrypt(message []byte, publicKey *ec.PublicKey) (string, error) {
142-
// // Generate an ephemeral EC private key
143-
// ephemeralPrivateKey, err := ec.NewPrivateKey()
144-
// if err != nil {
145-
// return "", err
146-
// }
147-
148-
// // Derive shared secret
149-
// x, _ := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, ephemeralPrivateKey.D.Bytes())
150-
// sharedSecret := x.Bytes()
151-
152-
// // Key derivation
153-
// keyMaterial := c.Sha512(sharedSecret)
154-
155-
// keyE := keyMaterial[:32] // AES-256 key
156-
// keyM := keyMaterial[32:] // HMAC key
157-
// iv := make([]byte, 16) // IV for AES (all zeros in Bitcore)
158-
159-
// // Encrypt the message
160-
// cipherText, err := aescbc.AESEncryptWithIV(message, keyE, iv)
161-
// if err != nil {
162-
// return "", err
163-
// }
164-
165-
// // Prepare the output
166-
// ephemeralPublicKey := ephemeralPrivateKey.PubKey().SerializeCompressed()
167-
// encryptedData := append(ephemeralPublicKey, cipherText...)
168-
169-
// // Calculate HMAC
170-
// hmacSum := c.Sha256HMAC(encryptedData, keyM)
171-
172-
// // Combine all parts
173-
// result := append(encryptedData, hmacSum...)
174-
175-
// return base64.StdEncoding.EncodeToString(result), nil
176-
// }
177-
178-
func BitcoreEncrypt(message []byte, toPublicKey *ec.PublicKey, fromPrivateKey *ec.PrivateKey, iv []byte) (string, error) {
179-
180-
// JS Implementation
181-
// if (!fromPrivateKey) {
182-
// fromPrivateKey = PrivateKey.fromRandom()
183-
// }
184-
// const r = fromPrivateKey
185-
// const RPublicKey = fromPrivateKey.toPublicKey()
186-
// const RBuf = RPublicKey.encode(true) as number[]
187-
// const KB = toPublicKey
188-
// const P = KB.mul(r)
189-
// const S = P.getX()
190-
// const Sbuf = S.toArray('be', 32)
191-
// const kEkM = Hash.sha512(Sbuf)
192-
// const kE = kEkM.slice(0, 32)
193-
// const kM = kEkM.slice(32, 64)
194-
// const c = AESCBC.encrypt(messageBuf, kE, ivBuf)
195-
// const d = Hash.sha256hmac(kM, [...c])
196-
// const encBuf = [...RBuf, ...c, ...d]
197-
// return encBuf
198-
// If IV is not provided, generate a random one
156+
// BitcoreEncrypt encrypts a message using ECIES
157+
func BitcoreEncrypt(message []byte,
158+
toPublicKey *ec.PublicKey,
159+
fromPrivateKey *ec.PrivateKey,
160+
iv []byte,
161+
) ([]byte, error) {
199162

163+
// If IV is not provided, fill it with zeros
200164
if iv == nil {
201165
iv = make([]byte, 16)
202166
}
@@ -206,7 +170,7 @@ func BitcoreEncrypt(message []byte, toPublicKey *ec.PublicKey, fromPrivateKey *e
206170
var err error
207171
fromPrivateKey, err = ec.NewPrivateKey()
208172
if err != nil {
209-
return "", err
173+
return nil, err
210174
}
211175
}
212176

@@ -219,45 +183,18 @@ func BitcoreEncrypt(message []byte, toPublicKey *ec.PublicKey, fromPrivateKey *e
219183
kM := kEkM[32:]
220184
cc, err := ecies.AESCBCEncrypt(message, kE, iv, true)
221185
if err != nil {
222-
return "", err
186+
return nil, err
223187
}
224188
d := c.Sha256HMAC(cc, kM)
225189
encBuf := append(RBuf, cc...)
226190
encBuf = append(encBuf, d...)
227191

228-
log.Printf("encBuf: %x", encBuf)
229-
result := base64.StdEncoding.EncodeToString(encBuf)
230-
return result, nil
192+
return encBuf, nil
231193
}
232194

233-
func BitcoreDecrypt(encryptedMessage string, toPrivatKey *ec.PrivateKey) ([]byte, error) {
234-
// const kB = toPrivateKey
235-
// const fromPublicKey = PublicKey.fromString(toHex(encBuf.slice(0, 33)))
236-
// const R = fromPublicKey
237-
// const P = R.mul(kB)
238-
// if (P.eq(new Point(0, 0))) {
239-
// throw new Error('P equals 0')
240-
// }
241-
// const S = P.getX()
242-
// const Sbuf = S.toArray('be', 32)
243-
// const kEkM = Hash.sha512(Sbuf)
244-
// const kE = kEkM.slice(0, 32)
245-
// const kM = kEkM.slice(32, 64)
246-
// const c = encBuf.slice(33, encBuf.length - 32)
247-
// const d = encBuf.slice(encBuf.length - 32, encBuf.length)
248-
// const d2 = Hash.sha256hmac(kM, c)
249-
// if (toHex(d) !== toHex(d2)) {
250-
// throw new Error('Invalid checksum')
251-
// }
252-
// const messageBuf = AESCBC.decrypt(c, kE)
253-
// return [...messageBuf]
254-
255-
data, err := base64.StdEncoding.DecodeString(encryptedMessage)
256-
if err != nil {
257-
return nil, err
258-
}
195+
func BitcoreDecrypt(encryptedMessage []byte, toPrivatKey *ec.PrivateKey) ([]byte, error) {
259196

260-
fromPublicKey, err := ec.ParsePubKey(data[:33])
197+
fromPublicKey, err := ec.ParsePubKey(encryptedMessage[:33])
261198
if err != nil {
262199
return nil, err
263200
}
@@ -272,41 +209,12 @@ func BitcoreDecrypt(encryptedMessage string, toPrivatKey *ec.PrivateKey) ([]byte
272209
kE := kEkM[:32]
273210
kM := kEkM[32:]
274211

275-
cipherText := data[33 : len(data)-32]
276-
mac := data[len(data)-32:]
212+
cipherText := encryptedMessage[33 : len(encryptedMessage)-32]
213+
mac := encryptedMessage[len(encryptedMessage)-32:]
277214
expectedMAC := c.Sha256HMAC(cipherText, kM)
278215
if !hmac.Equal(mac, expectedMAC) {
279216
return nil, errors.New("invalid ciphertext: HMAC mismatch")
280217
}
281218
iv := cipherText[:16]
282219
return ecies.AESCBCDecrypt(cipherText[16:], kE, iv)
283-
284-
// Derive shared secret
285-
// x, _ := privateKey.Curve.ScalarMult(fromPublicKey.X, fromPublicKey.Y, privateKey.D.Bytes())
286-
// sharedSecret := x.Bytes()
287-
288-
// Key derivation
289-
// keyMaterial := c.Sha512(sharedSecret)
290-
291-
// keyE := keyMaterial[:32] // AES-256 key
292-
// keyM := keyMaterial[32:] // HMAC key
293-
294-
// // Verify HMAC
295-
// mac := data[len(data)-32:]
296-
// encryptedData := data[:len(data)-32]
297-
// expectedMAC := c.Sha256HMAC(encryptedData, keyM)
298-
299-
// if !hmac.Equal(mac, expectedMAC) {
300-
// return nil, errors.New("invalid ciphertext: HMAC mismatch")
301-
// }
302-
303-
// // Decrypt
304-
// iv := make([]byte, 16) // In Bitcore, IV is usually all zeros
305-
// cipherText := encryptedData[33:]
306-
// plainText, err := ecies.AESCBCDecrypt(cipherText, keyE, iv)
307-
// if err != nil {
308-
// return nil, err
309-
// }
310-
311-
// return plainText, nil
312220
}

compat/ecies/ecies_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ func TestElectrumEncryptDecryptSingle(t *testing.T) {
3232
// Electrum Encrypt
3333
encryptedData, err := ElectrumEncrypt([]byte(msgString), pk.PubKey(), pk, false)
3434
require.NoError(t, err)
35-
require.Equal(t, "QklFMQO7zpX/GS4XpthCy6/hT38ZKsBGbn8JKMGHOY5ifmaoT890Krt9cIRk/ULXaB5uC08owRICzenFbm31pZGu0gCM2uOxpofwHacKidwZ0Q7aEw==", encryptedData)
35+
expectedB64, err := base64.StdEncoding.DecodeString("QklFMQO7zpX/GS4XpthCy6/hT38ZKsBGbn8JKMGHOY5ifmaoT890Krt9cIRk/ULXaB5uC08owRICzenFbm31pZGu0gCM2uOxpofwHacKidwZ0Q7aEw==")
36+
require.NoError(t, err)
37+
require.Equal(t, expectedB64, encryptedData)
3638

3739
// Electrum Decrypt
3840
decryptedData, _ := ElectrumDecrypt(encryptedData, pk, nil)
@@ -93,11 +95,10 @@ func TestBitcoreEncryptDecryptSolo(t *testing.T) {
9395
// Bitcore Encrypt
9496
encryptedData, err := BitcoreEncrypt([]byte(msgString), pk.PubKey(), pk, nil)
9597
require.NoError(t, err)
96-
require.Len(t, encryptedData, 132)
97-
require.Equal(t, expectedEncryptedData, encryptedData)
98+
require.Equal(t, decodedExpectedEncryptedData, encryptedData)
9899

99100
// Bitcore Decrypt
100-
decryptedData, err := BitcoreDecrypt(string(encryptedData), pk)
101+
decryptedData, err := BitcoreDecrypt(encryptedData, pk)
101102
require.NoError(t, err)
102103
require.Equal(t, msgString, string(decryptedData))
103104
}
@@ -110,9 +111,10 @@ func TestBitcoreEncryptDecryptWithCounterparty(t *testing.T) {
110111
// Bitcore Encrypt
111112
encryptedData, err := BitcoreEncrypt([]byte(msgString), counterpartyPk.PubKey(), pk1, nil)
112113
expectedEncryptedData := "A7vOlf8ZLhem2ELLr+FPfxkqwEZufwkowYc5jmJ+ZqhPAAAAAAAAAAAAAAAAAAAAAAmFslNpNc4TrjaMPmPLdooZwoP6/fE7GN3AeyLpFf2f+QGYRKIke8zbhxu8FcLOsA=="
114+
decodedExpectedEncryptedData, _ := base64.StdEncoding.DecodeString(expectedEncryptedData)
113115

114116
require.NoError(t, err)
115-
require.Equal(t, expectedEncryptedData, encryptedData)
117+
require.Equal(t, decodedExpectedEncryptedData, encryptedData)
116118

117119
// Bitcore Decrypt
118120
decryptedData, err := BitcoreDecrypt(encryptedData, counterpartyPk)

primitives/aescbc/cbc.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"crypto/aes"
66
"crypto/cipher"
77
"errors"
8-
"log"
98
)
109

1110
// AESCBCEncrypt encrypts data using AES in CBC mode with an IV
@@ -21,7 +20,6 @@ func AESCBCEncrypt(data, key, iv []byte, concatIv bool) ([]byte, error) {
2120
if concatIv {
2221
cipherText = append(iv, cipherText...)
2322
}
24-
log.Printf("cipherText: %x", cipherText)
2523
return cipherText, nil
2624
}
2725

0 commit comments

Comments
 (0)