slip39

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2024 License: MIT Imports: 16 Imported by: 1

README

go-slip39

PkgGoDev MIT License Go Build Status Go Report Card

A SLIP-0039 library for Go. Specifically, this library is a port of the SLIP-0039 python reference implementation to Go.

SLIP-0039 describes a standard and interoperable implementation of Shamir's secret sharing (SSS). SSS splits a secret into unique parts which can be distributed among participants, and requires a specified minimum number of parts to be supplied in order to reconstruct the original secret. Knowledge of fewer than the required number of parts does not leak information about the secret.

Documentation

Package documentation can be found at pkg.go.dev.

Specification

See https://github.com/satoshilabs/slips/blob/master/slip-0039.md for the full SLIP-0039 specification.

Security

This implementation is not using any hardening techniques. Secrets are passed in the open, and calculations are most likely vulnerable to side-channel attacks. The code has not been audited by security professionals. Use at your own risk.

At the very least, you should not use this library for non-testing purposes or with sensitive secrets outside an air-gapped live system such as Tails.

CLI

No executables are provided with this library. You can write your own tools pretty simply, or there is a sister project called seedkit which provides a command line tool that uses this library.

Usage

package main

import (
	"encoding/hex"
	"fmt"

	"github.com/gavincarr/go-slip39"
)

func main() {
	masterSecret := "bb54aac4b89dc868ba37d9cc21b2cece"
	passphrase := "TREZOR"

	// Generate a single group of 3 of 5 shares for masterSecret
	masterSecretBytes, _ := hex.DecodeString(masterSecret)
	groupCount := 1
	memberGroupParams := []slip39.MemberGroupParameters{{3, 5}}
	groups, _ := slip39.GenerateMnemonicsWithPassphrase(
		groupCount,
		memberGroupParams,
		masterSecretBytes,
		[]byte(passphrase),
	)
	fmt.Println(len(groups[0]))
	// Output: 5

	// Combine 3 of the 5 shares to recover the master secret
	shares := []string{groups[0][0], groups[0][2], groups[0][4]}
	recoveredSecret, _ := slip39.CombineMnemonicsWithPassphrase(
		shares,
		[]byte(passphrase),
	)
	fmt.Println(hex.EncodeToString(recoveredSecret))
	// Output: bb54aac4b89dc868ba37d9cc21b2cece
}

Documentation

Overview

Package slip39 is a Go implementation of the SLIP-0039 spec, implementing Shamir's Secret Sharing Scheme.

The official SLIP-0039 spec can be found at https://github.com/satoshilabs/slips/blob/master/slip-0039.md

Example
masterSecret := "bb54aac4b89dc868ba37d9cc21b2cece"
passphrase := "TREZOR"

// Generate a single group of 3 of 5 shares for masterSecret
masterSecretBytes, _ := hex.DecodeString(masterSecret)
groupCount := 1
memberGroupParams := []slip39.MemberGroupParameters{{3, 5}}
groups, _ := slip39.GenerateMnemonicsWithPassphrase(
	groupCount,
	memberGroupParams,
	masterSecretBytes,
	[]byte(passphrase),
)
fmt.Println(len(groups[0]))

// Combine 3 of the 5 shares to recover the master secret
shares := []string{groups[0][0], groups[0][2], groups[0][4]}
recoveredSecret, _ := slip39.CombineMnemonicsWithPassphrase(
	shares,
	[]byte(passphrase),
)
fmt.Println(hex.EncodeToString(recoveredSecret))
Output:

