minisign

package module
v0.0.0-...-e98fef1 Latest Latest
Warning

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

Go to latest
Published: Jul 4, 2023 License: MIT Imports: 17 Imported by: 0

README

Go Reference Github CI latest

minisign

minisign is a dead simple tool to sign files and verify signatures.

$ minisign -G                                                                                  
Please enter a password to protect the secret key.

Enter Password: 
Enter Password (one more time): 
Deriving a key from the password in order to encrypt the secret key... done

The secret key was saved as ~/.minisign/minisign.key - Keep it secret!
The public key was saved as minisign.pub - That one can be public.

Files signed using this key pair can be verified with the following command:

minisign -Vm <file> -P RWSYKA736yqh+JrZ7cRDdWgck/WKtwW9ATBFmk8pQ1lHeUKXtV6uJ7Fu
$ minisign -Sm message.txt
Enter password: 
Deriving a key from the password in order to decrypt the secret key... done
$ minisign -Vm message.txt
Signature and comment signature verified
Trusted comment: timestamp:1614718943	filename:message.txt

This is a Go implementation of the original C implementation by Frank Denis.

Usage

Usage:
    minisign -G [-p <pubKey>] [-s <secKey>]
    minisign -S [-x <signature>] [-s <secKey>] [-c <comment>] [-t <comment>] -m <file>...
    minisign -V [-H] [-x <signature>] [-p <pubKey> | -P <pubKey>] [-o] [-q | -Q ] -m <file>
    minisign -R [-s <secKey>] [-p <pubKey>]
 
Options:
    -G               Generate a new public/secret key pair.       
    -S               Sign files with a secret key.
    -V               Verify files with a public key.
    -m <file>        The file to sign or verify.
    -o               Combined with -V, output the file after verification.
    -H               Combined with -V, require a signature over a pre-hashed file.
    -p <pubKey>      Public key file (default: ./minisign.pub)
    -P <pubKey>      Public key as base64 string
    -s <secKey>      Secret key file (default: $HOME/.minisign/minisign.key)
    -x <signature>   Signature file (default: <file>.minisig)
    -c <comment>     Add a one-line untrusted comment.
    -t <comment>     Add a one-line trusted comment.
    -q               Quiet mode. Suppress output.
    -Q               Pretty quiet mode. Combined with -V, only print the trusted comment.
    -R               Re-create a public key file from a secret key.
    -f               Combined with -G or -R, overwrite any existing public/secret key pair.
    -v               Print version information.

Installation

On windows, linux and macOS, you can use the pre-built binaries:

OS ARCH Latest Release
Linux amd64 minisign-linux-amd64.tar.gz
Linux arm64 minisign-linux-arm64.tar.gz
MacOS arm64 minisign-darwin-arm64.tar.gz
MacOS amd64 minisign-darwin-amd64.tar.gz
Windows amd64 minisign-windows-amd64.zip

If your system has Go1.16+, you can build from source:

git clone https://aead.dev/minisign && cd minisign
go build -o . aead.dev/minisign/cmd/minisign

Library

import "aead.dev/minisign" 

The following example generates a minisign public/private key pair, signs a message and verifies the message signature.

package main

import (
	"crypto/rand"
	"log"

	"aead.dev/minisign"
)

func main() {
	var message = []byte("Hello World!")

	public, private, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		log.Fatalln(err)
	}

	signature := minisign.Sign(private, message)
	
	if !minisign.Verify(public, message, signature) {
		log.Fatalln("signature verification failed")
	}
	log.Println(string(message))
}

For more examples visit the package documentation.

Documentation

Overview

Package minisign implements the minisign signature scheme.

Index

Examples

Constants

View Source
const (
	// EdDSA refers to the Ed25519 signature scheme.
	//
	// Minisign uses this signature scheme to sign and
	// verify (non-hashed) messages.
	EdDSA uint16 = 0x6445

	// HashEdDSA refers to a Ed25519 signature scheme
	// with pre-hashed messages.
	//
	// Minisign uses this signature scheme to sign and
	// verify message that don't fit into memory.
	HashEdDSA uint16 = 0x4445
)

Variables

This section is empty.

Functions

func EncryptKey

func EncryptKey(password string, privateKey PrivateKey) ([]byte, error)

EncryptKey encrypts the private key with the given password using some entropy from the RNG of the OS.

Example
package main

import (
	"crypto/rand"
	"fmt"

	"aead.dev/minisign"
)

