signature

package
v1.3.9 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2021 License: Apache-2.0 Imports: 14 Imported by: 17

README

ndau key format

The keys for ndau are designed to be:

  • able to grow as keys and cryptography evolve
  • distinct from keys used for other cryptocurrencies
  • private and public keys are easily identifiable
  • human-readable
  • encoded in a way that they could be read aloud and manually typed without confusion
  • able to detect minor typos

Essentially, keys are converted to a binary form, and then converted to a base32 representation where case doesn't matter, the characters i, o, 1 and 0 are omitted, and includes a checksum.

Algorithm

Build a binary key

The instructions below are for non-extended keys ONLY. The "extra" bytes that apply to HD key trees are ignored in these instructions; the system supports doing this for extended keys but the explanation of how they are serialized is more complex.

  • Determine the key type (0=null key, 1=ed25519, 2=secp256k1)
  • Determine the key length:
    • ed25519 public keys are 32 bytes
    • ed25519 private keys are 64 bytes
    • secp256k1 public keys are 33 bytes
    • secp256k1 private keys are 32 bytes
  • Build the 5-byte prefix, which consists of 0x92, the key type, 0xc4, keylen+1, keylen:
    • for ed25519 public keys it is 9201c42120
    • for ed25519 private keys it is 9201c44140
    • for secp256k1 public keys it is 9202c42221
    • for secp256k1 private keys it is 9202c42120
  • Build the "packed" byte array, which concatenates:
    • The prefix
    • The bytes of the key
  • Calculate the checksum of this byte array. This is done by:
    • Calculating the number of bytes in the checksum, which is the number of bytes needed to pad the length of the array to a multiple of 5 -- but that number must be a minimum of 3. (So the checksum length will be between 3 and 7 bytes). For the keys below, that will be:
      • ed25519 public keys need 3 bytes
      • ed25519 private keys need 6 bytes
      • secp256k1 public keys need 7 bytes
      • secp256k1 private keys need 3 bytes
    • calculate the checksum as the trailing n bytes of the sha224 checksum of the input bytes
    • append the checksum to the end of the input bytes, making the total length a multiple of 5
    • convert the input byte stream to base32 using the alphabet abcdefghijkmnpqrstuvwxyz23456789
    • add "npub" for public keys and "npvt" for private keys to the front of the string

Sample checksum calculation in Go:

func cksumN(input []byte, n byte) []byte {
	sum := sha256.Sum224(input)
	return sum[sha256.Size224-int(n):]
}
Examples

Ed25519Public

  • raw key: 9e3e08c194fae465c70e0ac0487f63b00dd969c13e45417731d1ab12768f8636 (len 32)
  • packed: 9201c421209e3e08c194fae465c70e0ac0487f63b00dd969c13e45417731d1ab12768f8636 (len 37)
  • result: npuba8jadtbbecrd6cgbuv7qi3qhb2fnaud9nq2a5ymj2e9eksmzghi4yevyt8ddnuxhgw33b8ed

Ed25519Private

  • raw key: db531a15b6444c71b790aae7ae9dd6b3f87561319399af6e490ae1078cda9e03cd5c569c7160d694a9a2607944a267b7cd5fed312d3ef854cc46b5cf92857f2e (len 64)
  • packed: 9201c44140db531a15b6444c71b790aae7ae9dd6b3f87561319399af6e490ae1078cda9e03cd5c569c7160d694a9a2607944a267b7cd5fed312d3ef854cc46b5cf92857f2e (len 69)
  • result: npvtayjadtcbidpxggsxy3ce26pzucxqrmw74439s7mbggj3vm5qjefqcb6n5krahvk6k4qhc2gyuuw4e2d3iutgrp8pm9yvcmj89bkn2txx38jik93qvwy9fxad

Secp256k1Public

  • raw key: 02d235e59fc697cb3a2a416c60f1c4af1edf68fdd21001c97091a5f45db4267f1d (len 33)
  • packed: 9202c4222102d235e59fc697cb3a2a416c60f1c4af1edf68fdd21001c97091a5f45db4267f1d (len 38)
  • result: npuba4jaftbceebpeprfv9djru34fjay22ht2uzt7z5i9zjbaaqjqci4m7c7ysvh8hpwj3zwvgpn