5
bb54aac4b89dc868ba37d9cc21b2cece

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidChecksum is returned when the checksum on a mnemonic is invalid
	ErrInvalidChecksum = errors.New("invalid checksum")

	// ErrInvalidMnemonic is returned when a mnemonic is invalid
	ErrInvalidMnemonic = errors.New("invalid mnemonic")

	// ErrInvalidCommonParameters is returned when the mnemonics in a group differ
	// in their identifiers, group thresholds, or group counts
	ErrInvalidCommonParameters = errors.New("all mnemonics must begin with the same 2 words, must have the same group threshold and the same group count")

	// ErrEmptyShareGroup is returned when a share group is empty
	ErrEmptyShareGroup = errors.New("the share group is empty")

	// ErrMaxShareCountExceeded is returned when too many shares are provided
	ErrMaxShareCountExceeded = fmt.Errorf("the number of shares cannot exceed %d", maxShareCount)

	// ErrInvalidGroupThreshold is returned when the group threshold is invalid
	ErrInvalidGroupThreshold = errors.New("group threshold must be a positive integers and must not exceed the number of groups")

	// ErrInvalidThreshold is returned when the threshold is invalid
	ErrInvalidThreshold = errors.New("the requested threshold must be a positive integers and must not exceed the number of shares")

	// ErrInvalidSingleMemberThreshold is returned when the group threshold is invalid
	ErrInvalidSingleMemberThreshold = errors.New("cannot create multiple member shares with member threshold 1 - use 1-of-1 member sharing instead")

	// ErrInvalidMnemonicIndices is returned when a set of shares contains
	// non-unique share indices
	ErrInvalidMnemonicIndices = errors.New("invalid set of shares - share indices must be unique")

	// ErrInvalidMnemonicShareLengths is returned with a set of shares include
	// shares with different lengths
	ErrInvalidMnemonicShareLengths = errors.New("invalid set of shares - all share values must have the same length")

	// ErrInvalidMnemonicSharedSecretDigest is returned when a mnemonic has an
	// invalid shared secret digest
	ErrInvalidMnemonicSharedSecretDigest = errors.New("invalid shared secret digest")

	// ErrInvalidMasterSecretLength is returned when trying to use a (decrypted)
	// master secret with an invalid length
	ErrInvalidMasterSecretLength = errors.New("master secret length must be >= 16B and be a multiple of 2")

	// ErrInvalidEncryptedMasterSecretLength is returned when an encrypted master
	// secret has an invalid length
	ErrInvalidEncryptedMasterSecretLength = errors.New("the length of the encrypted master secret must be an even number")

	// ErrInvalidPassphrase is returned when a passphrase contains invalid
	// characters
	ErrInvalidPassphrase = errors.New("the passphrase must contain only printable ASCII characters (code points 32-126)")

	// ErrTooManyCombinations is returned when the number of share combinations
	// is too many for ValidateMnemonics to test exhaustively
	ErrTooManyCombinations = fmt.Errorf("too many combinations (more than %d)", maxCombinations)
)

Functions

func CombineMnemonics

func CombineMnemonics(mnemonics []string) ([]byte, error)

CombineMnemonics combines mnemonics into the master secret which was originally split into shares using Shamir's Secret Sharing Scheme (without a passphrase).