func main() {
	// Generate a new minisign private / public key pair.
	// We don't care about the public key in this example.
	_, privateKey, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		panic(err) // TODO: error handling
	}

	const password = "correct horse battery staple"

	// Encrypt the private key with the password
	encryptedKey, err := minisign.EncryptKey(password, privateKey)
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Then, decrypt the encrypted key with the password again
	decryptedKey, err := minisign.DecryptKey(password, encryptedKey)
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Now, both private keys should be identical
	fmt.Println(privateKey.Equal(decryptedKey))
}
Output:

true

func GenerateKey

func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error)

GenerateKey generates a public/private key pair using entropy from random. If random is nil, crypto/rand.Reader will be used.

Example
package main

import (
	"crypto/rand"
	"fmt"

	"aead.dev/minisign"
)

func main() {
	// Generate a new minisign private / public key pair.
	publicKey, privateKey, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Sign a message with the private key
	message := []byte("Hello Gopher!")
	signature := minisign.Sign(privateKey, message)

	// Verify the signature with the public key and
	// print the message if the signature is valid.
	if minisign.Verify(publicKey, message, signature) {
		fmt.Println(string(message))
	}
}
Output:

Hello Gopher!

func Sign

func Sign(privateKey PrivateKey, message []byte) []byte

Sign signs the given message with the private key.

It behaves like SignWithComments with some generic comments.

Example
package main

import (
	"fmt"

	"aead.dev/minisign"
)

func main() {
	const (
		rawPrivateKey = "RWRTY0IyorAWr/1gdweGki6ua7GpmoPqS+7rMBSmBy6hedA53dAAABAAAAAAAAAAAAIAAAAAwfmyB6qIIW2eGNiQaFzgs1oi52iN8cRHBPRupc9TVdfAeJvlPdvzu3TfA2DHTW2PZi98uihcr5sEB5fefFml2d0xBk72ZOGNJpOTsn95eHgEH/qUfzQZ018JfiVwWf8pNpdgNFX8ROs="
		password      = "correct horse battery staple"
	)

	// Decrypt the raw private key with the password
	privateKey, err := minisign.DecryptKey(password, []byte(rawPrivateKey))
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Sign a message with the private key
	message := []byte("Hello Gopher!")
	signature := minisign.Sign(privateKey, message)

	fmt.Println(string(signature))
}
Output:

func SignWithComments

func SignWithComments(privateKey PrivateKey, message []byte, trustedComment, untrustedComment string) []byte

SignWithComments signs the given message with the private key.

The trustedComment as well as the untrustedComment are embedded into the returned signature. The trustedComment is signed and will be checked when the signature is verified. The untrustedComment is not signed and must not be trusted.

Example
package main

import (
	"fmt"

	"aead.dev/minisign"
)

func main() {
	const (
		rawPrivateKey = "RWRTY0IyorAWr/1gdweGki6ua7GpmoPqS+7rMBSmBy6hedA53dAAABAAAAAAAAAAAAIAAAAAwfmyB6qIIW2eGNiQaFzgs1oi52iN8cRHBPRupc9TVdfAeJvlPdvzu3TfA2DHTW2PZi98uihcr5sEB5fefFml2d0xBk72ZOGNJpOTsn95eHgEH/qUfzQZ018JfiVwWf8pNpdgNFX8ROs="
		password      = "correct horse battery staple"
	)

	// Decrypt the raw private key with the password
	privateKey, err := minisign.DecryptKey(password, []byte(rawPrivateKey))
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Sign a message with comments with the private key
	const (
		trustedComment   = "This comment is signed and can be trusted"
		untrustedComment = "This comment is not signed and just informational"
	)
	message := []byte("Hello Gopher!")
	signature := minisign.SignWithComments(privateKey, message, trustedComment, untrustedComment)

	fmt.Println(string(signature))

}
Output:

untrusted comment: This comment is not signed and just informational
RWQGPaMY2ls0CmMflCAP5J/MpaXmt+3+UoT1vRSPRjXO6w0KNtpkcQe3TxQ35kAwhjFVB6CEYYrHZmMvWjXRutefRHicRUiAJwQ=
trusted comment: This comment is signed and can be trusted
/jXXGSI/q3MhrZ5PKzL221/qC+JFVpgilf9su6AcTtMffw+9ShYt5LjU2RG1M/EspIoEv4xxK/36TeCQBgHbBw==

func Verify

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

Verify checks whether message is authentic by verifying it with the given public key and signature. It returns true if and only if the signature verification is successful.

Example
package main

import (
	"fmt"

	"aead.dev/minisign"
)