Secp256k1Private

  • raw key: 72422691b240a4fd9b7ee6f31be97042ed38d2b8e126fe2d865958a9e2533e4e (len 32)
  • packed: 9202c4212072422691b240a4fd9b7ee6f31be97042ed38d2b8e126fe2d865958a9e2533e4e (len 37)
  • result: npvta8jaftbbeb3eejwtyjakj9n5r5vrgg9jqbbq4qguzdsup9tps3nxtkrckn9e643gumuxyt7z

ndau signature format

ndau signatures follow a similar pattern.

Signing transactions

  • Get the raw bytes of the private key
  • Generate the SignableBytes of the transaction (see below)
  • Sign the SignableBytes (do not hash it first) using the private key's standard signature algorithm
  • Calculate the checksum of the signature using the algorithm above
  • Generate the base32 representation using the same alphabet above

Signable Bytes

A transaction must be signed, but the signature itself must not be part of the signed result. To calculate the collection of Signable Bytes for any transaction:

  • Sort the JSON field names alphabetically
  • Build a byte array concatenating all the bytes of each of the field types:
    • If the field name matches the regular expression "[sS]ignature[s]?", it should be skipped
    • String -- use the bytes of the string
    • Positive integer and quantity of ndau -- store the 64-bit (8 bytes) representation of the quantity, highest-byte first. The value 258 would be stored as 00 00 00 00 00 00 01 02. Note that JavaScript can't accurately represent more than 53 bits so care must be taken when using JS.
    • Boolean -- true is 0x01, false is 0x00
    • Duration -- store the bytes of the text representation of the duration (1y5m17dt4h37m)
    • Nested objects -- treat recursively: sort the subkeys and record the concatenated values
  • The resulting byte array is the SignableBytes of the transaction

Verifying a transaction:

  • Get the raw bytes of the public key, decoding as necessary
  • Generate the SignableBytes of the transaction
  • Get the raw bytes of the signature
  • Verify the SignableBytes using the public key's standard verify algorithm

Documentation

Index

Constants

View Source
const (
	// ChecksumPadWidth is the moduland of which the length of a checksummed
	// byte slice will always equal 0.
	//
	// In other words, we choose a checksum width such that
	// `len(summedMsg) % ChecksumPadWidth == 0`.
	//
	// We choose 5 because we expect to encode this data in a base32 encoding,
	// which needs no padding when the input size is a multiple of 5.
	ChecksumPadWidth = 5
	// ChecksumMinBytes is the minimum number of checksum bytes. The chance
	// that a checksum will accidentally pass is roughly `1 / (256 ^ ChecksumMinBytes)`,
	// so the value of 3 used gives a false positive rate of about 1 / 16 million.
	ChecksumMinBytes = 3
)
View Source
const PrivateKeyPrefix = "npvt"

PrivateKeyPrefix always prefixes Ndau private keys in text serialization

View Source
const PublicKeyPrefix = "npub"

PublicKeyPrefix always prefixes Ndau public keys in text serialization

Variables

View Source
var (
	Ed25519   = ed25519.Ed25519
	Secp256k1 = secp256k1.Secp256k1
	Null      = null.Null
)

re-export package-native algorithms

Functions

func AddChecksum

func AddChecksum(bytes []byte) []byte

AddChecksum adds a checksum to a byte slice.

The number of bytes of the checksum depend on the width of the data: an appropriate number will be used, at least `ChecksumMinBytes`, such that the total length of the returned slice is a multiple of `ChecksumPadWidth`.

The checksum bytes are appended to the end of the message.

A single byte is also added at the head of the byte slice, containing the number of padding bytes, for ease of checking the checksum.

func CheckChecksum

func CheckChecksum(checked []byte) (message []byte, checksumOk bool)

CheckChecksum validates the checksum of a summed byte slice.

It returns the wrapped message stripped of checksum data and a boolean that indicates if the checksum was correct; if the boolean is false, the message is not valid.

func Generate

func Generate(al Algorithm, rdr io.Reader) (public PublicKey, private PrivateKey, err error)

Generate a high-level keypair

func IsPrivate

func IsPrivate(k Key) bool

IsPrivate is true when the supplied Key is private

func IsPublic

func IsPublic(k Key) bool

IsPublic is true when the supplied Key is public

func Match

func Match(pub PublicKey, pvt PrivateKey) bool

