srkeyring

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2020 License: MIT Imports: 13 Imported by: 0

README

srkeyring

PkgGoDev

Provides Go functions for implementing HDKD (Hierarchical Deterministic Key Derivation) using sr25519 (Schnorr over Ristretto25519) which is compatible with Substrates sr25519 key generation and command line utility subkey.

Supports Substrates SecretURI key derivation format specified as <mnemonic, mini-secret, or SS58 address>[//hard-derivation][/soft-derivation][///password] and SS58 public address formatting.

Requirements

Go v1.13 or newer

Usage

go get -u github.com/swdee/srkeyring

Examples

Note: In the following examples error handling is ignored for brevity.

New KeyRing

Generate new KeyRing (Private and Public keys) from randomly generated Mnemonic of specified word count.

package main

import (
    "github.com/swdee/srkeyring"  
    "log"    
)

func main() {
    // generate keyring with random 12 word mnemonic
    kr, _ := srkeyring.Generate(12, srkeyring.NetSubstrate{})

    // output keyring details
    mnemonic, _ := kr.Mnemonic()
    seed, _ := kr.SeedHex()
    pub := kr.PublicHex()
    ss58, _ := kr.SS58Address()  

    log.Printf("Mnemonic Phrase: %s", mnemonic)
    log.Printf("Seed: %s", seed)
    log.Printf("Public Key: %s", pub)
    log.Printf("SS58 Address: %s", ss58)
}
KeyRing from Secret URI with Signing

Create KeyRing from Secret URI, output public SS58 address, and sign message. Verification of signature is performed from KeyRing generated from public SS58 formatted address.

package main

import (
    "github.com/swdee/srkeyring"  
    "log"    
)

func main() {
    // generate keyring from 12 word mnemonic
    secretURI := "zebra extra skill occur rose muscle reveal robust cigar tilt jungle coral"
    kr, _ := srkeyring.FromURI(secretURI, srkeyring.NetSubstrate{})

    // output public SS58 formatted address
    ss58, _ := kr.SS58Address()  
    log.Printf("SS58 Address: %s", ss58)

    // sign message
    msg := []byte("setec astronomy")
    sig, _ := kr.Sign(kr.SigningContext(msg))
        
    // create new keyring from SS58 public address to verify message signature
    verkr, _ := srkeyring.FromURI(ss58, srkeyring.NetSubstrate{})
    
    if !verkr.Verify(verkr.SigningContext(msg), sig) {
        log.Fatalf("Error invalid signature for message")
    } else {
        log.Print("Signature is valid!")
    }
}
Hard/Soft Key Derivation

A more complex example uses Hard and Soft key derivation for generating a Master KeyRing from which Child KeyRing's are created. This enables the following type of scenario;

  • Master KeyRing is held by company executives and enables them to control all Child and GrandChild keys.
  • Child KeyRing is generated from Master KeyRing and given to the Website Development department of a Company, enabling them to control all GrandChild keys.
  • GrandChild KeyRing is created from Child KeyRing public address to create unique addresses to receive payments on the companies ecommerce website.

This hierarchical structure provides privilege separation within the company. Only public keys are present on the website servers, so no private keys are recoverable if the server infrastructure is hacked. The Website Development department only has control over GrandChild keys under its department and no other departments Child keys within the company.

package main

import (
    "github.com/swdee/srkeyring"     
    "fmt"
    "log"
)

func main() {
	// generate master keyring with random 24 word mnemonic
	masterKr, _ := srkeyring.Generate(24, srkeyring.NetSubstrate{})
	masterMnemonic, _ := masterKr.Mnemonic()

	// generate child keyring for website development department using Hard key
	childUri := fmt.Sprintf("%s//webdev", masterMnemonic)
	childKr, _ := srkeyring.FromURI(childUri, srkeyring.NetSubstrate{})

	// seed from child keyring is provided to website development department
	// if you would like them to have control over grandchild keyring's
	webdevSeed, _ := childKr.SeedHex()

	// if no control is to be given to website development department then only
	// provide the child KeyRing's public SS58 address
	webdevSS58, _ := childKr.SS58Address()

	// website is configured to generate unique addresses through grandchild keys
	// to receive payment from the child keyring's public ss58 address using
	// a Soft key
	payUri := fmt.Sprintf("%s/payment/42", webdevSS58)
	payKr, _ := srkeyring.FromURI(payUri, srkeyring.NetSubstrate{})

	// payment address is given to customer
	payAddr, _ := payKr.SS58Address()

	// the website development department can then generate the payment address
	// using Secret URI
	webdevClaimUri := fmt.Sprintf("%s/payment/42", webdevSeed)

	// payment address can be generated from master keyring using Secret URI
	masterClaimUri := fmt.Sprintf("%s//webdev/payment/42", masterMnemonic)

	log.Printf("payAddr: %s", payAddr)
	log.Printf("webdevClaimUri: \"%s\"", webdevClaimUri)
	log.Printf("masterClaimUri: \"%s\"", masterClaimUri)
}
Alternative Networks

This library only implements key generation for the Substrate network, to generate keys for other networks such as Polkadot Mainnet implement the Network interface.

package main

import (
	"github.com/swdee/srkeyring"
	"log"
)

func main() {
	// generate keyring with random 12 word mnemonic
	kr, _ := srkeyring.Generate(12, NetPolkadot{})

	// output keyring details
	mnemonic, _ := kr.Mnemonic()
	seed, _ := kr.SeedHex()
	pub := kr.PublicHex()
	ss58, _ := kr.SS58Address()

	log.Printf("Mnemonic Phrase: %s", mnemonic)
	log.Printf("Seed: %s", seed)
	log.Printf("Public Key: %s", pub)
	log.Printf("SS58 Address: %s", ss58)
}

// force NetPolkadot to implement Network interface
var _ srkeyring.Network = &NetPolkadot{}

// NetPolkadot implements the Network interface to define polkadots mainnet
// settings
type NetPolkadot struct{}

// Name returns the network name used in the KeyRing SigningContext transcript
func (n NetPolkadot) Name() string {
	return "polkadot"
}

// Version returns the network version number used in SS58 address formatting
func (n NetPolkadot) Version() uint8 {
	return 0
}

// AddressPrefix returns a prefix to apply to hex encoded addresses for
// public key and private seed
func (n NetPolkadot) AddressPrefix() srkeyring.HexPrefix {
	return "0x"
}

// ChecksumStart is the starting byte position of the blake2d checksum
// calculated when generating the SS58 address checksum
func (n NetPolkadot) ChecksumStart() int {
	return 0
}

// ChecksumEnd is the end byte position of the blake2d checksum
// calculated when generating the SS58 address checksum
func (n NetPolkadot) ChecksumEnd() int {
	return 2
}

Benchmark

A comparison between regular Sign/Verify and VRF equivalents show the addition overhead involved with the VRF scheme.

$ go test -bench=.

BenchmarkSign-8             9520            142143 ns/op            1784 B/op         38 allocs/op
BenchmarkVerify-8           6644            175140 ns/op            1408 B/op         30 allocs/op
BenchmarkVrfSign-8          2770            429305 ns/op            2656 B/op         59 allocs/op
BenchmarkVrfVerify-8        3753            319850 ns/op            5320 B/op         51 allocs/op

Inspiration

This library started off as a refactor of go-subkey with the following enhancements and changes.

  • Decoding of SS58 Addresses to raw bytes
  • Unit testing across entire code based
  • Shifted overlap of Soft and Hard key derivation to upstream package go-schnorrkel in #PR19
  • Supports deriving child public keys from public SS58 address
  • Convenience functions to output Public and Secret keys hex encoded
  • Functions to access KeyRing Seed bytes
  • Added VRF (Verifiable Random Function) Signing and Verifying
  • Implemented new random KeyRing generation
  • Decoupled Network settings and implemented by interface

Documentation

Overview

Package srkeyring provides functions to implementation HDKD (Hierarchical Deterministic Key Derivation) using sr25519 (Schnorr over Ristretto25519)

Compatible with Substrates key generation and command line utility subkey

Supports SS58 address formatting and SecretURI key derivation format specified as; `<mnemonic, mini-secret, or SS58 address>[//hard-derivation][/soft-derivation][///password]`

Index

Constants

View Source
const (

	// MiniSecretKeyLength is the length of the MiniSecret Key
	MiniSecretKeyLength = 32

	// SecretKeyLength is the length of the SecretKey
	SecretKeyLength = 64
)

Variables

View Source
var (
	ErrDecodingSecretHex = errors.New("Error decoding secret hex phrase")
	ErrUnknownPhraseType = errors.New("SURI phrase type is unknown or not set")
	ErrSeedNotAvailable  = errors.New("Unable to get seed from public address data")
	ErrInvalidWordCount  = errors.New("An invalid WordCount was given, valid values are 12, 15, 18, 21, or 24")
	ErrNonMnemonic       = errors.New("Error KeyRing was generated from non mnemonic source")
)
View Source
var (

	// Errors
	ErrInvalidSURIFormat = errors.New("Secret URI format is invalid")
	ErrEmptyPhrase       = errors.New("The parsed Secret URI has an empty phrase")
	ErrInvalidByteLength = errors.New("An invalid number of bytes has been given")
)

Functions

func DecodeHex

func DecodeHex(str string, prefix HexPrefix) (seed []byte, ok bool)

DecodeHex decodes the hex string to bytes and truncates any prefix used

func DecodeSS58Address

func DecodeSS58Address(addr string, net Network, ctype ChecksumType) ([32]byte, error)

DecodeSS58Address takes a string and checks if it is a validly encoded SS58 address and returns the raw address in bytes

func EncodeHex

func EncodeHex(raw []byte, prefix HexPrefix) string

EncodeHex encodes the raw bytes into a hex encoded string and appends any prefix required

func SS58Address

func SS58Address(addr [32]byte, net Network, ctype ChecksumType) (string, error)

SS58Address derives ss58 address from the address, network, and checksumType

Types

type ChecksumType

type ChecksumType int

ChecksumType represents the one or more checksum types. More here: https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)#checksum-types

