f5ad619450
Public keys can have X coordinate value shorter than 32 byte. In this case `encodePoint()` should return 32 byte value with leading zeros. This commit also adds unit test for public key with 31 byte X coordinate value.
197 lines
5.6 KiB
Go
197 lines
5.6 KiB
Go
package crypto
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"encoding/hex"
|
|
"math/big"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/nspcc-dev/neofs-crypto/test"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var testKeys = [...]string{
|
|
"0375099c302b77664a2508bec1cae47903857b762c62713f190e8d99912ef76737",
|
|
"025188d33a3113ac77fea0c17137e434d704283c234400b9b70bcdf4829094374a",
|
|
"02c4c574d1bbe7efb2feaeed99e6c03924d6d3c9ad76530437d75c07bff3ddcc0f",
|
|
"02563eece0b9035e679d28e2d548072773c43ce44a53cb7f30d3597052210dbb70",
|
|
"02f8152966ad33b3c2622bdd032f5989fbd63a9a3af34e12eefee912c37defc880",
|
|
"0252d9fd2376f6b3bcb4706cad54ec031d95a1a70414129286c247cd2bc521f73f",
|
|
"0251ec65b2496b1d8ece3efe68a8b57ce7bc75b4171f07fa5b26c63a27fb4f9216",
|
|
"025f7d63e18e6b896730f45989b7a8d00c0b86c75c2b834d903bc681833592bdcc",
|
|
"02d351a4c87ec3b33e62610cb3fd197962c0081bbe1b1b888bc41844f4c6df9cd3",
|
|
"036e3859e6ab43c0f45b7891761f0da86a7b62f931f3d963efd3103924920a73b3",
|
|
"03c02a93134f98d9c78ec54b1b1f97fc64cd81360f53a293f41e4ad54aac3c5717",
|
|
"03fea219d4ccfd7641cebbb2439740bb4bd7c4730c1abd6ca1dc44386533816df9",
|
|
"02a33413277a319cc6fd4c54a2feb9032eba668ec587f307e319dc48733087fa61",
|
|
}
|
|
|
|
func Test_decompressPoints(t *testing.T) {
|
|
t.Run("prepared public keys: decompressPoints", func(t *testing.T) {
|
|
for i := range testKeys {
|
|
bytes, err := hex.DecodeString(testKeys[i])
|
|
require.NoErrorf(t, err, testKeys[i])
|
|
|
|
x, y := decompressPoints(new(big.Int).SetBytes(bytes[1:]), uint(bytes[0]))
|
|
require.NotNil(t, x)
|
|
require.NotNil(t, y)
|
|
|
|
res := MarshalPublicKey(&ecdsa.PublicKey{Curve: curve, X: x, Y: y})
|
|
require.Equal(t, testKeys[i], hex.EncodeToString(res))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMarshalUnmarshal(t *testing.T) {
|
|
t.Run("prepared public keys: unmarshal / marshal", func(t *testing.T) {
|
|
for i := range testKeys {
|
|
bytes, err := hex.DecodeString(testKeys[i])
|
|
require.NoErrorf(t, err, testKeys[i])
|
|
|
|
key := UnmarshalPublicKey(bytes)
|
|
res := MarshalPublicKey(key)
|
|
require.Equal(t, testKeys[i], hex.EncodeToString(res))
|
|
}
|
|
})
|
|
|
|
t.Run("try to use stored keys", func(t *testing.T) {
|
|
for i := 0; i < 10; i++ {
|
|
i := i
|
|
sk := test.DecodeKey(i)
|
|
|
|
t.Run("marshal key_"+strconv.Itoa(i), func(t *testing.T) {
|
|
bytes := MarshalPublicKey(&sk.PublicKey)
|
|
require.Equal(t, testKeys[i], hex.EncodeToString(bytes))
|
|
})
|
|
|
|
t.Run("unmarshal key_"+strconv.Itoa(i), func(t *testing.T) {
|
|
bytes, err := hex.DecodeString(testKeys[i])
|
|
require.NoError(t, err)
|
|
|
|
pk := UnmarshalPublicKey(bytes)
|
|
require.Equal(t, pk, &sk.PublicKey)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("prepared private key: unmarshal / marshal", func(t *testing.T) {
|
|
walletPublicKey := "03fb2e07eba5477dd7de3f627d8803eb86bc36b98132f51d81017343bb6585d9c7"
|
|
walletPrivateKey := "dc80f6900b7b7a791496c06aa77695d744fafaaeff5c143801eb97935448c671"
|
|
|
|
data, err := hex.DecodeString(walletPrivateKey)
|
|
require.NoError(t, err)
|
|
|
|
_, err = x509.ParseECPrivateKey(data)
|
|
require.Error(t, err)
|
|
|
|
privateKey, err := UnmarshalPrivateKey(data)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, hex.EncodeToString(MarshalPublicKey(&privateKey.PublicKey)), walletPublicKey)
|
|
require.Equal(t, data, MarshalPrivateKey(privateKey))
|
|
})
|
|
|
|
t.Run("marshal / unmarshal public key with 31 byte X point", func(t *testing.T) {
|
|
privateKeyString := "4dd8baa41612a74c1cc664102330115f9b3f4d0fc9b7e4f95c4aceb5cbf335d6"
|
|
|
|
privateKeyHex, err := hex.DecodeString(privateKeyString)
|
|
require.NoError(t, err)
|
|
|
|
privateKey, err := UnmarshalPrivateKey(privateKeyHex)
|
|
require.NoError(t, err)
|
|
require.Len(t, privateKey.PublicKey.X.Bytes(), 31)
|
|
|
|
publicKeyHex := MarshalPublicKey(&privateKey.PublicKey)
|
|
require.Len(t, publicKeyHex, PublicKeyCompressedSize)
|
|
|
|
publicKey := UnmarshalPublicKey(publicKeyHex)
|
|
require.NotNil(t, publicKey)
|
|
require.Equal(t, *publicKey, privateKey.PublicKey)
|
|
})
|
|
}
|
|
|
|
func TestSignVerify(t *testing.T) {
|
|
t.Run("should not fail when we pass bad data to unmarshalXY", func(t *testing.T) {
|
|
var (
|
|
data = []byte("Hello world")
|
|
key = test.DecodeKey(0)
|
|
)
|
|
|
|
require.NotPanics(t, func() {
|
|
{ // 1. simple example
|
|
UnmarshalPublicKey(nil)
|
|
}
|
|
|
|
{ // 2. pass bad data (not public key)
|
|
r1, s1, err := ecdsa.Sign(rand.Reader, key, hashBytes(data))
|
|
require.NoError(t, err)
|
|
|
|
sign := marshalXY(r1, s1)
|
|
UnmarshalPublicKey(sign)
|
|
}
|
|
|
|
{ // 3. bad big.Ints
|
|
sign := marshalXY(big.NewInt(0), big.NewInt(1))
|
|
UnmarshalPublicKey(sign)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("low level", func(t *testing.T) {
|
|
var (
|
|
data = []byte("Hello world")
|
|
curve = elliptic.P256()
|
|
key = test.DecodeKey(0)
|
|
)
|
|
|
|
r1, s1, err := ecdsa.Sign(rand.Reader, key,
|
|
hashBytes(data))
|
|
require.NoError(t, err)
|
|
|
|
sign := marshalXY(r1, s1)
|
|
|
|
{ // This is just to validate, that we are on right way.. try to unmarshal R/S from sign
|
|
// validate bytes length
|
|
byteLen := (curve.Params().BitSize + 7) >> 3
|
|
require.Len(t, sign, 1+2*byteLen)
|
|
|
|
// uncompressed form?
|
|
require.Equal(t, byte(4), sign[0])
|
|
|
|
// validate R / S
|
|
p := curve.Params().P
|
|
r := new(big.Int).SetBytes(sign[1 : 1+byteLen])
|
|
s := new(big.Int).SetBytes(sign[1+byteLen:])
|
|
require.True(t, r.Cmp(p) < 0)
|
|
require.True(t, s.Cmp(p) < 0)
|
|
|
|
// don't check curves
|
|
// require.True(t, curve.IsOnCurve(r, s))
|
|
}
|
|
|
|
r2, s2 := unmarshalXY(sign)
|
|
require.NotNil(t, r2)
|
|
require.NotNil(t, s2)
|
|
|
|
require.Equal(t, r1.Bytes(), r2.Bytes())
|
|
require.Equal(t, s2.Bytes(), s2.Bytes())
|
|
})
|
|
|
|
t.Run("high level", func(t *testing.T) {
|
|
var (
|
|
data = []byte("Hello world")
|
|
key = test.DecodeKey(0)
|
|
)
|
|
|
|
sign, err := Sign(key, data)
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < 100; i++ {
|
|
require.NoError(t, Verify(&key.PublicKey, data, sign))
|
|
}
|
|
})
|
|
}
|