func main() {
	const rawPublicKey = "RWQGPaMY2ls0CkF/83ls7D+IU25w3jeYczwo3s451zDlnrJJwOdt2ro8"

	var (
		message = []byte("Hello Gopher!")

		signature = []byte(`untrusted comment: signature from private key: A345BDA18A33D06
RWQGPaMY2ls0CmMflCAP5J/MpaXmt+3+UoT1vRSPRjXO6w0KNtpkcQe3TxQ35kAwhjFVB6CEYYrHZmMvWjXRutefRHicRUiAJwQ=
trusted comment: timestamp:1600100266
2x/lxCqL+PHoT4I9Wc8PHmoNBtohgmFdWwPBON55Y2P0ttpBHgr4OFldr/Hq7nDcBGt5SBs2XjtMnxjVs6byBg==`)
	)

	var publicKey minisign.PublicKey
	if err := publicKey.UnmarshalText([]byte(rawPublicKey)); err != nil {
		panic(err) // TODO: error handling
	}

	if minisign.Verify(publicKey, message, signature) {
		fmt.Println(string(message))
	}
}
Output:

Hello Gopher!

Types

type PrivateKey

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

PrivateKey is a minisign private key.

A private key can sign messages to prove the their origin and authenticity.

PrivateKey implements the crypto.Signer interface.

func DecryptKey

func DecryptKey(password string, privateKey []byte) (PrivateKey, error)

DecryptKey tries to decrypt the encrypted private key with the given password.

Example
package main

import (
	"fmt"
	"strconv"
	"strings"

	"aead.dev/minisign"
)

func main() {
	const (
		rawPrivateKey = "RWRTY0IyorAWr/1gdweGki6ua7GpmoPqS+7rMBSmBy6hedA53dAAABAAAAAAAAAAAAIAAAAAwfmyB6qIIW2eGNiQaFzgs1oi52iN8cRHBPRupc9TVdfAeJvlPdvzu3TfA2DHTW2PZi98uihcr5sEB5fefFml2d0xBk72ZOGNJpOTsn95eHgEH/qUfzQZ018JfiVwWf8pNpdgNFX8ROs="
		password      = "correct horse battery staple"
	)

	// Decrypt the raw private key with the password
	privateKey, err := minisign.DecryptKey(password, []byte(rawPrivateKey))
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Print the key ID as upper-case hex string
	fmt.Println("Private Key", strings.ToUpper(strconv.FormatUint(privateKey.ID(), 16)))

}
Output:

Private Key A345BDA18A33D06

func PrivateKeyFromFile

func PrivateKeyFromFile(password, path string) (PrivateKey, error)

PrivateKeyFromFile reads and decrypts the private key file with the given password.

func (PrivateKey) Equal

func (p PrivateKey) Equal(x crypto.PrivateKey) bool

Equal returns true if and only if p and x have equivalent values.

func (PrivateKey) FromString

func (privateKey PrivateKey) FromString(keyString string) (PrivateKey, error)

func (PrivateKey) ID

func (p PrivateKey) ID() uint64

ID returns the 64 bit key ID.

func (PrivateKey) Public

func (p PrivateKey) Public() crypto.PublicKey

Public returns the corresponding public key.

func (PrivateKey) Sign

func (p PrivateKey) Sign(_ io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error)

Sign signs the given message.

The minisign signature scheme relies on Ed25519 and supports plain as well as pre-hashed messages. Therefore, opts can be either crypto.Hash(0) to signal that the message has not been hashed or crypto.BLAKE2b_512 to signal that the message is a BLAKE2b-512 digest. If opts is crypto.BLAKE2b_512 then message must be a 64 bytes long.

Minisign signatures are deterministic such that no randomness is necessary.

func (PrivateKey) String

func (privateKey PrivateKey) String() (string, error)

type PublicKey

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

PublicKey is a minisign public key.

A public key is used to verify whether messages have been signed with the corresponding private key.

func PublicKeyFromFile

func PublicKeyFromFile(path string) (PublicKey, error)

PublicKeyFromFile reads a new PublicKey from the given file.

func (PublicKey) Equal

func (p PublicKey) Equal(x crypto.PublicKey) bool

Equal returns true if and only if p and x have equivalent values.

func (PublicKey) FromString

func (publicKey PublicKey) FromString(text string) (PublicKey, error)

FromString returns a PublicKey from a PublicKey.String()

func (PublicKey) ID

func (p PublicKey) ID() uint64

ID returns the 64 bit key ID.

func (PublicKey) MarshalText

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

MarshalText returns a textual representation of the PublicKey p.

It never returns an error.

func (PublicKey) String

func (p PublicKey) String() string

String returns a base64 string representation of the PublicKey p.

func (*PublicKey) UnmarshalText

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

UnmarshalText parses text as textual-encoded public key. It returns an error if text is not a well-formed public key.

type Reader

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

Reader is an io.Reader that reads a message while, at the same time, computes its digest.

At any point, typically at the end of the message, Reader can sign the message digest with a private key or try to verify the message with a public key and signature.

Example
package main

