hdw

package module
v0.0.0-...-0b9e0a5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2022 License: MIT Imports: 7 Imported by: 1

README

HDW

import "github.com/ecadlabs/hdw/ecdsa"

The module implements

Master seed generation

mnemonic := "nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura"
seed := hdw.NewSeedFromMnemonic(mnemonic, "")

SLIP-10 / ECDSA

Directly supported curves are Secp256k1 and NIST P-256. The ecdsa package is agnostic to the curve implementation as the curve is detected by its parameters. Other curves can be used as well with custom HMAC key phrase, like "curve_name seed" (see the package documentation).

package main

import (
	stdecdsa "crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"

	"github.com/ecadlabs/hdw"
	"github.com/ecadlabs/hdw/ecdsa"
)

var seedData = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"

func main() {
	// alternatively use hdw.NewSeedFromMnemonic
	seed, err := hex.DecodeString(seedData)
	if err != nil {
		panic(err)
	}

	// generate the root key
	root, err := ecdsa.NewKeyFromSeed(seed, elliptic.P256())
	if err != nil {
		panic(err)
	}

	path := hdw.Path{0, 1, 2}
	// generate the derivative child private key
	priv, err := root.DerivePath(path)
	if err != nil {
		panic(err)
	}

	digest := sha256.Sum256([]byte("text"))
	sig, err := priv.Sign(rand.Reader, digest[:], nil)
	if err != nil {
		panic(err)
	}

	// get the corresponding public key
	pub := priv.Public()

	// verify the signature
	ok := stdecdsa.VerifyASN1(pub.(*stdecdsa.PublicKey), digest[:], sig)
	fmt.Printf("signature ok: %t\n", ok)

	// derive the public key from the root's public
	pub2, err := root.ExtendedPublic().DerivePath(path)
	if err != nil {
		panic(err)
	}
	// verify the signature
	ok = stdecdsa.VerifyASN1(pub2.Naked().(*stdecdsa.PublicKey), digest[:], sig)
	fmt.Printf("signature ok: %t\n", ok)
}

SLIP-10 / Ed25519

package main

import (
	"crypto"
	stded25519 "crypto/ed25519"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"

	"github.com/ecadlabs/hdw"
	"github.com/ecadlabs/hdw/ed25519"
)

var seedData = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"

func main() {
	// alternatively use hdw.NewSeedFromMnemonic
	seed, err := hex.DecodeString(seedData)
	if err != nil {
		panic(err)
	}

	// generate the root key
	root := ed25519.NewKeyFromSeed(seed)

	path := hdw.Path{0 | hdw.Hard, 1 | hdw.Hard, 2 | hdw.Hard}
	// generate the derivative child private key
	priv, err := root.DerivePath(path)
	if err != nil {
		panic(err)
	}

	digest := sha256.Sum256([]byte("text"))
	sig, err := priv.Sign(rand.Reader, digest[:], crypto.Hash(0))
	if err != nil {
		panic(err)
	}

	// get the corresponding public key
	pub := priv.Public()

	// verify the signature
	ok := stded25519.Verify(pub.(stded25519.PublicKey), digest[:], sig)
	fmt.Printf("signature ok: %t\n", ok)
}

BIP32-Ed25519

Different implementations of this algorithm may have some deviations from the paper in the root key generation step. bip25519 package implements some of them. Specifically it can use HMAC instead of plain SHA hash (as in Ledger and some other implementations) then it can rehash the result if the seed gives an unusable value (Ledger also does this) instead of returning nil or just clear undesired bits (not recommended).

Warning

Despite in the HMAC+Retry mode the result is identical to one produced by Speculos emulator it's still incompatible with the Ledger firmware

Standard mode
package main

import (
	"crypto"
	"crypto/ed25519"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"

	"github.com/ecadlabs/hdw"
	"github.com/ecadlabs/hdw/bip25519"
)

var seedData = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"

