Skip to content

Commit

Permalink
added generation of key by mnemonic
Browse files Browse the repository at this point in the history
  • Loading branch information
QuestofIranon committed Jan 9, 2020
1 parent 5021c0c commit a28d574
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
91 changes: 90 additions & 1 deletion crypto.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package hedera

import (
"crypto/ed25519"
"crypto/hmac"
"crypto/rand"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/pbkdf2"
"strings"

"github.com/hashgraph/hedera-sdk-go/proto"
Expand All @@ -13,6 +18,8 @@ import (
const ed25519PrivateKeyPrefix = "302e020100300506032b657004220420"
const ed25519PubKeyPrefix = "302a300506032b6570032100"

var ErrNoRNG = errors.New("could not retrieve random bytes from the operating system")

type Ed25519PrivateKey struct {
keyData []byte
chainCode []byte
Expand Down Expand Up @@ -58,6 +65,35 @@ func Ed25519PrivateKeyFromBytes(bytes []byte) (Ed25519PrivateKey, error) {
}, nil
}

func Ed25519PrivateKeyFromMnemonic(mnemonic string, passPhrase string) Ed25519PrivateKey {
salt := []byte("mnemonic" + passPhrase)
seed := pbkdf2.Key([]byte(mnemonic), salt, 2048, 64, sha512.New)

h := hmac.New(sha512.New, []byte("ed25519 seed"))
h.Write(seed)
digest := h.Sum(nil)

keyBytes := digest[0:32]
chainCode := digest[32:len(digest)]

// note the index is for derivation, not the index of the slice
for _, index := range []uint32{ 44, 3030, 0, 0 } {
keyBytes, chainCode = deriveChildKey(keyBytes, chainCode, index)
}

var publicKey []byte

copy(publicKey, keyBytes[32:len(keyBytes)])

return Ed25519PrivateKey{
keyData: keyBytes,
chainCode: chainCode,
publicKey: Ed25519PublicKey{
publicKey,
},
}
}

func Ed25519PrivateKeyFromString(s string) (Ed25519PrivateKey, error) {
switch len(s) {
case 64, 128: // private key : public key
Expand Down Expand Up @@ -99,6 +135,59 @@ func Ed25519PublicKeyFromString(s string) (Ed25519PublicKey, error) {
return Ed25519PublicKey{}, fmt.Errorf("invalid public key with length %v", len(s))
}

// SLIP-10/BIP-32 Child Key derivation
func deriveChildKey(parentKey []byte, chainCode []byte, index uint32) ([]byte, []byte) {
h := hmac.New(sha512.New, chainCode)

input := make([]byte, 37)

// 0x00 + parentKey + index(BE)
input[0] = 0

for i, b := range parentKey {
input[1 + i] = b
}

binary.BigEndian.PutUint32(input[33:37], index)

// harden the input
input[33] |= 128

h.Write(input)

digest := h.Sum(nil)

return digest[0:32], digest[32:len(digest)]
}

type MnemonicResult struct {
mnemonic string
}

func (mr MnemonicResult) GenerateKey(passPhrase string) Ed25519PrivateKey {
return Ed25519PrivateKeyFromMnemonic(mr.mnemonic, passPhrase)
}

// Generate a random 24-word mnemonic
func GenerateMnemonic() (*MnemonicResult, error) {
entropy, err := bip39.NewEntropy(256)

if err != nil {
// It is only possible for there to be an error if the operating
// system's rng is unreadable
return nil, ErrNoRNG
}

mnemonic, err := bip39.NewMnemonic(entropy)

if err != nil {
// todo: return proper error
return nil, err
}

return &MnemonicResult{mnemonic}, nil
}

func (sk Ed25519PrivateKey) PublicKey() Ed25519PublicKey {
return sk.publicKey
}
Expand Down
10 changes: 10 additions & 0 deletions crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const testPrivateKeyStr = "302e020100300506032b657004220420db484b828e64b2d8f12ce

const testPublicKeyStr = "302a300506032b6570032100e0c8ec2758a5879ffac226a13c0c516b799e72e35141a0dd828f94d37988a4b7"

// generated by hedera-keygen-java, not used anywhere
const testMnemonic = "inmate flip alley wear offer often piece magnet surge toddler submit right radio absent pear floor belt raven price stove replace reduce plate home";
const testMnemonicKey = "302e020100300506032b657004220420853f15aecd22706b105da1d709b4ac05b4906170c2b9c7495dff9af49e1391da";

func TestEd25519PrivateKeyGenerate(t *testing.T) {
key, err := GenerateEd25519PrivateKey()

Expand Down Expand Up @@ -57,6 +61,12 @@ func TestEd25519PublicKeyExternalSerializationForRawHex(t *testing.T) {
assert.Equal(t, testPublicKeyStr, key.String())
}

func TestEd25519PrivateKeyFromMnemonic(t *testing.T) {
key := Ed25519PrivateKeyFromMnemonic(testMnemonic, "")

assert.Equal(t, testMnemonicKey, key.String())
}

func TestSigning(t *testing.T) {
priKey, err := Ed25519PrivateKeyFromString(testPrivateKeyStr)
pubKey, err := Ed25519PublicKeyFromString(testPublicKeyStr)
Expand Down

0 comments on commit a28d574

Please sign in to comment.