Refactor crypto.FromECDSAPub to MarshalPubkey/Std (#3797)

Most places that used this method were cutting off the 1st byte.
Refactor this idea to a common place.

* better naming: MarshalPubkey matches existing UnmarshalPubkey
* "Std" suffix for the ANSI standard encoding without cut off
* docs
This commit is contained in:
battlmonstr 2022-03-31 06:06:20 +02:00 committed by GitHub
parent 4519e10e39
commit 930d662f21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 58 additions and 33 deletions

View File

@ -88,7 +88,7 @@ func main() {
}
if *writeAddr {
fmt.Printf("%x\n", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:])
fmt.Printf("%x\n", crypto.MarshalPubkey(&nodeKey.PublicKey))
os.Exit(0)
}

View File

@ -1491,7 +1491,7 @@ func (s *EmptyStep) author() (common.Address, error) {
if err != nil {
return common.Address{}, err
}
ecdsa, err := crypto.UnmarshalPubkey(public)
ecdsa, err := crypto.UnmarshalPubkeyStd(public)
if err != nil {
return common.Address{}, err
}

View File

@ -173,8 +173,10 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}
// UnmarshalPubkey converts bytes to a secp256k1 public key.
func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
// UnmarshalPubkeyStd parses a public key from the given bytes in the standard "uncompressed" format.
// The input slice must be 65 bytes long and have this format: [4, X..., Y...]
// See MarshalPubkeyStd.
func UnmarshalPubkeyStd(pub []byte) (*ecdsa.PublicKey, error) {
x, y := elliptic.Unmarshal(S256(), pub)
if x == nil {
return nil, errInvalidPubkey
@ -182,13 +184,39 @@ func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}
func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
// MarshalPubkeyStd converts a public key into the standard "uncompressed" format.
// It returns a 65 bytes long slice that contains: [4, X..., Y...]
// Returns nil if the given public key is not initialized.
// See UnmarshalPubkeyStd.
func MarshalPubkeyStd(pub *ecdsa.PublicKey) []byte {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil
}
return elliptic.Marshal(S256(), pub.X, pub.Y)
}
// UnmarshalPubkey parses a public key from the given bytes in the 64 bytes "uncompressed" format.
// The input slice must be 64 bytes long and have this format: [X..., Y...]
// See MarshalPubkey.
func UnmarshalPubkey(keyBytes []byte) (*ecdsa.PublicKey, error) {
keyBytes = append([]byte{0x4}, keyBytes...)
return UnmarshalPubkeyStd(keyBytes)
}
// MarshalPubkey converts a public key into a 64 bytes "uncompressed" format.
// It returns a 64 bytes long slice that contains: [X..., Y...]
// In the standard 65 bytes format the first byte is always constant (equal to 4),
// so it can be cut off and trivially recovered later.
// Returns nil if the given public key is not initialized.
// See UnmarshalPubkey.
func MarshalPubkey(pubkey *ecdsa.PublicKey) []byte {
keyBytes := MarshalPubkeyStd(pubkey)
if keyBytes == nil {
return nil
}
return keyBytes[1:]
}
// HexToECDSA parses a secp256k1 private key.
func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) {
b, err := hex.DecodeString(hexkey)
@ -284,8 +312,8 @@ func ValidateSignatureValues(v byte, r, s *uint256.Int, homestead bool) bool {
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
pubBytes := MarshalPubkey(&p)
return common.BytesToAddress(Keccak256(pubBytes)[12:])
}
func zeroBytes(bytes []byte) {

View File

@ -78,7 +78,7 @@ func TestUnmarshalPubkey(t *testing.T) {
}
var (
enc, _ = hex.DecodeString("04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
enc, _ = hex.DecodeString("760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
dec = &ecdsa.PublicKey{
Curve: S256(),
X: hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
@ -107,7 +107,7 @@ func TestSign(t *testing.T) {
if err != nil {
t.Errorf("ECRecover error: %s", err)
}
pubKey, _ := UnmarshalPubkey(recoveredPub)
pubKey, _ := UnmarshalPubkeyStd(recoveredPub)
recoveredAddr := PubkeyToAddress(*pubKey)
if addr != recoveredAddr {
t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)

View File

@ -90,7 +90,7 @@ func TestDecompressPubkey(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if uncompressed := FromECDSAPub(key); !bytes.Equal(uncompressed, testpubkey) {
if uncompressed := MarshalPubkeyStd(key); !bytes.Equal(uncompressed, testpubkey) {
t.Errorf("wrong public key result: got %x, want %x", uncompressed, testpubkey)
}
if _, err := DecompressPubkey(nil); err == nil {

View File

@ -247,7 +247,7 @@ func (e *rootEntry) sigHash() []byte {
func (e *rootEntry) verifySignature(pubkey *ecdsa.PublicKey) bool {
sig := e.sig[:crypto.RecoveryIDOffset] // remove recovery id
enckey := crypto.FromECDSAPub(pubkey)
enckey := crypto.MarshalPubkeyStd(pubkey)
return crypto.VerifySignature(enckey, e.sigHash(), sig)
}

View File

@ -161,7 +161,6 @@ func parsePubkey(in string) (*ecdsa.PublicKey, error) {
} else if len(b) != 64 {
return nil, fmt.Errorf("wrong length, want %d hex chars", 128)
}
b = append([]byte{0x4}, b...)
return crypto.UnmarshalPubkey(b)
}
@ -175,7 +174,7 @@ func (n *Node) URLv4() string {
n.Load((*Secp256k1)(&key))
switch {
case scheme == "v4" || key != ecdsa.PublicKey{}:
nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:])
nodeid = fmt.Sprintf("%x", crypto.MarshalPubkey(&key))
default:
nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
}

View File

@ -567,7 +567,7 @@ func (h *handshakeState) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error)
msg := new(authMsgV4)
copy(msg.Signature[:], signature)
copy(msg.InitiatorPubkey[:], crypto.FromECDSAPub(&prv.PublicKey)[1:])
copy(msg.InitiatorPubkey[:], crypto.MarshalPubkey(&prv.PublicKey))
copy(msg.Nonce[:], h.initNonce)
msg.Version = 4
return msg, nil
@ -640,20 +640,18 @@ func (h *handshakeState) sealEIP8(msg interface{}) ([]byte, error) {
return append(prefix, enc...), err
}
// importPublicKey unmarshals 512 bit public keys.
// importPublicKey unmarshals 64 or 65 bytes long public keys.
func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) {
var pubKey65 []byte
var pub *ecdsa.PublicKey
var err error
switch len(pubKey) {
case 64:
// add 'uncompressed key' flag
pubKey65 = append([]byte{0x04}, pubKey...)
pub, err = crypto.UnmarshalPubkey(pubKey)
case 65:
pubKey65 = pubKey
pub, err = crypto.UnmarshalPubkeyStd(pubKey)
default:
return nil, fmt.Errorf("invalid public key length %v (expect 64/65)", len(pubKey))
}
// TODO: fewer pointless conversions
pub, err := crypto.UnmarshalPubkey(pubKey65)
if err != nil {
return nil, err
}

View File

@ -268,12 +268,12 @@ var (
func TestHandshakeForwardCompatibility(t *testing.T) {
var (
pubA = crypto.FromECDSAPub(&keyA.PublicKey)[1:]
pubB = crypto.FromECDSAPub(&keyB.PublicKey)[1:]
pubA = crypto.MarshalPubkey(&keyA.PublicKey)
pubB = crypto.MarshalPubkey(&keyB.PublicKey)
ephA, _ = crypto.HexToECDSA("869d6ecf5211f1cc60418a13b9d870b22959d0c16f02bec714c960dd2298a32d")
ephB, _ = crypto.HexToECDSA("e238eb8e04fee6511ab04c6dd3c89ce097b11f25d584863ac2b6d5b35b1847e4")
ephPubA = crypto.FromECDSAPub(&ephA.PublicKey)[1:]
ephPubB = crypto.FromECDSAPub(&ephB.PublicKey)[1:]
ephPubA = crypto.MarshalPubkey(&ephA.PublicKey)
ephPubB = crypto.MarshalPubkey(&ephB.PublicKey)
nonceA = unhex("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6")
nonceB = unhex("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd")
_, _, _, _ = pubA, pubB, ephPubA, ephPubB

View File

@ -511,8 +511,8 @@ func (srv *Server) Start(ctx context.Context) error {
func (srv *Server) setupLocalNode() error {
// Create the devp2p handshake.
pubkey := crypto.FromECDSAPub(&srv.PrivateKey.PublicKey)
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, Pubkey: pubkey[1:]}
pubkey := crypto.MarshalPubkey(&srv.PrivateKey.PublicKey)
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, Pubkey: pubkey}
for _, p := range srv.Protocols {
srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())
}

View File

@ -58,7 +58,7 @@ func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey) (*ecdsa.PublicKey,
}
func (c *testTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
pubkey := crypto.FromECDSAPub(c.rpub)[1:]
pubkey := crypto.MarshalPubkey(c.rpub)
return &protoHandshake{Pubkey: pubkey, Name: "test"}, nil
}
@ -317,7 +317,7 @@ func TestServerPeerLimits(t *testing.T) {
var tp = &setupTransport{
pubkey: &clientkey.PublicKey,
phs: protoHandshake{
Pubkey: crypto.FromECDSAPub(&clientkey.PublicKey)[1:],
Pubkey: crypto.MarshalPubkey(&clientkey.PublicKey),
// Force "DiscUselessPeer" due to unmatching caps
// Caps: []Cap{discard.cap()},
},
@ -416,13 +416,13 @@ func TestServerSetupConn(t *testing.T) {
wantCloseErr: errors.New("foo"),
},
{
tt: &setupTransport{pubkey: srvpub, phs: protoHandshake{Pubkey: crypto.FromECDSAPub(srvpub)[1:]}},
tt: &setupTransport{pubkey: srvpub, phs: protoHandshake{Pubkey: crypto.MarshalPubkey(srvpub)}},
flags: inboundConn,
wantCalls: "doEncHandshake,close,",
wantCloseErr: DiscSelf,
},
{
tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{Pubkey: crypto.FromECDSAPub(clientpub)[1:]}},
tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{Pubkey: crypto.MarshalPubkey(clientpub)}},
flags: inboundConn,
wantCalls: "doEncHandshake,doProtoHandshake,close,",
wantCloseErr: DiscUselessPeer,

View File

@ -30,11 +30,11 @@ import (
func TestProtocolHandshake(t *testing.T) {
var (
prv0, _ = crypto.GenerateKey()
pub0 = crypto.FromECDSAPub(&prv0.PublicKey)[1:]
pub0 = crypto.MarshalPubkey(&prv0.PublicKey)
hs0 = &protoHandshake{Version: 3, Pubkey: pub0, Caps: []Cap{{"a", 0}, {"b", 2}}}
prv1, _ = crypto.GenerateKey()
pub1 = crypto.FromECDSAPub(&prv1.PublicKey)[1:]
pub1 = crypto.MarshalPubkey(&prv1.PublicKey)
hs1 = &protoHandshake{Version: 3, Pubkey: pub1, Caps: []Cap{{"c", 1}, {"d", 3}}}
wg sync.WaitGroup