Skip to content

Commit

Permalink
feat: ExportLoginKeys, ExportRecoverKeys
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-sirotin committed Jan 20, 2025
1 parent 65a2c97 commit 2925ae2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 2 deletions.
100 changes: 100 additions & 0 deletions internal/keycard_context_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/ebfe/scard"
"github.com/ethereum/go-ethereum/crypto"
"github.com/pkg/errors"
"github.com/status-im/keycard-go"
"github.com/status-im/keycard-go/io"
Expand Down Expand Up @@ -633,3 +634,102 @@ func (kc *KeycardContextV2) GetMetadata() (*Metadata, error) {
return ToMetadata(metadata), nil
}

func (kc *KeycardContextV2) exportedKeyToAddress(key *types.ExportedKey) (string, error) {
if key.PubKey() == nil {
return "", nil
}

ecdsaPubKey, err := crypto.UnmarshalPubkey(key.PubKey())
if err != nil {
return "", errors.Wrap(err, "failed to unmarshal public key")
}

return crypto.PubkeyToAddress(*ecdsaPubKey).Hex(), nil
}

func (kc *KeycardContextV2) exportKey(path string, exportOption uint8) (*KeyPair, error) {
// 1. As for today, it's pointless to use the 'current path' feature. So we always derive.
// 2. We keep this workaround for `makeCurrent` to mitigate a bug in an older version of the Keycard applet
// that doesn't correctly export the public key for the master path unless it is also the current path.
const derive = true
makeCurrent := path == MasterPath

exportedKey, err := kc.cmdSet.ExportKeyExtended(derive, makeCurrent, exportOption, path)
if err != nil {
return nil, kc.checkSCardError(err, "ExportKeyExtended")
}

address, err := kc.exportedKeyToAddress(exportedKey)
if err != nil {
return nil, errors.Wrap(err, "failed to convert key to address")
}

return &KeyPair{
Address: address,
PublicKey: exportedKey.PubKey(),
PrivateKey: exportedKey.PrivKey(),
ChainCode: exportedKey.ChainCode(),
}, nil
}

func (kc *KeycardContextV2) ExportLoginKeys() (*LoginKeys, error) {
if !kc.keycardReady() {
return nil, errKeycardNotReady
}

var err error
keys := &LoginKeys{}

keys.EncryptionPrivateKey, err = kc.exportKey(EncryptionPath, keycard.P2ExportKeyPrivateAndPublic)
if err != nil {
return nil, err
}

keys.WhisperPrivateKey, err = kc.exportKey(WhisperPath, keycard.P2ExportKeyPrivateAndPublic)
if err != nil {
return nil, err
}

return keys, err
}

func (kc *KeycardContextV2) ExportRecoverKeys() (*RecoverKeys, error) {
if !kc.keycardReady() {
return nil, errKeycardNotReady
}

loginKeys, err := kc.ExportLoginKeys()
if err != nil {
return nil, err
}

keys := &RecoverKeys{
LoginKeys: *loginKeys,
}

keys.EIP1581key, err = kc.exportKey(Eip1581Path, keycard.P2ExportKeyPublicOnly)
if err != nil {
return nil, err
}

rootExportOptions := map[bool]uint8{
true: keycard.P2ExportKeyExtendedPublic,
false: keycard.P2ExportKeyPublicOnly,
}
keys.WalletRootKey, err = kc.exportKey(WalletRoothPath, rootExportOptions[kc.status.KeycardSupportsExtendedKeys()])
if err != nil {
return nil, err
}

keys.WalletKey, err = kc.exportKey(WalletPath, keycard.P2ExportKeyPublicOnly)
if err != nil {
return nil, err
}

keys.MasterKey, err = kc.exportKey(MasterPath, keycard.P2ExportKeyPublicOnly)
if err != nil {
return nil, err
}

return keys, err
}
13 changes: 13 additions & 0 deletions internal/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,16 @@ type Metadata struct {
Name string `json:"name"`
Wallets []Wallet `json:"wallets"`
}

type LoginKeys struct {
EncryptionPrivateKey *KeyPair `json:"encryptionPrivateKey"`
WhisperPrivateKey *KeyPair `json:"whisperPrivateKey"`
}

type RecoverKeys struct {
LoginKeys
EIP1581key *KeyPair `json:"eip1581"`
WalletRootKey *KeyPair `json:"walletRootKey"`
WalletKey *KeyPair `json:"walletKey"`
MasterKey *KeyPair `json:"masterKey"`
}
31 changes: 29 additions & 2 deletions pkg/session/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,35 @@ func (s *KeycardService) GetMetadata(args *struct{}, reply *GetMetadataResponse)
if s.keycardContext == nil {
return errKeycardServiceNotStarted
}
var err error
reply.Metadata, err = s.keycardContext.GetMetadata()
return err
}

type ExportLoginKeysResponse struct {
Keys *internal.LoginKeys `json:"keys"`
}

func (s *KeycardService) ExportLoginKeys(args *struct{}, reply *ExportLoginKeysResponse) error {
if s.keycardContext == nil {
return errKeycardServiceNotStarted
}

var err error
reply.Keys, err = s.keycardContext.ExportLoginKeys()
return err
}

type ExportRecoveredKeysResponse struct {
Keys *internal.RecoverKeys `json:"keys"`
}

metadata, err := s.keycardContext.GetMetadata()
reply.Metadata = metadata
func (s *KeycardService) ExportRecoverKeys(args *struct{}, reply *ExportRecoveredKeysResponse) error {
if s.keycardContext == nil {
return errKeycardServiceNotStarted
}

var err error
reply.Keys, err = s.keycardContext.ExportRecoverKeys()
return err
}

0 comments on commit 2925ae2

Please sign in to comment.