Match indicates whether or not given public and private keys match each other

func MaybePrivate

func MaybePrivate(s string) bool

MaybePrivate provides a fast way to check whether a string looks like it might be an ndau private key.

To get a definitive answer as to whether something is a private key, one must attempt to deserialize it using UnmarshalText and check the error value. That takes some work; it's faster to use this to get a first impression.

This function will allow some false positives, but no false negatives: some values for which it returns `true` may not be actual valid keys, but no values for which it returns `false` will return actual valid keys.

func MaybePublic

func MaybePublic(s string) bool

MaybePublic provides a fast way to check whether a string looks like it might be an ndau public key.

To get a definitive answer as to whether something is a public key, one must attempt to deserialize it using UnmarshalText and check the error value. That takes some work; it's faster to use this to get a first impression.

This function will allow some false positives, but no false negatives: some values for which it returns `true` may not be actual valid keys, but no values for which it returns `false` will return actual valid keys.

func NameOf

func NameOf(al Algorithm) string

NameOf returns the name of an Algorithm

func RegisterAlgorithm

func RegisterAlgorithm(id AlgorithmID, al Algorithm) error

RegisterAlgorithm makes it possible to serialize and deserialize custom Algorithms

If you build a custom Algorithm, you probably want to call this in an init function All IDs < 128 are reserved for canonical implementations.

func SameAlgorithm

func SameAlgorithm(a1 Algorithm, a2 Algorithm) bool

SameAlgorithm returns true when two algorithms are in fact the same algorithm, even if they are not the same instance.

Unknown algorithms are never the same.

Types

type Algorithm

type Algorithm interface {
	// PublicKeySize is the size in bytes of this algorithm's public keys
	PublicKeySize() int
	// PrivateKeySize is the size in bytes of this algorithm's private keys
	PrivateKeySize() int
	// SignatureSize is the size in bytes of this algorithm's signatures
	SignatureSize() int

	// Public generates a public key when given a private key
	Public(private []byte) []byte

	// Generate creates a new keypair
	Generate(rand io.Reader) (public, private []byte, err error)
	// Sign signs the message with privateKey and returns a signature
	Sign(private, message []byte) []byte
	// Verify verifies a message's signature
	//
	// Return true if the signature is valid
	Verify(public, message, sig []byte) bool
}

Algorithm abstracts over a variety of signature algorithms

The required methods here are low-level, for simplicity of external implementation. Consumers should consider generating their keys using the `Generate` function and then interacting with the keys and signatures using the high-level interface.

type AlgorithmID

type AlgorithmID uint8

AlgorithmID is an identifier uniquely associated with each supported signature algorithm

func (*AlgorithmID) DecodeMsg

func (z *AlgorithmID) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (AlgorithmID) EncodeMsg

func (z AlgorithmID) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (AlgorithmID) MarshalMsg

func (z AlgorithmID) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (AlgorithmID) Msgsize

func (z AlgorithmID) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*AlgorithmID) UnmarshalMsg

func (z *AlgorithmID) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type IdentifiedData

type IdentifiedData struct {
	Algorithm AlgorithmID
	Data      []byte
}

IdentifiedData is a byte slice associated with an algorithm

func (*IdentifiedData) DecodeMsg

func (z *IdentifiedData) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*IdentifiedData) EncodeMsg

func (z *IdentifiedData) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*IdentifiedData) MarshalMsg

func (z *IdentifiedData) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*IdentifiedData) Msgsize

func (z *IdentifiedData) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*IdentifiedData) UnmarshalMsg

func (z *IdentifiedData) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type Key

type Key interface {
	encoding.TextMarshaler
	encoding.TextUnmarshaler
	fmt.Stringer
	msgp.Marshaler
	msgp.Unmarshaler
	msgp.Sizer

	KeyBytes() []byte
	ExtraBytes() []byte
	Algorithm() Algorithm
	Truncate()
	Zeroize()
}

A Key is a public or private key which knows about its algorithm

This is most useful when abstracting over what might be a public or a private key. To recover the concrete instance, consider a typeswitch:

switch key := keyI.(type) { case PublicKey:

...

case PrivateKey:

    ...
}

Key includes several other interfaces to ensure consistent marshalling and unmarshalling in both binary and text formats

func ParseKey

func ParseKey(text string) (Key, error)