func main() {
	// alternatively use hdw.NewSeedFromMnemonic
	seed, err := hex.DecodeString(seedData)
	if err != nil {
		panic(err)
	}

	// generate the root key
	root := bip25519.NewKeyFromSeed(seed, nil)
	if root == nil {
		panic("unusable seed")
	}

	path := hdw.Path{0, 1, 2}
	// generate the derivative child private key
	priv, err := root.DerivePath(path)
	if err != nil {
		panic(err)
	}

	digest := sha256.Sum256([]byte("text"))
	sig, err := priv.Sign(rand.Reader, digest[:], crypto.Hash(0))
	if err != nil {
		panic(err)
	}

	// get the corresponding public key
	pub := priv.Public()

	// verify the signature
	ok := ed25519.Verify(pub.(ed25519.PublicKey), digest[:], sig)
	fmt.Printf("signature ok: %t\n", ok)

	// derive the public key from the root's public
	pub2, err := root.ExtendedPublic().DerivePath(path)
	if err != nil {
		panic(err)
	}
	// verify the signature
	ok = ed25519.Verify(pub2.Naked().(ed25519.PublicKey), digest[:], sig)
	fmt.Printf("signature ok: %t\n", ok)
}
HMAC mode

The same as above except the root key generation step:

// generate the root key
root := bip25519.NewKeyFromSeed(seed, &bip25519.Options{Mode: bip25519.ModeRetry, HMAC: true})
// no need for nil check

Documentation

Overview

Package hdw provides a unified API for dealing with hierarchical deterministic keys also known as hierarchical deterministic wallets in application to blockchains as defined in BIP-32 and SLIP-10

Index

Constants

View Source
const ChainCodeSize = 32

ChainCodeSize is the length of chain code common for all key types

View Source
const Hard uint32 = 1 << 31

Hard is the flag signifying so called hardened derivation if added to the derivation index. The exact use of it is specific to the chosen algorithm but in general it means that the private key is used to build the hash chain

Variables

View Source
var ErrHardenedPublic = errors.New("hdw: can't use hardened derivation with public key")

ErrHardenedPublic returned if an attempt to use hardened derivation with a public key was made

Functions

func Derive

func Derive(root any, path Path) (any, error)

Derive returns a child key of root using a full path. root must be either PrivateKey or PublicKey

func NewSeedFromMnemonic

func NewSeedFromMnemonic(mnemonic string, password string) []byte

NewSeedFromMnemonic generates the seed value from the mnemonic as specified in BIP-39. The seed is the primary secret used to derive root keys and their derivatives

Types

type Path

type Path []uint32

Path is a BIP-32/SLIP-10 derivation path

func ParsePath

func ParsePath(s string) (Path, error)

ParsePath parses s as a BIP-32/SLIP-10 path, like "m/1'/2"

func (Path) HasPrefix

func (p Path) HasPrefix(prefix Path) bool

HasPrefix reports whether receiver has a given prefix

func (Path) String

func (path Path) String() string

String formats the path to the BIP-32/SLIP-10 textual form

type PrivateKey

type PrivateKey interface {
	crypto.Signer
	// Equal reports whether receiver and x have the same value
	Equal(x crypto.PrivateKey) bool
	// Chain returns the chain code
	Chain() []byte
	// Derive returns a child key of the receiver using a single index
	Derive(index uint32) (PrivateKey, error)
	// Derive returns a child key of the receiver using a full path
	DerivePath(path Path) (PrivateKey, error)
	// Public returns the extended public key corresponding to the receiver
	ExtendedPublic() PublicKey
	// Naked returns the naked private key that can be used with the standard Go crypto library
	Naked() crypto.PrivateKey
}

PrivateKey is an extended private key bearing the chain code required for derivation of child keys

type PublicKey

type PublicKey interface {
	// Equal reports whether receiver and x have the same value
	Equal(x crypto.PublicKey) bool
	// Chain returns the chain code
	Chain() []byte
	// Derive returns a child key of the receiver using single index
	Derive(index uint32) (PublicKey, error)
	// Derive returns a child key of the receiver using a full path
	DerivePath(path Path) (PublicKey, error)
	// Naked returns the naked public key that can be used with the standard Go crypto library
	Naked() crypto.PublicKey
}

PrivateKey is an extended public key bearing the chain code required for derivation of child keys

Directories

Path Synopsis
Package bip25519 deals with BIP32-Ed25519 keys as specified in paper by Khovratovich and Law.
Package bip25519 deals with BIP32-Ed25519 keys as specified in paper by Khovratovich and Law.
ex25519
Package ex25519 provides operations with expanded 512 bit ed25519 private keys.
Package ex25519 provides operations with expanded 512 bit ed25519 private keys.
Package ed25519 deals with SLIP-10/Ed25519 keys.
Package ed25519 deals with SLIP-10/Ed25519 keys.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL