Fix key derivation

This commit is contained in:
Guillaume Ballet 2019-03-20 15:30:15 +01:00
parent 7ec6fa03d3
commit e273031dce

View File

@ -40,6 +40,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/log"
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
@ -67,9 +68,9 @@ var ErrAlreadyOpen = errors.New("smartcard: already open")
var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch")
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}
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 (
@ -926,41 +927,54 @@ func (s *Session) initialize(seed []byte) error {
// derive derives a new HD key path on the card.
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
// start again.
remainingPath := path
pubkey, err := s.publicKey()
if err != nil {
return accounts.Account{}, err
}
currentPath, err := s.derivationPath()
startingPoint, path, err := derivationpath.Decode(path.String())
if err != nil {
return accounts.Account{}, err
}
reset := false
if len(currentPath) <= len(path) {
for i := 0; i < len(currentPath); i++ {
if path[i] != currentPath[i] {
reset = true
break
}
}
if !reset {
remainingPath = path[len(currentPath):]
}
} else {
reset = true
var p1 uint8
switch startingPoint {
case derivationpath.StartingPointMaster:
p1 = P1DeriveKeyFromMaster
case derivationpath.StartingPointParent:
p1 = P1DeriveKeyFromParent
case derivationpath.StartingPointCurrent:
p1 = P1DeriveKeyFromCurrent
default:
return accounts.Account{}, fmt.Errorf("invalid startingPoint %d", startingPoint)
}
for _, pathComponent := range remainingPath {
pubkey, err = s.deriveKeyAssisted(reset, pathComponent)
reset = false
if err != nil {
data := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(data, binary.BigEndian, segment); err != nil {
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)
if err != nil {
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
}
// 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.
type keyExport struct {
PublicKey []byte `asn1:"tag:0"`
@ -1085,7 +1052,7 @@ func determinePublicKey(sig, pubkeyX []byte) ([]byte, error) {
sig[64] = byte(v)
pubkey, err := crypto.Ecrecover(DerivationSignatureHash[:], sig)
if err == nil {
if bytes.Equal(pubkey[1:33], pubkeyX) {
if bytes.Equal(pubkey, pubkeyX) {
return pubkey, nil
}
} else if v == 1 || err != secp256k1.ErrRecoverFailed {