ParseKey attempts to parse the given text as a Key of the proper type

type PrivateKey

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

A PrivateKey is the private half of a keypair

func ParsePrivateKey

func ParsePrivateKey(s string) (*PrivateKey, error)

ParsePrivateKey parses a string representation of a private key, if possible

func RawPrivateKey

func RawPrivateKey(al Algorithm, key, extra []byte) (*PrivateKey, error)

RawPrivateKey creates a PrivateKey from raw data

This is unsafe and subject to only minimal type-checking; it should normally be avoided.

func (PrivateKey) Algorithm

func (key PrivateKey) Algorithm() Algorithm

Algorithm returns the key's algorithm

func (PrivateKey) ExtraBytes

func (key PrivateKey) ExtraBytes() []byte

ExtraBytes returns any extra data

func (PrivateKey) FullString

func (key PrivateKey) FullString() string

FullString returns the key's human-readable serialization

func (PrivateKey) IsZero

func (key PrivateKey) IsZero() bool

IsZero is true when this key is the zero value

func (PrivateKey) KeyBytes

func (key PrivateKey) KeyBytes() []byte

KeyBytes returns the key's data

func (PrivateKey) Marshal

func (key PrivateKey) Marshal() (serialized []byte, err error)

Marshal marshals the key into a serialized binary format which includes a type byte for the algorithm.

func (PrivateKey) MarshalMsg

func (key PrivateKey) MarshalMsg(in []byte) (out []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*PrivateKey) MarshalString

func (key *PrivateKey) MarshalString() (string, error)

MarshalString is like MarshalText, but to a string

func (PrivateKey) MarshalText

func (key PrivateKey) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

PublicKeys encode like Keys, with the addition of a human-readable prefix for easy identification.

func (*PrivateKey) Msgsize

func (key *PrivateKey) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message Msgsize implements msgp.Sizer

This method was copy-pasted from the IdentifiedData Msgsize implementation, as fundamentally a PrivateKey gets serialized as an IdentifiedData, and so should have the same size.

func (PrivateKey) Sign

func (key PrivateKey) Sign(message []byte) Signature

Sign the supplied message

func (PrivateKey) Size

func (key PrivateKey) Size() int

Size returns the size of this key

func (PrivateKey) String

func (key PrivateKey) String() string

String returns a shorthand for the key's data

This returns the first 8 characters of the text serialization, an ellipsis, then the final 4 characters of the text serialization. Total output size is constant at 15 characters.

This destructively truncates the key, but it is a useful format for humans.

func (*PrivateKey) Truncate

func (key *PrivateKey) Truncate()

Truncate removes all extra data from this key.

This is a destructive operation which cannot be undone; make copies first if you need to.

func (*PrivateKey) Unmarshal

func (key *PrivateKey) Unmarshal(serialized []byte) error

Unmarshal unmarshals the serialized bytes into the PrivateKey pointer

func (*PrivateKey) UnmarshalMsg

func (key *PrivateKey) UnmarshalMsg(in []byte) (leftover []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*PrivateKey) UnmarshalText

func (key *PrivateKey) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler

func (*PrivateKey) Zeroize

func (key *PrivateKey) Zeroize()

Zeroize removes all data from this key

This is a destructive operation which cannot be undone; make copies first if you need to.

type PublicKey

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

A PublicKey is the public half of a keypair

func ParsePublicKey

func ParsePublicKey(s string) (*PublicKey, error)

ParsePublicKey parses a string representation of a public key, if possible

func RawPublicKey

func RawPublicKey(al Algorithm, key, extra []byte) (*PublicKey, error)

RawPublicKey creates a PublicKey from raw data

This is unsafe and subject to only minimal type-checking; it should normally be avoided.

func (PublicKey) Algorithm

func (key PublicKey) Algorithm() Algorithm

Algorithm returns the key's algorithm

func (PublicKey) ExtraBytes

func (key PublicKey) ExtraBytes() []byte

ExtraBytes returns any extra data

func (PublicKey) FullString

func (key PublicKey) FullString() string

FullString returns the key's human-readable serialization

func (PublicKey) IsZero

func (key PublicKey) IsZero() bool

IsZero is true when this key is the zero value

func (PublicKey) KeyBytes

func (key PublicKey) KeyBytes() []byte

