In progress: Use Nimbus keystore

This commit is contained in:
Pedro Pombeiro 2020-01-06 10:30:35 +01:00 committed by Pedro Pombeiro
parent db01f0b3e4
commit d4710faae2
7 changed files with 592 additions and 11 deletions

View File

@ -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
}

View File

@ -1,3 +1,5 @@
// +build !nimbus
// TODO: Make independent version for Nimbus
package account

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -31,6 +31,7 @@ github.com/aristanetworks/goarista v0.0.0-20191106175434-873d404c7f40/go.mod h1:
github.com/aristanetworks/splunk-hec-go v0.3.3/go.mod h1:1VHO9r17b0K7WmOlLb9nTk/2YanvOEnLMUgsFrxBROc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd v0.0.0-20190418232430-6867ff32788a/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
@ -50,7 +51,9 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
@ -103,6 +106,7 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
@ -172,6 +176,7 @@ github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f/go.mod h1:2zXcozF
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
@ -221,17 +226,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=
github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/prometheus v0.0.0-20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
@ -359,8 +365,7 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -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
}