Documentation ¶
Overview ¶
Package minisign implements the minisign signature scheme.
Index ¶
- Constants
- func EncryptKey(password string, privateKey PrivateKey) ([]byte, error)
- func GenerateKey(random io.Reader) (PublicKey, PrivateKey, error)
- func Sign(privateKey PrivateKey, message []byte) []byte
- func SignWithComments(privateKey PrivateKey, message []byte, trustedComment, untrustedComment string) []byte
- func Verify(publicKey PublicKey, message, signature []byte) bool
- type PrivateKey
- func (p PrivateKey) Equal(x crypto.PrivateKey) bool
- func (privateKey PrivateKey) FromString(keyString string) (PrivateKey, error)
- func (p PrivateKey) ID() uint64
- func (p PrivateKey) Public() crypto.PublicKey
- func (p PrivateKey) Sign(_ io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error)
- func (privateKey PrivateKey) String() (string, error)
- type PublicKey
- type Reader
- type Signature
Examples ¶
Constants ¶
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 ¶
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) 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 ¶
PublicKeyFromFile reads a new PublicKey from the given file.
func (PublicKey) FromString ¶
FromString returns a PublicKey from a PublicKey.String()
func (PublicKey) MarshalText ¶
MarshalText returns a textual representation of the PublicKey p.
It never returns an error.
func (*PublicKey) UnmarshalText ¶
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 ¶
NewReader returns a new Reader that reads from r and computes a digest of the read data.
func (*Reader) Read ¶
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 ¶
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 ¶
SignatureFromFile reads a new Signature from the given file.
func (Signature) Equal ¶
Equal reports whether s and x have equivalent values.
The untrusted comments of two equivalent signatures may differ.
func (Signature) MarshalText ¶
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 ¶
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 ¶
UnmarshalText parses text as textual-encoded signature. It returns an error if text is not a well-formed minisign signature.