Skip to content

Commit

Permalink
In progress: Use Nimbus keystore
Browse files Browse the repository at this point in the history
  • Loading branch information
pedropombeiro committed Jan 20, 2020
1 parent db01f0b commit d4710fa
Show file tree
Hide file tree
Showing 7 changed files with 592 additions and 11 deletions.
8 changes: 2 additions & 6 deletions account/accounts_nimbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ func (m *Manager) InitKeystore(keydir string) error {
m.mu.Lock()
defer m.mu.Unlock()

// TODO: Wire with the Nimbus keystore
manager, err := makeAccountManager(keydir)
if err != nil {
return err
}
m.keystore, err = makeKeyStore(manager)
var err error
m.keystore, err = makeKeyStore(keydir)
return err
}
2 changes: 2 additions & 0 deletions account/keystore_geth.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build !nimbus

// TODO: Make independent version for Nimbus

package account
Expand Down
51 changes: 51 additions & 0 deletions account/keystore_nimbus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// +build nimbus

package account

import (
// "io/ioutil"
// "os"

// gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
nimbusbridge "github.com/status-im/status-go/eth-node/bridge/nimbus"
"github.com/status-im/status-go/eth-node/types"
)

// // makeAccountManager creates ethereum accounts.Manager with single disk backend and lightweight kdf.
// // If keydir is empty new temporary directory with go-ethereum-keystore will be intialized.
// func makeAccountManager(keydir string) (manager *accounts.Manager, err error) {
// if keydir == "" {
// // There is no datadir.
// keydir, err = ioutil.TempDir("", "nimbus-keystore")
// }
// if err != nil {
// return nil, err
// }
// if err := os.MkdirAll(keydir, 0700); err != nil {
// return nil, err
// }
// config := accounts.Config{InsecureUnlockAllowed: false}
// return accounts.NewManager(&config, keystore.NewKeyStore(keydir, keystore.LightScryptN, keystore.LightScryptP)), nil
// }

// func makeKeyStore(keydir string) (types.KeyStore, error) {
// manager, err := makeAccountManager(keydir)
// if err != nil {
// return err
// }

// backends := manager.Backends(keystore.KeyStoreType)
// if len(backends) == 0 {
// return nil, ErrAccountKeyStoreMissing
// }
// keyStore, ok := backends[0].(*keystore.KeyStore)
// if !ok {
// return nil, ErrAccountKeyStoreMissing
// }

// return gethbridge.WrapKeyStore(keyStore), nil
// }

func makeKeyStore(_ string) (types.KeyStore, error) {
return nimbusbridge.WrapKeyStore(), nil
}
263 changes: 263 additions & 0 deletions eth-node/bridge/nimbus/keystore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// +build nimbus

package nimbusbridge

// https://golang.org/cmd/cgo/

/*
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <libnimbus.h>
*/
import "C"
import (
"crypto/ecdsa"
"encoding/binary"
"errors"
"fmt"
"math/big"
"unsafe"

"github.com/btcsuite/btcd/btcec"
"github.com/pborman/uuid"

"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/extkeys"
)

var (
ErrInvalidSeed = errors.New("seed is invalid")
ErrInvalidKeyLen = errors.New("Nimbus serialized extended key length is invalid")
)

type nimbusKeyStoreAdapter struct {
}

// WrapKeyStore creates a types.KeyStore wrapper over the singleton Nimbus node
func WrapKeyStore() types.KeyStore {
return &nimbusKeyStoreAdapter{}
}

func (k *nimbusKeyStoreAdapter) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (types.Account, error) {
fmt.Println("ImportECDSA")
panic("ImportECDSA")

var privateKeyC unsafe.Pointer
if priv != nil {
privateKeyC = C.CBytes(crypto.FromECDSA(priv))
defer C.free(privateKeyC)
}
passphraseC := C.CString(passphrase)
defer C.free(unsafe.Pointer(passphraseC))

var nimbusAccount C.account
if !C.nimbus_keystore_import_ecdsa((*C.uchar)(privateKeyC), passphraseC, &nimbusAccount) {
return types.Account{}, errors.New("failed to import ECDSA private key")
}
return accountFrom(&nimbusAccount), nil
}

func (k *nimbusKeyStoreAdapter) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (types.Account, error) {
fmt.Println("ImportSingleExtendedKey")
panic("ImportSingleExtendedKey")

extKeyJSONC := C.CString(extKey.String())
defer C.free(unsafe.Pointer(extKeyJSONC))
passphraseC := C.CString(passphrase)
defer C.free(unsafe.Pointer(passphraseC))

var nimbusAccount C.account
if !C.nimbus_keystore_import_single_extendedkey(extKeyJSONC, passphraseC, &nimbusAccount) {
return types.Account{}, errors.New("failed to import extended key")
}
return accountFrom(&nimbusAccount), nil
}

func (k *nimbusKeyStoreAdapter) ImportExtendedKeyForPurpose(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, passphrase string) (types.Account, error) {
fmt.Println("ImportExtendedKeyForPurpose")

passphraseC := C.CString(passphrase)
defer C.free(unsafe.Pointer(passphraseC))
extKeyJSONC := C.CString(extKey.String())
defer C.free(unsafe.Pointer(extKeyJSONC))

var nimbusAccount C.account
if !C.nimbus_keystore_import_extendedkeyforpurpose(C.int(keyPurpose), extKeyJSONC, passphraseC, &nimbusAccount) {
return types.Account{}, errors.New("failed to import extended key")
}
return accountFrom(&nimbusAccount), nil
}