KeyBytes returns the key's data

func (PublicKey) Marshal

func (key PublicKey) Marshal() (serialized []byte, err error)

Marshal marshals the key into a serialized binary format which includes a type byte for the algorithm.

func (PublicKey) MarshalMsg

func (key PublicKey) MarshalMsg(in []byte) (out []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*PublicKey) MarshalString

func (key *PublicKey) MarshalString() (string, error)

MarshalString is like MarshalText, but to a string

func (PublicKey) MarshalText

func (key PublicKey) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

PublicKeys encode like Keys, with the addition of a human-readable prefix for easy identification.

func (*PublicKey) Msgsize

func (key *PublicKey) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message Msgsize implements msgp.Sizer

This method was copy-pasted from the IdentifiedData Msgsize implementation, as fundamentally a PublicKey gets serialized as an IdentifiedData, and so should have the same size.

func (PublicKey) Size

func (key PublicKey) Size() int

Size returns the size of this key

func (PublicKey) String

func (key PublicKey) String() string

String returns a shorthand for the key's data

This returns the first 8 characters of the text serialization, an ellipsis, then the final 4 characters of the text serialization. Total output size is constant at 15 characters.

This destructively truncates the key, but it is a useful format for humans.

func (*PublicKey) Truncate

func (key *PublicKey) Truncate()

Truncate removes all extra data from this key.

This is a destructive operation which cannot be undone; make copies first if you need to.

func (*PublicKey) Unmarshal

func (key *PublicKey) Unmarshal(serialized []byte) error

Unmarshal unmarshals the serialized bytes into the PublicKey pointer

func (*PublicKey) UnmarshalMsg

func (key *PublicKey) UnmarshalMsg(in []byte) (leftover []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*PublicKey) UnmarshalText

func (key *PublicKey) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler

func (PublicKey) Verify

func (key PublicKey) Verify(message []byte, sig Signature) bool

Verify the supplied message with the given signature

func (*PublicKey) Zeroize

func (key *PublicKey) Zeroize()

Zeroize removes all data from this key

This is a destructive operation which cannot be undone; make copies first if you need to.

type Signature

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

A Signature is a byte slice with known algorithm type

func ParseSignature

func ParseSignature(s string) (*Signature, error)

ParseSignature parses a string representation of a signature, if possible

func RawSignature

func RawSignature(al Algorithm, data []byte) (*Signature, error)

RawSignature creates a Signature from raw data

This is unsafe and subject to only minimal type-checking; it should normally be avoided.

func (Signature) Algorithm

func (signature Signature) Algorithm() Algorithm

Algorithm gets the signature's algorithm

func (*Signature) Bytes

func (signature *Signature) Bytes() []byte

Bytes returns the key's data

func (Signature) Marshal

func (signature Signature) Marshal() (serialized []byte, err error)

Marshal marshals the signature into a serialized binary format which includes a type byte for the algorithm.

func (Signature) MarshalMsg

func (signature Signature) MarshalMsg(in []byte) (out []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Signature) MarshalString

func (signature *Signature) MarshalString() (string, error)

MarshalString is like MarshalText, but to a string

func (Signature) MarshalText

func (signature Signature) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler

This marshaller uses a custom b32 encoding which is case-insensitive and lacks certain confusing pairs, for ease of human-friendly handling. For the same reason, it embeds a checksum, so it's easy to tell whether or not it was received correctly.

func (*Signature) Msgsize

func (signature *Signature) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message Msgsize implements msgp.Sizer

This method was copy-pasted from the IdentifiedData Msgsize implementation, as fundamentally a Signature gets serialized as an IdentifiedData, and so should have the same size.

func (Signature) Size

func (signature Signature) Size() int

Size returns the size of this signature

func (*Signature) Unmarshal

func (signature *Signature) Unmarshal(serialized []byte) error

Unmarshal unmarshals the serialized binary data into the supplied signature instance

func (*Signature) UnmarshalMsg

func (signature *Signature) UnmarshalMsg(in []byte) (leftover []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*Signature) UnmarshalText

func (signature *Signature) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler

func (Signature) Verify

func (signature Signature) Verify(message []byte, key PublicKey) bool

Verify is a convenience function to verify from a signature

Directories

Path Synopsis
algorithms

Jump to

Keyboard shortcuts

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