silk.crypto.minisign.
Sign and verify files with minisign, the Ed25519 signature format Silk releases use.
When to use#
Use verify to check a downloaded file against a publisher's minisign public key, and
sign with a MinisignSecretKey to publish signatures that minisign -V and verify
accept. Use silk.crypto.ed25519 for raw Ed25519 signatures in another format.
Details#
A public key is the base64 text of Ed, an 8-byte key id and a 32-byte Ed25519 key. A signature
file has four lines: an untrusted comment, the base64 of an algorithm, the key id and a 64-byte
signature, trusted comment: <text>, and the base64 of a 64-byte global signature over the
signature followed by the trusted comment text. The ED algorithm signs the BLAKE2b-512 digest
of the content and the legacy Ed algorithm signs the content itself; sign writes ED.
A secret key file holds the base64 of the algorithms, the scrypt salt and limits, and the key id,
Ed25519 key and BLAKE2b-256 checksum, XORed with the scrypt output of the password when the key
is encrypted. secretKeyFile encrypts with minisign's default limits, which make scrypt use
1 GiB of memory, or writes an unencrypted key like minisign -G -W without a password.
Gotchas#
Ed25519 signing is deterministic, so signing the same content with the same key and comment gives the same file. Secrets are not erased from memory.
Examples#
Sign and verify a message#
import silk.core.effect { Effect }
import silk.core.result { Result }
import silk.crypto.minisign as Minisign { MinisignError, MinisignKey, MinisignSecretKey }
import silk.collections.bytes { Bytes }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.system.random { Random }
import silk.system.random_native { OsRandom }
effect fn roundTrip() -> i32 ! OutOfMemoryError ? &mut Allocator | &mut Random {
let secret = run MinisignSecretKey.generate()
let signature = match run Minisign.sign(&secret, b"hello", b"greeting") {
Result<Bytes, MinisignError>.Success { value } => move value
Result<Bytes, MinisignError>.Failure { error } => { return 1 }
}
let public = match Minisign.key(&secret.publicKeyText()) {
Result<MinisignKey, MinisignError>.Success { value } => move value
Result<MinisignKey, MinisignError>.Failure { error } => { return 2 }
}
return match Minisign.verify(&public, Bytes.asSlice(&signature), b"hello") {
Result<&[u8], MinisignError>.Success { value } => 0
Result<&[u8], MinisignError>.Failure { error } => 3
}
}
pub effect fn main() -> i32 ! OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let mut random = OsRandom.make()
return run roundTrip()
|> Effect.provideMut<Random>(&mut random)
|> Effect.provideMut<Allocator>(&mut allocator)
}Import as Minisign with import silk.crypto.minisign as Minisign.
Public declarations: 10.
MinisignError#
pub enum MinisignErrorWhy a key or signature was refused.
InvalidKey#
InvalidKeyThe public key text is not the base64 of an Ed25519 minisign key.
MalformedSignature#
MalformedSignatureThe signature file does not have the four minisign lines.
UnsupportedAlgorithm#
UnsupportedAlgorithmThe signature names an algorithm other than ED or Ed.
WrongKey#
WrongKeyThe signature was made with a different key.
InvalidSignature#
InvalidSignatureThe signature does not verify for this content.
InvalidTrustedComment#
InvalidTrustedCommentThe global signature over the trusted comment does not verify.
InvalidSecretKey#
InvalidSecretKeyThe secret key text is not a minisign Ed25519 secret key, or its key derivation settings are out of range.
PasswordRequired#
PasswordRequiredThe secret key is encrypted and no password was given.
WrongPassword#
WrongPasswordThe password does not decrypt the secret key: its checksum does not match.
InvalidComment#
InvalidCommentA trusted comment to sign has a line break or is longer than 1024 bytes.
MinisignKey#
pub struct MinisignKeyA decoded minisign public key.
describe#
pub fn describe(error: MinisignError) -> &'static [u8]A short description of error for messages.
key#
pub fn key(text: &[u8]) -> Result<MinisignKey, MinisignError>Decodes the base64 public key text, such as the contents of minisign.pub after its comment
line.
verify#
pub fn verify<'s>(signer: &MinisignKey, signature: &'s [u8], content: &[u8]) -> Result<
&'s [u8],
MinisignError
>Verifies the minisign signature file of content against signer, and returns its trusted
comment.
Both the signature over the content and the global signature over the trusted comment must verify, so the returned comment is authenticated too.
MinisignSecretKey#
pub struct MinisignSecretKeyA minisign secret key: its key id and Ed25519 key.
Implementation MinisignSecretKey#
impl MinisignSecretKeyAssociated function generate#
pub effect fn generate() -> MinisignSecretKey ? &mut RandomReturns a new key with a random key id, from the Random provider.
Gotchas#
Use a secure Random provider, such as silk.system.random_native.OsRandom.
Method keyId#
pub fn keyId(self: &Self) -> [u8; 8]Returns the 8-byte key id, which signatures carry to name their key.
Method idText#
pub fn idText(self: &Self) -> [u8; 16]Returns the key id as minisign prints it: 16 uppercase hexadecimal digits of the id read as a
little-endian number, such as 2EFCB60D3D0A7C1E.
Method publicKeyText#
pub fn publicKeyText(self: &Self) -> [u8; 56]Returns the base64 public key, the second line of a minisign.pub file, which key reads.
publicKeyFile#
pub effect fn publicKeyFile(secret: &MinisignSecretKey) -> Bytes ! OutOfMemoryError ? &mut AllocatorReturns the text of a minisign.pub file for secret's public key: an untrusted comment naming
the key id, then the base64 key.
secretKeyFile#
pub effect fn secretKeyFile(secret: &MinisignSecretKey, password: Option<&[u8]>) -> Bytes
! OutOfMemoryError
? &mut Allocator | &mut RandomReturns the text of a minisign secret key file for secret, encrypted with password as
minisign -G writes it, or unencrypted as minisign -G -W writes it when password is None.
Details#
Encrypting draws a random salt from Random and runs scrypt with minisign's default limits,
which use 1 GiB of memory.
secretKey#
pub effect fn secretKey(text: &[u8], password: Option<&[u8]>) -> Result<MinisignSecretKey, MinisignError>
! OutOfMemoryError
? &mut AllocatorReads a minisign secret key file, decrypting it with password when it is encrypted.
Errors#
InvalidSecretKeywhen the text is not a minisign Ed25519 secret key.PasswordRequiredwhen the key is encrypted andpasswordisNone.WrongPasswordwhen the checksum does not match after decryption.
Gotchas#
Decrypting runs scrypt with the limits the file names, 1 GiB of memory for minisign's defaults.
sign#
pub effect fn sign(secret: &MinisignSecretKey, content: &[u8], trustedComment: &[u8]) -> Result<Bytes, MinisignError>
! OutOfMemoryError
? &mut AllocatorReturns the minisign signature file of content for secret, with trustedComment signed too.
Details#
The signature uses the ED algorithm, over the BLAKE2b-512 digest of content, which
minisign -V and verify accept.
Errors#
InvalidComment when trustedComment has a line break or is longer than 1024 bytes.
Category: Cryptography.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.