const (

	// SS58Checksum uses the concat(address-type, address) as blake2b hash pre-image
	SS58Checksum ChecksumType = iota
	// AccountID uses the address as the blake2b hash pre-image
	AccountID
)

type DerivablePrivateKey

type DerivablePrivateKey bool

DerivablePrivateKey indicates if the returned sr25519.DerivableKey is a PublicKey or SecretKey

type HexPrefix

type HexPrefix string

HexPrefix defines a prefix used in the hex encoded output

type KeyRing

type KeyRing struct {
	// contains filtered or unexported fields
}

KeyRing defines a key pair from a derive Secret URI

func FromPublic

func FromPublic(b [32]byte, net Network) (*KeyRing, error)

FromPublic returns a KeyRing from the raw bytes of a public key

func FromURI

func FromURI(str string, net Network) (*KeyRing, error)

FromURI returns a KeyRing from the given Secret URI

func Generate

func Generate(words int, net Network) (*KeyRing, error)

Generate creates a new KeyRing with a randomly created mnemonic of the specified number of words

func (*KeyRing) Mnemonic

func (k *KeyRing) Mnemonic() (string, error)

Mnemonic returns the mnemonic phrase if the KeyRing was generated by a mnemonic phrase or an error if generated by other source

func (*KeyRing) Public

