import (
"context"
+ "fmt"
"testing"
+ "time"
"github.com/bytom/blockchain/account"
"github.com/bytom/blockchain/asset"
"github.com/bytom/blockchain/pin"
+ "github.com/bytom/blockchain/pseudohsm"
+ "github.com/bytom/blockchain/txbuilder"
"github.com/bytom/blockchain/txdb"
cfg "github.com/bytom/config"
"github.com/bytom/consensus"
+ "github.com/bytom/crypto/ed25519/chainkd"
"github.com/bytom/protocol"
+ "github.com/bytom/protocol/bc"
"github.com/bytom/protocol/bc/legacy"
dbm "github.com/tendermint/tmlibs/db"
)
+const dirPath = "pseudohsm/testdata/pseudo"
+
func TestHSM(t *testing.T) {
ctx := context.Background()
config := cfg.DefaultConfig()
genesisBlock.UnmarshalText(consensus.InitBlock())
txPool := protocol.NewTxPool()
chain, err := protocol.NewChain(ctx, genesisBlock.Hash(), store, txPool, nil)
-
- accountsDB := dbm.NewDB("account", config.DBBackend, config.DBDir())
+ if err != nil {
+ t.Fatal(err)
+ }
accUTXODB := dbm.NewDB("accountutxos", config.DBBackend, config.DBDir())
pinStore = pin.NewStore(accUTXODB)
err = pinStore.LoadAll(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ accountsDB := dbm.NewDB("account", config.DBBackend, config.DBDir())
accounts = account.NewManager(accountsDB, chain, pinStore)
assetsDB := dbm.NewDB("asset", config.DBBackend, config.DBDir())
assets = asset.NewRegistry(assetsDB, chain)
+ hsm, err := pseudohsm.New(dirPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ xpub1, err := hsm.XCreate("xpub1", "password")
+ if err != nil {
+ t.Fatal(err)
+ }
+ xpub2, err := hsm.XCreate("xpub2", "password")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ acct1, err := accounts.Create(ctx, []chainkd.XPub{xpub1.XPub}, 1, "acc1", nil, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ acct2, err := accounts.Create(ctx, []chainkd.XPub{xpub2.XPub}, 1, "acc2", nil, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ assetDef1 := map[string]interface{}{"foo": 1}
+ assetDef2 := map[string]interface{}{"foo": 2}
+
+ asset1, err := assets.Define(ctx, []chainkd.XPub{xpub1.XPub}, 1, assetDef1, "foo1", nil, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ asset2, err := assets.Define(ctx, []chainkd.XPub{xpub2.XPub}, 1, assetDef2, "foo2", nil, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ issue1 := txbuilder.Action(assets.NewIssueAction(bc.AssetAmount{AssetId: &asset1.AssetID, Amount: 100}, nil))
+ issue2 := txbuilder.Action(assets.NewIssueAction(bc.AssetAmount{AssetId: &asset2.AssetID, Amount: 200}, nil))
+ spend1 := accounts.NewControlAction(bc.AssetAmount{AssetId: &asset1.AssetID, Amount: 100}, acct1.ID, nil)
+ spend2 := accounts.NewControlAction(bc.AssetAmount{AssetId: &asset2.AssetID, Amount: 200}, acct2.ID, nil)
+
+ tmpl, err := txbuilder.Build(ctx, nil, []txbuilder.Action{issue1, issue2, spend1, spend2}, time.Now().Add(time.Minute))
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = txbuilder.Sign(ctx, tmpl, []chainkd.XPub{xpub1.XPub, xpub2.XPub}, "password", func(_ context.Context, xpub chainkd.XPub, path [][]byte, data [32]byte, password string) ([]byte, error) {
+ sigBytes, err := hsm.XSign(xpub, path, data[:], password)
+ if err != nil {
+ return nil, nil
+ }
+ return sigBytes, err
+ })
+ fmt.Printf("###data: %v#####", *tmpl)
+
/*
c := prottest.NewChain(t)
assets := asset.NewRegistry(db, c, pinStore)
coretest.CreatePins(ctx, t, pinStore)
accounts.IndexAccounts(query.NewIndexer(db, c, pinStore))
go accounts.ProcessBlocks(ctx)
- mockhsm := hsm.New(db)
- xpub1, err := hsm.XCreate(ctx, "")
- if err != nil {
- t.Fatal(err)
- }
- acct1, err := accounts.Create(ctx, []chainkd.XPub{xpub1.XPub}, 1, "", nil, "")
- if err != nil {
- t.Fatal(err)
- }
-
- _, xpub2, err := chainkd.NewXKeys(nil)
- if err != nil {
- t.Fatal(err)
- }
- acct2, err := accounts.Create(ctx, []chainkd.XPub{xpub2}, 1, "", nil, "")
- if err != nil {
- t.Fatal(err)
- }
-
- assetDef1 := map[string]interface{}{"foo": 1}
- assetDef2 := map[string]interface{}{"foo": 2}
-
- asset1ID := coretest.CreateAsset(ctx, t, assets, assetDef1, "", nil)
- asset2ID := coretest.CreateAsset(ctx, t, assets, assetDef2, "", nil)
-
- issueSrc1 := txbuilder.Action(assets.NewIssueAction(bc.AssetAmount{AssetId: &asset1ID, Amount: 100}, nil))
- issueSrc2 := txbuilder.Action(assets.NewIssueAction(bc.AssetAmount{AssetId: &asset2ID, Amount: 200}, nil))
- issueDest1 := accounts.NewControlAction(bc.AssetAmount{AssetId: &asset1ID, Amount: 100}, acct1.ID, nil)
- issueDest2 := accounts.NewControlAction(bc.AssetAmount{AssetId: &asset2ID, Amount: 200}, acct2.ID, nil)
- tmpl, err := txbuilder.Build(ctx, nil, []txbuilder.Action{issueSrc1, issueSrc2, issueDest1, issueDest2}, time.Now().Add(time.Minute))
- if err != nil {
- t.Fatal(err)
- }
coretest.SignTxTemplate(t, ctx, tmpl, &testutil.TestXPrv)
err = txbuilder.FinalizeTx(ctx, c, g, tmpl.Transaction)
if err != nil {
package pseudohsm
import (
- _ "encoding/hex"
- //"encoding/json"
"fmt"
"io/ioutil"
"os"
keytype = "bytom_kd"
)
+// XKey struct type for keystore file
type XKey struct {
- Id uuid.UUID
+ ID uuid.UUID
KeyType string
Alias string
XPrv chainkd.XPrv
type encryptedKeyJSON struct {
Crypto cryptoJSON `json:"crypto"`
- Id string `json:"id"`
+ ID string `json:"id"`
Type string `json:"type"`
Version int `json:"version"`
Alias string `json:"alias"`
- XPub string `json:"xpub"`
+ XPub string `json:"xpub"`
}
type cryptoJSON struct {
Salt string `json:"salt"`
}
-/*
-func (k *XKey) MarshalJSON() (j []byte, err error) {
- jStruct := plainKeyJSON{
- hex.EncodeToString(k.Address[:]),
- hex.EncodeToString(k.XPrv[:]),
- hex.EncodeToString(k.XPub[:]),
- k.Id.String(),
- k.KeyType,
- version,
- }
- j, err = json.Marshal(jStruct)
- return j, err
-}
-
-
-func (k *XKey) UnmarshalJSON(j []byte) (err error) {
- keyJSON := new(plainKeyJSON)
- err = json.Unmarshal(j, &keyJSON)
- if err != nil {
- return err
- }
- u := new(uuid.UUID)
- *u = uuid.Parse(keyJSON.Id)
- k.Id = *u
- addr, err := hex.DecodeString(keyJSON.Address)
- if err != nil {
- return err
- }
-
- privkey, err := hex.DecodeString(keyJSON.PrivateKey)
- if err != nil {
- return err
- }
-
- pubkey, err := hex.DecodeString(keyJSON.PublicKey)
- if err != nil {
- return err
- }
-
- ktype, err := hex.DecodeString(keyJSON.Type)
- if err != nil {
- return err
- }
- k.KeyType = hex.EncodeToString(ktype)
- if k.KeyType != keytype {
- return ErrInvalidKeyType
- }
-
- k.Address = common.BytesToAddress(addr)
-
- copy(k.XPrv[:], privkey)
- copy(k.XPub[:], pubkey)
- return nil
-}
-*/
func writeKeyFile(file string, content []byte) error {
// Create the keystore directory with appropriate permissions
// in case it is not present yet.
import (
"bufio"
- "encoding/json"
"encoding/hex"
+ "encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"sync"
"time"
- "github.com/bytom/crypto/ed25519/chainkd"
+ "github.com/bytom/crypto/ed25519/chainkd"
)
// Minimum amount of time between cache reloads. This limit applies if the platform does
func (s keysByFile) Less(i, j int) bool { return s[i].File < s[j].File }
func (s keysByFile) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-// AmbiguousAddrError is returned when attempting to unlock
+// AmbiguousKeyError is returned when attempting to unlock
// an XPub for which more than one file exists.
type AmbiguousKeyError struct {
- Pubkey string
+ Pubkey string
Matches []XPub
}
buf = new(bufio.Reader)
keys []XPub
keyJSON struct {
- Alias string `json:"alias"`
- XPub chainkd.XPub `json:"xpub"`
+ Alias string `json:"alias"`
+ XPub chainkd.XPub `json:"xpub"`
}
)
for _, fi := range files {
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
/*
-
This key store behaves as KeyStorePlain with the difference that
the private key is encrypted and on disk uses another JSON encoding.
-
-The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
-
*/
package pseudohsm
const (
keyHeaderKDF = "scrypt"
- // n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
+ // StandardScryptN n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
StandardScryptN = 1 << 18
+ // StandardScryptP fit above
StandardScryptP = 1
- // n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU.
+ // LightScryptN n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU.
LightScryptN = 1 << 12
+ //LightScryptP fit above
LightScryptP = 6
scryptR = 8
func (ks keyStorePassphrase) JoinPath(filename string) string {
if filepath.IsAbs(filename) {
return filename
- } else {
- return filepath.Join(ks.keysDirPath, filename)
}
+ return filepath.Join(ks.keysDirPath, filename)
}
// EncryptKey encrypts a key using the specified scrypt parameters into a json
}
encryptedKeyJSON := encryptedKeyJSON{
cryptoStruct,
- key.Id.String(),
+ key.ID.String(),
key.KeyType,
version,
key.Alias,
}
// Depending on the version try to parse one way or another
var (
- keyBytes, keyId []byte
+ keyBytes, keyID []byte
err error
)
k := new(encryptedKeyJSON)
return nil, err
}
- keyBytes, keyId, err = decryptKey(k, auth)
+ keyBytes, keyID, err = decryptKey(k, auth)
// Handle any decryption errors and return the key
if err != nil {
return nil, err
//key := crypto.ToECDSA(keyBytes)
return &XKey{
- Id: uuid.UUID(keyId),
+ ID: uuid.UUID(keyID),
XPrv: xprv,
XPub: xpub,
KeyType: k.Type,
}, nil
}
-func decryptKey(keyProtected *encryptedKeyJSON, auth string) (keyBytes []byte, keyId []byte, err error) {
+func decryptKey(keyProtected *encryptedKeyJSON, auth string) (keyBytes []byte, keyID []byte, err error) {
if keyProtected.Version != version {
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
}
return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
}
- keyId = uuid.Parse(keyProtected.Id)
+ keyID = uuid.Parse(keyProtected.ID)
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
if err != nil {
return nil, nil, err
if err != nil {
return nil, nil, err
}
- return plainText, keyId, err
+ return plainText, keyID, err
}
func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
package pseudohsm
import (
- _ "fmt"
"os"
"path/filepath"
"strconv"
ErrNoKey = errors.New("key not found")
ErrInvalidKeySize = errors.New("key invalid size")
ErrTooManyAliasesToList = errors.New("requested aliases exceeds limit")
- ErrAmbiguousAlias = errors.New("multiple keys match alias")
+ ErrAmbiguousAlias = errors.New("multiple keys match alias")
ErrDecrypt = errors.New("could not decrypt key with given passphrase")
ErrInvalidKeyType = errors.New("key type stored invalid")
)
+// HSM type for storing pubkey and privatekey
type HSM struct {
cacheMu sync.Mutex
keyStore keyStore
kdCache map[chainkd.XPub]chainkd.XPrv
}
+// XPub type for pubkey for anyone can see
type XPub struct {
- Alias string `json:"alias"`
- XPub chainkd.XPub `json:"xpub"`
- File string `json:"file"`
+ Alias string `json:"alias"`
+ XPub chainkd.XPub `json:"xpub"`
+ File string `json:"file"`
}
+// New method for HSM struct
func New(keypath string) (*HSM, error) {
keydir, _ := filepath.Abs(keypath)
return &HSM{
}
id := uuid.NewRandom()
key := &XKey{
- Id: id,
+ ID: id,
KeyType: "bytom_kd",
XPub: xpub,
XPrv: xprv,
Alias: alias,
}
- file := h.keyStore.JoinPath(keyFileName(key.Id.String()))
+ file := h.keyStore.JoinPath(keyFileName(key.ID.String()))
if err := h.keyStore.StoreKey(file, key, auth); err != nil {
return nil, false, errors.Wrap(err, "storing keys")
}
return xpb, xkey, err
}
-// Update alias of an existing xpub
+// UpdateAlias of an existing xpub
func (h *HSM) UpdateAlias(xpub chainkd.XPub, auth, newAlias string) error {
xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
if err != nil {
return h.keyStore.StoreKey(xpb.File, xkey, auth)
}
-// Update changes the passphrase of an existing xpub
+// ResetPassword the passphrase of an existing xpub
func (h *HSM) ResetPassword(xpub chainkd.XPub, auth, newAuth string) error {
xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
if err != nil {
+++ /dev/null
-{"crypto":{"cipher":"aes-128-ctr","ciphertext":"9f0a30d58aaa2a3d96a44f97cbd472f14f2de786b1bd3bed276c31760519af8ffd69bf9d8d4a0dc647a7b7a1231e3b623e22765c3616c7e1b4381f159b1515b4","cipherparams":{"iv":"4dd376faa9159de630f2a1ae05f0a677"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"af9d5ddf380fa4211a06ed6e69a4ff646829f7be62b4964b094bf5e4bf2ec97c"},"mac":"16251256dafb629aca3d697a8ee35cf83a96e0a232079084695ca4fa71c7e821"},"id":"54b8b8e0-ba1e-43ad-9287-812ecb68e81e","type":"bytom_kd","version":1,"alias":"hsmtest","xpub":"332a650827fbb5de97393df0ebb6a8fb5bef1efffb40ef3c5ff5a20f1fcbd6c90552878e5bcc78809d3c044dcbcab78b19032c53d6b625f09aa27423d48218f4"}
\ No newline at end of file
+++ /dev/null
-{"crypto":{"cipher":"aes-128-ctr","ciphertext":"1a0fb96f5ef70289a1cfa3763625cfb186c49590cc216cb4c67256398113ae10db58516513a99ed20af35cd11aff91190d6d928999da3a2309c441da84c9c6c1","cipherparams":{"iv":"f1c149019c2864c225e122cea52140f5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"ec8019297fefd45ff40ddafa23796e3e076506d6f57b9302dec67e8da50cd30b"},"mac":"7ec9683cdc8b82f8275de04d4835c1261a9e1cae799f5f7e35306f83a9d5607f"},"id":"61afcc3e-e733-47d9-b8ae-7bf1a4bf82a0","type":"bytom_kd","version":1,"alias":"hsmtest2","xpub":"74bf1e5f1351d30cd20c8762243cd5afe386d284abf5feee6f31839e02e2a30e0b9a012139c506215e90a6222187377bd536fafacc142d98e27aca1ef5f108f8"}
\ No newline at end of file