Fix key derivation
This commit is contained in:
parent
7ec6fa03d3
commit
e273031dce
@ -40,6 +40,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
pcsc "github.com/gballet/go-libpcsclite"
|
pcsc "github.com/gballet/go-libpcsclite"
|
||||||
|
"github.com/status-im/keycard-go/derivationpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing
|
// ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing
|
||||||
@ -67,9 +68,9 @@ var ErrAlreadyOpen = errors.New("smartcard: already open")
|
|||||||
var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch")
|
var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// appletAID = []byte{0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x61, 0x6C, 0x6C, 0x65, 0x74, 0x41, 0x70, 0x70}
|
|
||||||
appletAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01, 0x01}
|
appletAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01, 0x01}
|
||||||
DerivationSignatureHash = sha256.Sum256([]byte("STATUS KEY DERIVATION"))
|
// DerivationSignatureHash is used to derive the public key from the signature of this hash
|
||||||
|
DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes())
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -926,41 +927,54 @@ func (s *Session) initialize(seed []byte) error {
|
|||||||
|
|
||||||
// derive derives a new HD key path on the card.
|
// derive derives a new HD key path on the card.
|
||||||
func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) {
|
func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) {
|
||||||
// If the current path is a prefix of the desired path, we don't have to
|
startingPoint, path, err := derivationpath.Decode(path.String())
|
||||||
// start again.
|
|
||||||
remainingPath := path
|
|
||||||
|
|
||||||
pubkey, err := s.publicKey()
|
|
||||||
if err != nil {
|
|
||||||
return accounts.Account{}, err
|
|
||||||
}
|
|
||||||
currentPath, err := s.derivationPath()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
reset := false
|
var p1 uint8
|
||||||
if len(currentPath) <= len(path) {
|
switch startingPoint {
|
||||||
for i := 0; i < len(currentPath); i++ {
|
case derivationpath.StartingPointMaster:
|
||||||
if path[i] != currentPath[i] {
|
p1 = P1DeriveKeyFromMaster
|
||||||
reset = true
|
case derivationpath.StartingPointParent:
|
||||||
break
|
p1 = P1DeriveKeyFromParent
|
||||||
}
|
case derivationpath.StartingPointCurrent:
|
||||||
}
|
p1 = P1DeriveKeyFromCurrent
|
||||||
if !reset {
|
default:
|
||||||
remainingPath = path[len(currentPath):]
|
return accounts.Account{}, fmt.Errorf("invalid startingPoint %d", startingPoint)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reset = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pathComponent := range remainingPath {
|
data := new(bytes.Buffer)
|
||||||
pubkey, err = s.deriveKeyAssisted(reset, pathComponent)
|
for _, segment := range path {
|
||||||
reset = false
|
if err := binary.Write(data, binary.BigEndian, segment); err != nil {
|
||||||
if err != nil {
|
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = s.Channel.TransmitEncrypted(claSCWallet, insDeriveKey, p1, 0, data.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return accounts.Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := s.Channel.TransmitEncrypted(claSCWallet, insSign, 0, 0, DerivationSignatureHash[:])
|
||||||
|
if err != nil {
|
||||||
|
return accounts.Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sigdata := new(signatureData)
|
||||||
|
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
||||||
|
return accounts.Account{}, err
|
||||||
|
}
|
||||||
|
rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
|
||||||
|
sig := make([]byte, 65)
|
||||||
|
copy(sig[32-len(rbytes):32], rbytes)
|
||||||
|
copy(sig[64-len(sbytes):64], sbytes)
|
||||||
|
|
||||||
|
pubkey, err := determinePublicKey(sig, sigdata.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return accounts.Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
pub, err := crypto.UnmarshalPubkey(pubkey)
|
pub, err := crypto.UnmarshalPubkey(pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
@ -968,53 +982,6 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
|||||||
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
|
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyDerivationInfo contains information on the current key derivation step.
|
|
||||||
type keyDerivationInfo struct {
|
|
||||||
PublicKeyX []byte `asn1:"tag:3"` // The X coordinate of the current public key
|
|
||||||
Signature struct {
|
|
||||||
R *big.Int
|
|
||||||
S *big.Int
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deriveKeyAssisted does one step of assisted key generation, asking the card to generate
|
|
||||||
// a specific path, and performing the necessary computations to finish the public key
|
|
||||||
// generation step.
|
|
||||||
func (s *Session) deriveKeyAssisted(reset bool, pathComponent uint32) ([]byte, error) {
|
|
||||||
p1 := deriveP1Assisted
|
|
||||||
if !reset {
|
|
||||||
p1 |= deriveP1Append
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := binary.Write(buf, binary.BigEndian, pathComponent); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
response, err := s.Channel.TransmitEncrypted(claSCWallet, insDeriveKey, p1, deriveP2KeyPath, buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
keyinfo := new(keyDerivationInfo)
|
|
||||||
if _, err := asn1.UnmarshalWithParams(response.Data, keyinfo, "tag:2"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rbytes, sbytes := keyinfo.Signature.R.Bytes(), keyinfo.Signature.S.Bytes()
|
|
||||||
sig := make([]byte, 65)
|
|
||||||
copy(sig[32-len(rbytes):32], rbytes)
|
|
||||||
copy(sig[64-len(sbytes):64], sbytes)
|
|
||||||
|
|
||||||
pubkey, err := determinePublicKey(sig, keyinfo.PublicKeyX)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
_, err = s.Channel.TransmitEncrypted(claSCWallet, insDeriveKey, deriveP1Assisted|deriveP1Append, deriveP2PublicKey, pubkey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return pubkey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyExport contains information on an exported keypair.
|
// keyExport contains information on an exported keypair.
|
||||||
type keyExport struct {
|
type keyExport struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
@ -1085,7 +1052,7 @@ func determinePublicKey(sig, pubkeyX []byte) ([]byte, error) {
|
|||||||
sig[64] = byte(v)
|
sig[64] = byte(v)
|
||||||
pubkey, err := crypto.Ecrecover(DerivationSignatureHash[:], sig)
|
pubkey, err := crypto.Ecrecover(DerivationSignatureHash[:], sig)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if bytes.Equal(pubkey[1:33], pubkeyX) {
|
if bytes.Equal(pubkey, pubkeyX) {
|
||||||
return pubkey, nil
|
return pubkey, nil
|
||||||
}
|
}
|
||||||
} else if v == 1 || err != secp256k1.ErrRecoverFailed {
|
} else if v == 1 || err != secp256k1.ErrRecoverFailed {
|
||||||
|
Loading…
Reference in New Issue
Block a user