func (k *nimbusKeyStoreAdapter) AccountDecryptedKey(a types.Account, auth string) (types.Account, *types.Key, error) {
fmt.Println("AccountDecryptedKey")
panic("AccountDecryptedKey")

authC := C.CString(auth)
defer C.free(unsafe.Pointer(authC))

var nimbusAccount C.account
err := nimbusAccountFrom(a, &nimbusAccount)
if err != nil {
return types.Account{}, nil, err
}

var nimbusKey C.key
if !C.nimbus_keystore_account_decrypted_key(authC, &nimbusAccount, &nimbusKey) {
return types.Account{}, nil, errors.New("failed to decrypt account key")
}
key, err := keyFrom(&nimbusKey)
if err != nil {
return types.Account{}, nil, err
}
return accountFrom(&nimbusAccount), key, nil
}

func (k *nimbusKeyStoreAdapter) Delete(a types.Account, auth string) error {
fmt.Println("Delete")

var nimbusAccount C.account
err := nimbusAccountFrom(a, &nimbusAccount)
if err != nil {
return err
}

authC := C.CString(auth)
defer C.free(unsafe.Pointer(authC))

if !C.nimbus_keystore_delete(&nimbusAccount, authC) {
return errors.New("failed to delete account")
}

return nil
}

func nimbusAccountFrom(account types.Account, nimbusAccount *C.account) error {
fmt.Println("nimbusAccountFrom")
err := copyAddressToCBuffer(&nimbusAccount.address[0], account.Address.Bytes())
if err != nil {
return err
}
if account.URL == "" {
nimbusAccount.url[0] = C.char(0)
} else if len(account.URL) >= C.URL_LEN {
return errors.New("URL is too long to fit in Nimbus struct")
} else {
copyURLToCBuffer(&nimbusAccount.url[0], account.URL)
}
return err
}

func accountFrom(nimbusAccount *C.account) types.Account {
return types.Account{
Address: types.BytesToAddress(C.GoBytes(unsafe.Pointer(&nimbusAccount.address[0]), C.ADDRESS_LEN)),
URL: C.GoString(&nimbusAccount.url[0]),
}
}

// copyAddressToCBuffer copies a Go buffer to a C buffer without allocating new memory
func copyAddressToCBuffer(dst *C.uchar, src []byte) error {
if len(src) != C.ADDRESS_LEN {
return errors.New("invalid buffer size")
}

p := (*[C.ADDRESS_LEN]C.uchar)(unsafe.Pointer(dst))
for index, b := range src {
p[index] = C.uchar(b)
}

return nil
}

// copyURLToCBuffer copies a Go buffer to a C buffer without allocating new memory
func copyURLToCBuffer(dst *C.char, src string) error {
if len(src)+1 > C.URL_LEN {
return errors.New("URL is too long to fit in Nimbus struct")
}

p := (*[C.URL_LEN]C.uchar)(unsafe.Pointer(dst))
for index := 0; index <= len(src); index++ {
p[index] = C.uchar(src[index])
}

return nil
}

func keyFrom(k *C.key) (*types.Key, error) {
fmt.Println("keyFrom")
if k == nil {
return nil, nil
}

var err error
key := types.Key{
Id: uuid.Parse(C.GoString(&k.id[0])),
}
key.Address = types.BytesToAddress(C.GoBytes(unsafe.Pointer(&k.address[0]), C.ADDRESS_LEN))
key.PrivateKey, err = crypto.ToECDSA(C.GoBytes(unsafe.Pointer(&k.privateKeyID[0]), C.PRIVKEY_LEN))
if err != nil {
return nil, err
}
key.ExtendedKey, err = newExtKeyFromBuffer(C.GoBytes(unsafe.Pointer(&k.extKey[0]), C.EXTKEY_LEN))
if err != nil {
return nil, err
}
return &key, err
}

// newExtKeyFromBuffer returns a new extended key instance from a serialized
// extended key.
func newExtKeyFromBuffer(key []byte) (*extkeys.ExtendedKey, error) {
if len(key) == 0 {
return &extkeys.ExtendedKey{}, nil
}

if len(key) != C.EXTKEY_LEN {
return nil, ErrInvalidKeyLen
}

// The serialized format is:
// version (4) || depth (1) || parent fingerprint (4)) ||
// child num (4) || chain code (32) || key data (33)

payload := key

// Deserialize each of the payload fields.
version := payload[:4]
depth := payload[4:5][0]
fingerPrint := payload[5:9]
childNumber := binary.BigEndian.Uint32(payload[9:13])
chainCode := payload[13:45]
keyData := payload[45:78]

// The key data is a private key if it starts with 0x00. Serialized
// compressed pubkeys either start with 0x02 or 0x03.
isPrivate := keyData[0] == 0x00
if isPrivate {
// Ensure the private key is valid. It must be within the range
// of the order of the secp256k1 curve and not be 0.
keyData = keyData[1:]
keyNum := new(big.Int).SetBytes(keyData)
if keyNum.Cmp(btcec.S256().N) >= 0 || keyNum.Sign() == 0 {
return nil, ErrInvalidSeed
}
} else {
// Ensure the public key parses correctly and is actually on the
// secp256k1 curve.
_, err := btcec.ParsePubKey(keyData, btcec.S256())
if err != nil {
return nil, err
}
}

return &extkeys.ExtendedKey{
Version: version,
KeyData: keyData,
ChainCode: chainCode,
FingerPrint: fingerPrint,
Depth: depth,
ChildNumber: childNumber,
IsPrivate: isPrivate,
}, nil
}
1 change: 1 addition & 0 deletions eth-node/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ replace github.com/status-im/status-go/whisper/v6 => ../whisper
replace github.com/status-im/status-go/waku => ../waku

require (
github.com/btcsuite/btcd v0.20.1-beta
github.com/ethereum/go-ethereum v1.9.5
github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f
github.com/pborman/uuid v1.2.0
Expand Down
Loading

0 comments on commit d4710fa

Please sign in to comment.