Make sure privkey is on curve before using unsafe secp256k1.PrivKeyFromBytes
This commit is contained in:
parent
ddde532a9e
commit
7c892dcd23
34
nip44.go
34
nip44.go
@ -7,10 +7,12 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
"golang.org/x/crypto/chacha20"
|
"golang.org/x/crypto/chacha20"
|
||||||
@ -134,19 +136,25 @@ func Decrypt(conversationKey []byte, ciphertext string) (string, error) {
|
|||||||
return string(unpadded), nil
|
return string(unpadded), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateConversationKey(sendPrivkey *secp256k1.PrivateKey, recvPubkey *secp256k1.PublicKey) []byte {
|
func GenerateConversationKey(sendPrivkey []byte, recvPubkey []byte) ([]byte, error) {
|
||||||
// TODO: Make sure keys are not invalid or weak since the secp256k1 package does not.
|
var (
|
||||||
// See documentation of secp256k1.PrivKeyFromBytes:
|
N = secp256k1.S256().N
|
||||||
// ================================================================================
|
sk *secp256k1.PrivateKey
|
||||||
// | WARNING: This means passing a slice with more than 32 bytes is truncated and |
|
pk *secp256k1.PublicKey
|
||||||
// | that truncated value is reduced modulo N. Further, 0 is not a valid private |
|
err error
|
||||||
// | key. It is up to the caller to provide a value in the appropriate range of |
|
)
|
||||||
// | [1, N-1]. Failure to do so will either result in an invalid private key or |
|
// make sure that private key is on curve before using unsafe secp256k1.PrivKeyFromBytes
|
||||||
// | potentially weak private keys that have bias that could be exploited. |
|
// see https://pkg.go.dev/github.com/decred/dcrd/dcrec/secp256k1/v4#PrivKeyFromBytes
|
||||||
// ================================================================================
|
skX := new(big.Int).SetBytes(sendPrivkey)
|
||||||
// -- https://pkg.go.dev/github.com/decred/dcrd/dcrec/secp256k1/v4#PrivKeyFromBytes
|
if skX.Cmp(big.NewInt(0)) == 0 || skX.Cmp(N) >= 0 {
|
||||||
shared := secp256k1.GenerateSharedSecret(sendPrivkey, recvPubkey)
|
return []byte{}, fmt.Errorf("invalid private key: x coordinate %s is not on the secp256k1 curve", hex.EncodeToString(sendPrivkey))
|
||||||
return hkdf.Extract(sha256.New, shared, []byte("nip44-v2"))
|
}
|
||||||
|
sk = secp256k1.PrivKeyFromBytes(sendPrivkey)
|
||||||
|
if pk, err = secp256k1.ParsePubKey(recvPubkey); err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
shared := secp256k1.GenerateSharedSecret(sk, pk)
|
||||||
|
return hkdf.Extract(sha256.New, shared, []byte("nip44-v2")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func chacha20_(key []byte, nonce []byte, message []byte) ([]byte, error) {
|
func chacha20_(key []byte, nonce []byte, message []byte) ([]byte, error) {
|
||||||
|
@ -94,29 +94,25 @@ func assertDecryptFail(t *testing.T, conversationKey string, plaintext string, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertConversationKeyFail(t *testing.T, sk1 string, pub2 string, msg string) {
|
func assertConversationKeyFail(t *testing.T, sk1 string, pub2 string, msg string) {
|
||||||
// TODO: Update GenerateConversationKey since secp256k1 does accept invalid or weak keys
|
|
||||||
t.Skip("secp256k1 keys are not validated yet during conversation key generation.")
|
|
||||||
var (
|
var (
|
||||||
// sendPrivkey *secp256k1.PrivateKey
|
sk1Decoded []byte
|
||||||
// recvPubkey *secp256k1.PublicKey
|
pub2Decoded []byte
|
||||||
decoded []byte
|
ok bool
|
||||||
ok bool
|
err error
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
decoded, err = hex.DecodeString(sk1)
|
sk1Decoded, err = hex.DecodeString(sk1)
|
||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// sendPrivkey = secp256k1.PrivKeyFromBytes(decoded)
|
pub2Decoded, err = hex.DecodeString("02" + pub2)
|
||||||
decoded, err = hex.DecodeString("02" + pub2)
|
|
||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for pub2: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for pub2: %v", err); !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = secp256k1.ParsePubKey(decoded)
|
_, err = nip44.GenerateConversationKey(sk1Decoded, pub2Decoded)
|
||||||
assert.ErrorContains(t, err, msg)
|
assert.ErrorContains(t, err, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertConversationKeyGeneration(t *testing.T, sendPrivkey *secp256k1.PrivateKey, recvPubkey *secp256k1.PublicKey, conversationKey string) bool {
|
func assertConversationKeyGeneration(t *testing.T, sendPrivkey []byte, recvPubkey []byte, conversationKey string) bool {
|
||||||
var (
|
var (
|
||||||
actualConversationKey []byte
|
actualConversationKey []byte
|
||||||
expectedConversationKey []byte
|
expectedConversationKey []byte
|
||||||
@ -127,7 +123,10 @@ func assertConversationKeyGeneration(t *testing.T, sendPrivkey *secp256k1.Privat
|
|||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
actualConversationKey = nip44.GenerateConversationKey(sendPrivkey, recvPubkey)
|
actualConversationKey, err = nip44.GenerateConversationKey(sendPrivkey, recvPubkey)
|
||||||
|
if ok = assert.NoErrorf(t, err, "conversation key generation failed: %v", err); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if ok = assert.Equalf(t, expectedConversationKey, actualConversationKey, "wrong conversation key"); !ok {
|
if ok = assert.Equalf(t, expectedConversationKey, actualConversationKey, "wrong conversation key"); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -136,41 +135,39 @@ func assertConversationKeyGeneration(t *testing.T, sendPrivkey *secp256k1.Privat
|
|||||||
|
|
||||||
func assertConversationKeyGenerationSec(t *testing.T, sk1 string, sk2 string, conversationKey string) bool {
|
func assertConversationKeyGenerationSec(t *testing.T, sk1 string, sk2 string, conversationKey string) bool {
|
||||||
var (
|
var (
|
||||||
sendPrivkey *secp256k1.PrivateKey
|
sk1Decoded []byte
|
||||||
recvPubkey *secp256k1.PublicKey
|
pub2Decoded []byte
|
||||||
ok bool
|
ok bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if decoded, err := hex.DecodeString(sk1); err == nil {
|
sk1Decoded, err = hex.DecodeString(sk1)
|
||||||
sendPrivkey = secp256k1.PrivKeyFromBytes(decoded)
|
|
||||||
}
|
|
||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if decoded, err := hex.DecodeString(sk2); err == nil {
|
if decoded, err := hex.DecodeString(sk2); err == nil {
|
||||||
recvPubkey = secp256k1.PrivKeyFromBytes(decoded).PubKey()
|
pub2Decoded = secp256k1.PrivKeyFromBytes(decoded).PubKey().SerializeCompressed()
|
||||||
}
|
}
|
||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for sk2: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for sk2: %v", err); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return assertConversationKeyGeneration(t, sendPrivkey, recvPubkey, conversationKey)
|
return assertConversationKeyGeneration(t, sk1Decoded, pub2Decoded, conversationKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertConversationKeyGenerationPub(t *testing.T, sk1 string, pub2 string, conversationKey string) bool {
|
func assertConversationKeyGenerationPub(t *testing.T, sk1 string, pub2 string, conversationKey string) bool {
|
||||||
var (
|
var (
|
||||||
sendPrivkey *secp256k1.PrivateKey
|
sk1Decoded []byte
|
||||||
recvPubkey *secp256k1.PublicKey
|
pub2Decoded []byte
|
||||||
ok bool
|
ok bool
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if decoded, err := hex.DecodeString(sk1); err == nil {
|
sk1Decoded, err = hex.DecodeString(sk1)
|
||||||
sendPrivkey = secp256k1.PrivKeyFromBytes(decoded)
|
|
||||||
}
|
|
||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if decoded, err := hex.DecodeString("02" + pub2); err == nil {
|
if decoded, err := hex.DecodeString("02" + pub2); err == nil {
|
||||||
recvPubkey, err = secp256k1.ParsePubKey(decoded)
|
if recvPubkey, err := secp256k1.ParsePubKey(decoded); err == nil {
|
||||||
|
pub2Decoded = recvPubkey.SerializeCompressed()
|
||||||
|
}
|
||||||
if ok = assert.NoErrorf(t, err, "parse pubkey failed: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "parse pubkey failed: %v", err); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -178,7 +175,7 @@ func assertConversationKeyGenerationPub(t *testing.T, sk1 string, pub2 string, c
|
|||||||
if ok = assert.NoErrorf(t, err, "hex decode failed for pub2: %v", err); !ok {
|
if ok = assert.NoErrorf(t, err, "hex decode failed for pub2: %v", err); !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return assertConversationKeyGeneration(t, sendPrivkey, recvPubkey, conversationKey)
|
return assertConversationKeyGeneration(t, sk1Decoded, pub2Decoded, conversationKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertMessageKeyGeneration(t *testing.T, conversationKey string, salt string, chachaKey string, chachaSalt string, hmacKey string) bool {
|
func assertMessageKeyGeneration(t *testing.T, conversationKey string, salt string, chachaKey string, chachaSalt string, hmacKey string) bool {
|
||||||
@ -431,7 +428,7 @@ func TestConversationKeyFail001(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||||
"x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve",
|
"invalid private key: x coordinate ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,7 +437,7 @@ func TestConversationKeyFail002(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||||
"x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve",
|
"invalid private key: x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,7 +446,7 @@ func TestConversationKeyFail003(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139",
|
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139",
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
"x >= field prime",
|
"invalid public key: x >= field prime",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,7 +455,7 @@ func TestConversationKeyFail004(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
|
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
|
||||||
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||||
"x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve",
|
"invalid private key: x coordinate fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +464,7 @@ func TestConversationKeyFail005(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002",
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||||
"x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve",
|
"invalid public key: x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,7 +473,7 @@ func TestConversationKeyFail006(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve",
|
"invalid public key: x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,7 +482,7 @@ func TestConversationKeyFail007(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
||||||
"eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d",
|
"eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d",
|
||||||
"x coordinate eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d is not on the secp256k1 curve",
|
"invalid public key: x coordinate eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,7 +491,7 @@ func TestConversationKeyFail008(t *testing.T) {
|
|||||||
assertConversationKeyFail(t,
|
assertConversationKeyFail(t,
|
||||||
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
||||||
"709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f",
|
"709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f",
|
||||||
"x coordinate 709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f is not on the secp256k1 curve",
|
"invalid public key: x coordinate 709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f is not on the secp256k1 curve",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user