It requires a slice of mnemonics that contains a minimal quorum of shares for each required group (e.g. if the shares were generated with a 1of2 group and a 3of5 group, then 1 share from the first group and 3 from the second group must be supplied.

The CombineMnenonics functions are the standard entry points for recovering the master secret from a set of slip39 mnemonics.

func CombineMnemonicsWithPassphrase

func CombineMnemonicsWithPassphrase(
	mnemonics []string,
	passphrase []byte,
) ([]byte, error)

CombineMnemonicsWithPassphrase combines mnemonics protected with a passphrase to give the master secret which was originally split into shares using Shamir's Secret Sharing Scheme,

It requires a slice of mnemonics that contains a minimal quorum of shares for each required group (e.g. if the shares were generated with a 1of2 group and a 3of5 group, then 1 share from the first group and 3 from the second group must be supplied.

func SplitMnemonicWords

func SplitMnemonicWords(mnemonic string) ([]string, error)

SplitMnemonicWords splits mnemonic into a slice of words. If mnemonic returns too few words for a slip39 mnemonic, it returns an error.

Types

type ErrBadGroupThreshold

type ErrBadGroupThreshold struct{}

ErrBadGroupThreshold is returned when the group threshold exceeds the group count

func (ErrBadGroupThreshold) Error

func (e ErrBadGroupThreshold) Error() string

type ErrInvalidMnemonicWord

type ErrInvalidMnemonicWord struct{}

ErrInvalidMnemonicWord is returned when a mnemonic word is not found in the reference wordlist

func (ErrInvalidMnemonicWord) Error

func (e ErrInvalidMnemonicWord) Error() string

type ErrInvalidPadding

type ErrInvalidPadding struct{}

ErrInvalidPadding is returned when the padding on a mnemonic is invalid

func (ErrInvalidPadding) Error

func (e ErrInvalidPadding) Error() string

type ErrTooFewShares

type ErrTooFewShares struct{} // public error, for testing with errors.Is()

ErrTooFewShares is returned when the number of groups or shares supplied is fewer than the required threshold

func (ErrTooFewShares) Error

func (e ErrTooFewShares) Error() string

ErrToo{Few,Many}Shares

type ErrTooManyShares

type ErrTooManyShares struct{} // public error, for testing with errors.Is()

ErrTooManyShares is returned when the number of groups or shares supplied is greater than the required threshold

func (ErrTooManyShares) Error

func (e ErrTooManyShares) Error() string

type MemberGroupParameters

type MemberGroupParameters struct {
	MemberThreshold int `json:"member_threshold"`
	MemberCount     int `json:"member_count"`
}

MemberGroupParameters define the (MemberThreshold, MemberCount) pairs required for the share groups created by GenerateMnemonics. MemberCount is the number of shares to generate for the group, and MemberThreshold is the number of members required to reconstruct the group secret.

type Share

type Share struct {
	ShareGroupParameters `json:",inline"`
	MemberIndex          int    `json:"member_index"`
	ShareValues          []byte `json:"share_values"`
}

Share represents a single share of a Shamir secret scheme

func ParseShare

func ParseShare(mnemonic string) (Share, error)

ParseShare parses a slip39 mnemonic string and returns a Share struct, or an error if the mnemonic is invalid.

func (Share) Mnemonic

func (s Share) Mnemonic() (string, error)

Mnemonic returns the mnemonic string for s, or an error

func (Share) Words

func (s Share) Words() ([]string, error)

Words returns the mnemonic words for s as a string slice, or an error

type ShareCommonParameters

type ShareCommonParameters struct {
	Identifier        int `json:"identifier"`
	Extendable        int `json:"extendable"`
	IterationExponent int `json:"iteration_exponent"`
	GroupThreshold    int `json:"group_threshold"`
	GroupCount        int `json:"group_count"`
}

ShareCommonParameters represents the common parameters for a set of shares in a Shamir secret scheme

type ShareGroupParameters

type ShareGroupParameters struct {
	ShareCommonParameters `json:",inline"`
	GroupIndex            int `json:"group_index"`
	MemberThreshold       int `json:"member_threshold"`
}

ShareGroupParameters represents the common parameters for a single group in a Shamir secret scheme

type ShareGroups

type ShareGroups [][]string

ShareGroups is a slice of string slices, each comprising a group of slip39 shares

func CollateShareGroups

func CollateShareGroups(mnemonics []string) (ShareGroups, error)

CollateShareGroups creates a ShareGroups slice from a slice of mnemonics representing a full set of shares, or returns an error.

func CombineLabelledShares

func CombineLabelledShares(labelledShares string) (ShareGroups, error)

CombineLabelledShares converts a string containing a set of labelled shares, one per line, in "<label> <word>" format, into a ShareGroups slice. It checks that the labels are in a consistent format, and are consecutive by group, share, and word order, or returns an error.

func GenerateMnemonics

func GenerateMnemonics(
	groupThreshold int,
	groups []MemberGroupParameters,
	masterSecret []byte,
) (ShareGroups, error)

GenerateMnemonics splits masterSecret into mnemonic shares using Shamir's secret sharing scheme. See GenerateMnemonicsWithOptions for parameter documenantation.

The GenerateMnenonics functions are the standard entry points for splitting a master secret into slip39 mnemonic shares.

func GenerateMnemonicsWithOptions

func GenerateMnemonicsWithOptions(
	groupThreshold int,
	groups []MemberGroupParameters,
	masterSecret []byte,
	passphrase []byte,
	extendable bool,
	iterationExponent int,
) (ShareGroups, error)

GenerateMnemonicsWithOptions splits masterSecret into mnemonic shares using Shamir's secret sharing scheme. The return value is a slice containing the requested groups of shares, each of which is a string slice containing the individual shares for that group.

Parameters:

  • group_threshold is the number of groups required to reconstruct the master secret
  • groups is a slice of MemberGroupParameters, which contain ( MemberThreshold, MemberCount ) pairs for each group, where MemberCount is the number of shares to generate for the group, and MemberThreshold is the number of members required to reconstruct the group secret
  • masterSecret is the secret to split into shares
  • passphrase is an optional passphrase to protect the shares
  • extendable is a boolean flag indicating whether the set of shares is 'extendable', allowing additional sets of shares to be created later for the same master secret (and passphrase, if used). Defaults to true.
  • iterationExponent is the exponent used to derive the iteration count used in the PBKDF2 key derivation function. The number of iterations is calculated as 10000 * 2^iterationExponent. Defaults to 1.

func GenerateMnemonicsWithPassphrase

func GenerateMnemonicsWithPassphrase(
	groupThreshold int,
	groups []MemberGroupParameters,
	masterSecret []byte,
	passphrase []byte,
) (ShareGroups, error)

GenerateMnemonicsWithPassphrase splits masterSecret into mnemonic shares protected by a passphrase using Shamir's secret sharing scheme. See GenerateMnemonicsWithOptions for parameter documenantation.

func (ShareGroups) String

func (sg ShareGroups) String() string

String converts sg to a string with one share per line, output in share group order

func (ShareGroups) StringLabelled

func (sg ShareGroups) StringLabelled() (string, error)

StringLabelled converts sg to a string with each share word on a separate line and prefixed with a numeric label formatted as follows:

  • If there is only a single share group then the label is a 3- or 4-digit number of the form:

    share_number * 100 + word number e.g. 101, 102, ... 201, 202... etc.

  • If there are multiple share groups then then label is a 4- to 6-digit number of the form:

    group_number * 1000 + share number * 100 + word_number, OR: group_number * 10000 + share number * 100 + word_number

    e.g. 1101, 1102... 1201, 1202..., 2101, 2102... etc. OR e.g. 10101, 10102... 10201, 10202..., 20101, 20102... etc.

func (ShareGroups) ValidateMnemonics

func (sg ShareGroups) ValidateMnemonics() ([]byte, int, error)

ValidateMnemonics generates the exhaustive set of share combinations for sg that satisfy the groupThreshold and memberThreshold requirements, and calls CombineMnemonics on each one, confirming they all produce the same master secret. Returns the master secret produced and the number of share combinations tested, or an error.

func (ShareGroups) ValidateMnemonicsWithPassphrase

func (sg ShareGroups) ValidateMnemonicsWithPassphrase(
	passphrase []byte,
) ([]byte, int, error)

ValidateMnemonicsWithPassphrase generates the exhaustive set of share combinations for sg that satisfy the groupThreshold and memberThreshold requirements, and calls CombineMnemonicsWithPassphrase on each one, confirming they all produce the same master secret. Returns the master secret produced and the number of share combinations tested, or an error.

Jump to

Keyboard shortcuts

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