func (k *KeyRing) Public() [32]byte

Public returns the public key in raw bytes

func (*KeyRing) PublicHex

func (k *KeyRing) PublicHex() string

PublicHex returns the public key hex encoded

func (*KeyRing) SS58Address

func (k *KeyRing) SS58Address() (string, error)

SS58Address returns the public key encoded as a SS58 address

func (*KeyRing) Secret

func (k *KeyRing) Secret() [32]byte

Secret returns the private secret key in raw bytes

func (*KeyRing) SecretHex

func (k *KeyRing) SecretHex() string

SecretHex returns the private secret key hex encoded

func (*KeyRing) Seed

func (k *KeyRing) Seed() ([32]byte, error)

Seed returns the seed generated from the mnemonic in raw bytes

func (*KeyRing) SeedHex

func (k *KeyRing) SeedHex() (string, error)

SeedHex returns the seed generated from the mnemonic hex encoded

func (*KeyRing) Sign

func (k *KeyRing) Sign(t *merlin.Transcript) (signature [64]byte, err error)

Sign signs the message using the secret key

func (*KeyRing) SigningContext

func (k *KeyRing) SigningContext(msg []byte) *merlin.Transcript

SigningContext returns the transcript used for message signing for the Network set

func (*KeyRing) Verify

func (k *KeyRing) Verify(t *merlin.Transcript, signature [64]byte) bool