import (
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"strings"

	"aead.dev/minisign"
)

func main() {
	// Generate a new minisign public / private key pair.
	publicKey, privateKey, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		panic(err) // TODO: error handling
	}

	const Message = "Hello Gopher!"

	// Sign a data stream after processing it. (Here, we just discard it)
	reader := minisign.NewReader(strings.NewReader(Message))
	if _, err := io.Copy(ioutil.Discard, reader); err != nil {
		panic(err) // TODO: error handling
	}
	signature := reader.Sign(privateKey)

	// Read a data stream and then verify its authenticity with
	// the public key.
	reader = minisign.NewReader(strings.NewReader(Message))
	message, err := ioutil.ReadAll(reader)
	if err != nil {
		panic(err) // TODO: error handling
	}
	if reader.Verify(publicKey, signature) {
		fmt.Println(string(message))
	}
}
Output:

Hello Gopher!

func NewReader

func NewReader(r io.Reader) *Reader

NewReader returns a new Reader that reads from r and computes a digest of the read data.

func (*Reader) Read

func (r *Reader) Read(p []byte) (int, error)

Read reads from the underlying io.Reader as specified by the io.Reader interface.

func (*Reader) Sign

func (r *Reader) Sign(privateKey PrivateKey) []byte

Sign signs whatever has been read from the underlying io.Reader up to this point in time with the given private key.

It behaves like SignWithComments but uses some generic comments.

func (*Reader) SignWithComments

func (r *Reader) SignWithComments(privateKey PrivateKey, trustedComment, untrustedComment string) []byte

SignWithComments signs whatever has been read from the underlying io.Reader up to this point in time with the given private key.

The trustedComment as well as the untrustedComment are embedded into the returned signature. The trustedComment is signed and will be checked when the signature is verified. The untrustedComment is not signed and must not be trusted.

SignWithComments computes the digest as a snapshot. So, it is possible to create multiple signatures of different message prefixes by reading up to a certain byte, signing this message prefix, and then continue reading.

func (*Reader) Verify

func (r *Reader) Verify(publicKey PublicKey, signature []byte) bool

Verify checks whether whatever has been read from the underlying io.Reader up to this point in time is authentic by verifying it with the given public key and signature.

Verify computes the digest as a snapshot. Therefore, Verify can verify any signature produced by Sign or SignWithComments, including signatures of partial messages, given the correct public key and signature.

type Signature

type Signature struct {

	// Algorithm is the signature algorithm. It is either
	// EdDSA or HashEdDSA.
	Algorithm uint16

	// KeyID may be the 64 bit ID of the private key that was used
	// to produce this signature. It can be used to identify the
	// corresponding public key that can verify the signature.
	//
	// However, key IDs are random identifiers and not protected at all.
	// A key ID is just a hint to quickly identify a public key candidate.
	KeyID uint64

	// TrustedComment is a comment that has been signed and is
	// verified during signature verification.
	TrustedComment string

	// UntrustedComment is a comment that has not been signed
	// and is not verified during signature verification.
	//
	// It must not be considered authentic - in contrast to the
	// TrustedComment.
	UntrustedComment string

	// Signature is the Ed25519 signature of the message that
	// has been signed.
	Signature [ed25519.SignatureSize]byte

	// CommentSignature is the Ed25519 signature of Signature
	// concatenated with the TrustedComment:
	//
	//    CommentSignature = ed25519.Sign(PrivateKey, Signature || TrustedComment)
	//
	// It is used to verify that the TrustedComment is authentic.
	CommentSignature [ed25519.SignatureSize]byte
	// contains filtered or unexported fields
}

Signature is a structured representation of a minisign signature.

A signature is generated when signing a message with a private key:

signature = Sign(privateKey, message)

The signature of a message can then be verified with the corresponding public key:

if Verify(publicKey, message, signature) {
   // => signature is valid
   // => message has been signed with correspoding private key
}

func SignatureFromFile

func SignatureFromFile(file string) (Signature, error)

SignatureFromFile reads a new Signature from the given file.

func (Signature) Equal

func (s Signature) Equal(x Signature) bool

Equal reports whether s and x have equivalent values.

The untrusted comments of two equivalent signatures may differ.

func (Signature) MarshalText

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

MarshalText returns a textual representation of the Signature s.

It returns an error if s cannot be a valid signature - e.g. because the signature algorithm is neither EdDSA nor HashEdDSA.

func (Signature) String

func (s Signature) String() string

String returns a string representation of the Signature s.

In contrast to MarshalText, String does not fail if s is not a valid minisign signature.

func (*Signature) UnmarshalText

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

UnmarshalText parses text as textual-encoded signature. It returns an error if text is not a well-formed minisign signature.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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