silk.crypto.dsa.
DSA keys and signatures (FIPS 186-4): parameter and key generation, signing, verification, and the DER forms that OpenSSL and Node.js use.
When to use#
Use DsaPrivateKey to sign and DsaPublicKey to verify where a format requires DSA, such
as Node.js crypto.sign with a dsa key, generateKeyPair('dsa'), or old SSH and X.509 keys.
For a new design, use silk.crypto.ed25519 or ECDSA instead.
Details#
A DsaParameters value holds the primes p and q, where q divides p - 1, and a
generator g of order q modulo p. p has 512 to 8192 bits and q 160, 224, or 256 bits
(the pairs of FIPS 186-4 are 1024/160, 2048/224, 2048/256, and 3072/256). A private key is a
number x from 1 to q - 1 and its public key is y = g^x mod p.
The signing functions take the digest of the message, not the message. A digest longer than
q keeps its leftmost bytes, as OpenSSL does. A signature is r || s, each big-endian and as
long as q, the IEEE P1363 form that Node.js calls ieee-p1363. DsaSignature converts it
to and from the DER form that is the default of Node.js.
The private operations use the constant-time arithmetic of silk.crypto.modular: their time
does not depend on x or on the per-signature secret k.
Errors#
DsaErrorfor parameters, a key, a length, an encoding, or a signature that is not valid.
Gotchas#
The keys do not check that p and q are prime or that g has order q. Use keys and
parameters only from sources that you trust. Each signature needs a fresh secret from a
cryptographically secure Random; a repeated or predictable one reveals the private key.
Examples#
Sign and verify a digest#
import silk.core.effect { Effect }
import silk.core.result { Result }
import silk.crypto.dsa { DsaError, DsaParameters, DsaPrivateKey }
import silk.crypto.sha2 { Sha256 }
import silk.num.u64
import silk.system.random { Random }
// A test provider. Never use it for keys.
struct Counter {
state: u64,
}
impl Random for Counter {
effect fn fillBytes(self: &mut Self, output: &mut [u8]) -> () {
let mut index: usize = 0
while index < output.length {
self.state = u64.wrappingAdd(u64.wrappingMultiply(self.state, 6364136223846793005), 1)
output[index] = u64.toU8(u64.shiftRight(self.state, 56))
index = index + 1
}
}
}
effect fn signAndVerify() -> i32 ! DsaError ? &mut Random {
let parameters = run DsaParameters.generate(1024, 160)
let key = run DsaPrivateKey.generate(¶meters)
let digest = Sha256.hash(b"hello")
let mut signature: [u8; 40] = [0; 40]
run key.signDigest(&digest, &mut signature)
return match key.publicKey().verifyDigest(&digest, &signature) {
Result<(), DsaError>.Success { value: _ } => 0
Result<(), DsaError>.Failure { error: _ } => 1
}
}
pub effect fn main() -> i32 ! DsaError {
let mut random = Counter { state: 1 }
return run signAndVerify() |> Effect.provideMut<Random>(&mut random)
}Import as DsaPrivateKey with import silk.crypto.dsa { DsaPrivateKey }.
Public declarations: 5.
DsaError#
pub enum DsaErrorThe reason that DSA parameters, a key, an encoding, or a signature were rejected.
InvalidParameters#
InvalidParametersThe sizes are not supported: p is not 512 to 8192 bits, or q is not 160, 224, or 256
bits, or the values are not consistent: p or q is even, or g is not from 2 to p - 1.
InvalidKey#
InvalidKeyA key value is out of range: x is not from 1 to q - 1, or y is not from 2 to p - 1.
InvalidEncoding#
InvalidEncodingA DER value is not valid, not minimal, has bytes after it, or names another algorithm.
InvalidLength#
InvalidLengthAn output or a signature does not have the required length.
InvalidSignature#
InvalidSignatureThe signature does not match the digest and the key.
Implementation Debug for DsaError#
impl Debug for DsaErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the name of the variant, such as InvalidKey.
DsaParameters#
pub struct DsaParametersThe domain parameters of DSA: the primes p and q, where q divides p - 1, and the
generator g.
When to use#
Make one with DsaParameters.generate, from its parts with DsaParameters.fromComponents,
or from the DER Dss-Parms of a key or a DSA PARAMETERS PEM block with
DsaParameters.fromDer. Many keys can share one set of parameters.
Details#
p has 512 to 8192 bits and q has 160, 224, or 256 bits.
Gotchas#
It does not check that p and q are prime or that g has order q. It is a fixed-size
value of about 5 KiB. Pass it by reference.
Implementation Copy for DsaParameters#
impl Copy for DsaParametersDsaPublicKey#
pub struct DsaPublicKeyA DSA public key: the parameters and y = g^x mod p.
When to use#
Use it to verify signatures. Make one with DsaPublicKey.fromComponents, from a
SubjectPublicKeyInfo with DsaPublicKey.fromSpkiDer, or from a private key with
DsaPrivateKey.publicKey.
Gotchas#
It is a fixed-size value of about 6 KiB. Pass it by reference.
Implementation Copy for DsaPublicKey#
impl Copy for DsaPublicKeyDsaPrivateKey#
pub struct DsaPrivateKeyA DSA private key: the parameters, the secret x, and the public value y.
When to use#
Use it to sign digests. Generate one with DsaPrivateKey.generate, or read one with
DsaPrivateKey.fromComponents, DsaPrivateKey.fromPkcs8Der, or
DsaPrivateKey.fromTraditionalDer.
Gotchas#
It does not erase its memory when dropped. It is a fixed-size value of about 7 KiB. Pass it by reference.
Implementation Copy for DsaPrivateKey#
impl Copy for DsaPrivateKeyDsaSignature#
pub struct DsaSignatureConversions between the r || s form of a DSA signature and its DER form.
When to use#
Use it for Node.js dsaEncoding: der, the default, is the DER SEQUENCE of two
INTEGERs, and ieee-p1363 is r || s, which the signing functions of this module write.
Implementation DsaParameters#
impl DsaParametersAssociated function generate#
pub effect fn generate(pBits: usize, qBits: usize) -> DsaParameters
! DsaError
? &mut RandomReturns new parameters with a p of pBits bits and a q of qBits bits, from the ambient
Random, as generateKeyPair('dsa') and generateKeyPair('dsa', { divisorLength }) do in
Node.js.
q is a random prime of qBits bits, p a random prime of pBits bits with p = 1 modulo
2q, both tested with Prime.recommendedRounds of Miller-Rabin, and g is
h^((p - 1) / q) mod p for the first h from 2 up that does not give 1 (FIPS 186-4 A.2.1).
Errors#
InvalidParametersifpBitsis not 512 to 8192, orqBitsis not 160, 224, or 256.
Gotchas#
It takes a random time, about a second for 2048 bits and much longer for 3072 bits. The primes do not come from a seed, so they cannot be verified as FIPS 186-4 A.1.1.2 primes can.
Associated function fromComponents#
pub fn fromComponents(p: &[u8], q: &[u8], g: &[u8]) -> Result<DsaParameters, DsaError>Returns the parameters for the big-endian p, q, and g. Leading zero bytes are allowed.
Errors#
InvalidParametersifpis not an odd number of 512 to 8192 bits,qis not an odd number of 160, 224, or 256 bits, orgis not from 2 top - 1.
Gotchas#
It does not check that p and q are prime, that q divides p - 1, or that g has
order q.
Method primeBits#
pub fn primeBits(self: &Self) -> usizeReturns the number of bits of p, as modulusLength in Node.js.
Method divisorBits#
pub fn divisorBits(self: &Self) -> usizeReturns the number of bits of q, as divisorLength in Node.js.
Method primeLength#
pub fn primeLength(self: &Self) -> usizeReturns the length of p in bytes, which is the length of a public value y.
Method divisorLength#
pub fn divisorLength(self: &Self) -> usizeReturns the length of q in bytes, which is the length of a secret x and of each half of
a signature.
Method signatureLength#
pub fn signatureLength(self: &Self) -> usizeReturns the length in bytes of a signature in the r || s form: twice
DsaParameters.divisorLength.
Method prime#
pub fn prime(self: &Self) -> NaturalReturns p as a number.
Method divisor#
pub fn divisor(self: &Self) -> NaturalReturns q as a number.
Method generator#
pub fn generator(self: &Self) -> NaturalReturns g as a number, with the word length of p.
Implementation DsaPublicKey#
impl DsaPublicKeyAssociated function fromComponents#
pub fn fromComponents(parameters: &DsaParameters, y: &[u8]) -> Result<DsaPublicKey, DsaError>Returns the public key for parameters and the big-endian public value y. Leading zero
bytes are allowed.
Errors#
InvalidKeyifyis not from 2 top - 1.
Method parameters#
pub fn parameters(self: &Self) -> DsaParametersReturns the parameters of the key.
Method publicValue#
pub fn publicValue(self: &Self) -> NaturalReturns the public value y, with the word length of p.
Method verifyDigest#
pub fn verifyDigest(self: &Self, digest: &[u8], signature: &[u8]) -> Result<(), DsaError>Checks that signature is a valid signature in the r || s form of the message with the
digest digest.
A digest longer than q keeps its leftmost DsaParameters.divisorLength bytes. The time
depends on the values, which are all public.
Errors#
InvalidLengthifsignatureis notDsaParameters.signatureLengthbytes.InvalidSignatureifrorsis not from 1 toq - 1, or the signature does not match.
Implementation DsaPrivateKey#
impl DsaPrivateKeyAssociated function generate#
pub effect fn generate(parameters: &DsaParameters) -> DsaPrivateKey
? &mut RandomReturns a new key for parameters, with a secret from the ambient Random.
The secret is uniform from 1 to q - 1 (FIPS 186-4 B.1.1). The time does not depend on the
secret. The Random provider must be cryptographically secure.
Associated function fromComponents#
pub fn fromComponents(parameters: &DsaParameters, x: &[u8]) -> Result<DsaPrivateKey, DsaError>Returns the private key for parameters and the big-endian secret x, and computes its
public value. Leading zero bytes are allowed.
Errors#
InvalidKeyifxis not from 1 toq - 1.
Method parameters#
pub fn parameters(self: &Self) -> DsaParametersReturns the parameters of the key.
Method publicKey#
pub fn publicKey(self: &Self) -> DsaPublicKeyReturns the public half of the key.
Method secret#
pub fn secret(self: &Self) -> NaturalReturns the secret x, with the word length of q.
Gotchas#
The value is the private key. Keep it secret.
Method signDigest#
pub effect fn signDigest(self: &Self, digest: &[u8], signature: &mut [u8]) -> ()
! DsaError
? &mut RandomSigns the message with the digest digest and writes the signature to signature in the
r || s form. It reads the per-signature secret k from the ambient Random.
A digest longer than q keeps its leftmost DsaParameters.divisorLength bytes, as
OpenSSL does. The time does not depend on x or k.
Errors#
InvalidLengthifsignatureis notDsaParameters.signatureLengthbytes.
Gotchas#
The Random provider must be cryptographically secure: a repeated or biased k reveals
the private key.
Implementation DsaSignature#
impl DsaSignatureAssociated function derLength#
pub fn derLength(signature: &[u8]) -> usizeReturns the length of the DER form of the r || s signature signature.
Gotchas#
The program traps if signature has an odd length or is longer than 64 bytes.
Associated function toDer#
pub fn toDer(signature: &[u8], output: &mut [u8]) -> Result<usize, DsaError>Writes the DER form of the r || s signature signature to the start of output, and
returns its length, which is DsaSignature.derLength.
The bytes of output after the DER do not change.
Errors#
InvalidLengthifoutputis shorter than the DER.
Gotchas#
The program traps if signature has an odd length or is longer than 64 bytes.
Associated function fromDer#
pub fn fromDer(der: &[u8], output: &mut [u8]) -> Result<(), DsaError>Reads the DER signature der and writes it to all of output in the r || s form, each
half output.length / 2 bytes, such as DsaParameters.signatureLength.
Errors#
InvalidEncodingifderis not strict DER of two non-negative integers.InvalidSignatureifrorsis 0 or does not fit in half ofoutput.InvalidLengthifoutputhas an odd length.
Implementation DsaParameters#
impl DsaParametersAssociated function fromDer#
pub fn fromDer(der: &[u8]) -> Result<DsaParameters, DsaError>Reads the DER Dss-Parms der: SEQUENCE { p, q, g }, the content of a DSA PARAMETERS
PEM block.
Errors#
InvalidEncodingifderis not strict DER of three non-negative integers.InvalidParametersif the values are not valid forDsaParameters.fromComponents.
Method derLength#
pub fn derLength(self: &Self) -> usizeReturns the length of the DER Dss-Parms of the parameters.
Method writeDer#
pub fn writeDer(self: &Self, output: &mut [u8]) -> Result<usize, DsaError>Writes the DER Dss-Parms of the parameters to the start of output, and returns its
length, which is DsaParameters.derLength.
Errors#
InvalidLengthifoutputis shorter than the DER.
Implementation DsaPublicKey#
impl DsaPublicKeyAssociated function fromSpkiDer#
pub fn fromSpkiDer(der: &[u8]) -> Result<DsaPublicKey, DsaError>Reads the DER SubjectPublicKeyInfo der of a DSA key, with the id-dsa algorithm and its
Dss-Parms, as Node.js exports with type: 'spki'.
Errors#
InvalidEncodingifderis not strict DER, names another algorithm, or has no parameters.InvalidParametersif the parameters are not valid.InvalidKeyifyis not from 2 top - 1.
Method spkiDerLength#
pub fn spkiDerLength(self: &Self) -> usizeReturns the length of the DER SubjectPublicKeyInfo of the key.
Method writeSpkiDer#
pub fn writeSpkiDer(self: &Self, output: &mut [u8]) -> Result<usize, DsaError>Writes the DER SubjectPublicKeyInfo of the key to the start of output, and returns its
length, which is DsaPublicKey.spkiDerLength.
Errors#
InvalidLengthifoutputis shorter than the DER.
Implementation DsaPrivateKey#
impl DsaPrivateKeyAssociated function fromPkcs8Der#
pub fn fromPkcs8Der(der: &[u8]) -> Result<DsaPrivateKey, DsaError>Reads the DER PKCS #8 PrivateKeyInfo der of a DSA key, with the id-dsa algorithm and
its Dss-Parms and the secret as an INTEGER, as Node.js exports with type: 'pkcs8'.
It computes the public value from the secret. Version 1 keys, attributes, and a public key field (RFC 5958) are accepted and the public key field is ignored.
Errors#
InvalidEncodingifderis not strict DER or names another algorithm.InvalidParametersif the parameters are not valid.InvalidKeyifxis not from 1 toq - 1.
Method pkcs8DerLength#
pub fn pkcs8DerLength(self: &Self) -> usizeReturns the length of the DER PKCS #8 PrivateKeyInfo of the key.
Method writePkcs8Der#
pub fn writePkcs8Der(self: &Self, output: &mut [u8]) -> Result<usize, DsaError>Writes the DER PKCS #8 PrivateKeyInfo of the key, version 0 with the secret as an
INTEGER, to the start of output, and returns its length, which is
DsaPrivateKey.pkcs8DerLength.
Errors#
InvalidLengthifoutputis shorter than the DER.
Associated function fromTraditionalDer#
pub fn fromTraditionalDer(der: &[u8]) -> Result<DsaPrivateKey, DsaError>Reads the DER OpenSSL DSA private key der: SEQUENCE { 0, p, q, g, y, x }, the content of
a DSA PRIVATE KEY PEM block.
Errors#
InvalidEncodingifderis not strict DER of six non-negative integers, or the version is not 0.InvalidParametersif the parameters are not valid.InvalidKeyifxis not from 1 toq - 1, oryis notg^x mod p.
Method traditionalDerLength#
pub fn traditionalDerLength(self: &Self) -> usizeReturns the length of the DER OpenSSL DSA private key of the key.
Method writeTraditionalDer#
pub fn writeTraditionalDer(self: &Self, output: &mut [u8]) -> Result<usize, DsaError>Writes the DER OpenSSL DSA private key SEQUENCE { 0, p, q, g, y, x } of the key to the
start of output, and returns its length, which is DsaPrivateKey.traditionalDerLength.
Errors#
InvalidLengthifoutputis shorter than the DER.
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.