Verify the message against the signature

func (*KeyRing) VrfSign

func (k *KeyRing) VrfSign(t *merlin.Transcript) (output [32]byte, proof [64]byte, err error)

VrfSign creates a signed output and proof

func (*KeyRing) VrfVerify

func (k *KeyRing) VrfVerify(t *merlin.Transcript, output [32]byte, proof [64]byte) (
	bool, error)

VrfVerify verifies if the given proof and output are valid against the public key

type NetSubstrate

type NetSubstrate struct{}

NetSubstrate implements the Network interface to define Substrates mainnet settings

func (NetSubstrate) AddressPrefix

func (n NetSubstrate) AddressPrefix() HexPrefix

AddressPrefix returns a prefix to apply to hex encoded addresses for public key and private seed

func (NetSubstrate) ChecksumEnd

func (n NetSubstrate) ChecksumEnd() int

ChecksumEnd is the end byte position of the blake2d checksum calculated when generating the SS58 address checksum

func (NetSubstrate) ChecksumStart

func (n NetSubstrate) ChecksumStart() int

ChecksumStart is the starting byte position of the blake2d checksum calculated when generating the SS58 address checksum

func (NetSubstrate) Name

func (n NetSubstrate) Name() string

Name returns the network name used in the KeyRing SigningContext transcript

func (NetSubstrate) Version

func (n NetSubstrate) Version() uint8

Version returns the network version number used in SS58 address formatting

type Network

type Network interface {
	// Name returns the network name used in the KeyRing SigningContext transcript
	Name() string
	// Version returns the network version number used in SS58 address formatting
	// see https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)#checksum-types
	// section Address Type
	Version() uint8
	// AddressPrefix returns a prefix to apply to hex encoded addresses for
	// public key and private seed
	AddressPrefix() HexPrefix
	// ChecksumStart is the starting byte position of the blake2d checksum
	// calculated when generating the SS58 address checksum.  Valid ranges
	// are 0 to 30.  Standard value is 0.
	ChecksumStart() int
	// ChecksumEnd is the end byte position of the blake2d checksum
	// calculated when generating the SS58 address checksum.  Valid ranges
	// are 2 to 32, where ChecksumEnd must be a higher number than
	// ChecksumStart.  Standard value is 2.
	ChecksumEnd() int
}

Network defines the interface for a specific networks settings to be used for key generation and address formatting

type PhraseType

type PhraseType int

PhraseType specifies the type of data that was passed as the phrase component in a Secret URI

const (
	SecretHex PhraseType = iota + 1
	SS58Public
	Mnemonic
	RawPublicKey
)

type SecretURI

type SecretURI struct {
	Phrase   string
	Path     string
	Password string
	Network  Network
	Type     PhraseType
}

SecretURI defines a struct consisting of the parts of a Secret URI

func NewSecretURI

func NewSecretURI(suri string, net Network) (*SecretURI, error)

NewSecretURI takes a given string Secret URI and splits it into Phrase, Path, and Password components given the format <phrase><path>///<password> and returns it as a struct

func (*SecretURI) DerivableKey

func (s *SecretURI) DerivableKey() (sr25519.DerivableKey, DerivablePrivateKey, error)

DerivableKey returns a DerivableKey from the Secret URI

func (*SecretURI) GetJunctions

func (s *SecretURI) GetJunctions() ([]*junction, error)

GetJunctions returns the junction parts of the path component of the Secret URI.

type WordCount

type WordCount int

WordCount defines the type for specifying the number of words in a mnemonic. Valid values are 12, 15, 18, 21, or 24 words

Jump to

Keyboard shortcuts

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