silk.crypto.dh.
Finite-field Diffie-Hellman key agreement (DH) over a prime modulus.
When to use#
Use DhPrivateKey where a protocol needs classic Diffie-Hellman, such as Node.js
crypto.createDiffieHellman and crypto.getDiffieHellman, SSH diffie-hellman-group14, or the
TLS groups ffdhe2048 and up. For a new design, silk.crypto.x25519 is faster and simpler.
Details#
A DhGroup is a prime p and a generator g. DhGroup.named gives the MODP groups of RFC
2409 and RFC 3526 and the FFDHE groups of RFC 7919, all with g = 2. DhGroup.make takes any
odd prime from 512 to 8192 bits. A secret x gives the public key g^x mod p, and
DhPrivateKey.agree returns y^x mod p for the peer key y, big-endian and padded to the
length of p, as Node.js and TLS expect. The exponentiation takes the same time for every
secret of a group. Nothing allocates.
Errors#
DhErrorfor a group, a secret, or a peer key that is not valid.
Gotchas#
DhGroup.make does not test that p is prime. Call DhGroup.check on a group from a
source that you do not trust, and use only safe primes. The key exchange does not authenticate the
peer. Pass the shared secret through silk.crypto.hkdf before you use it as a key. The secret
is not erased when it is dropped.
Examples#
Agree on a shared secret#
import silk.crypto.dh { DhError, DhGroup, DhNamedGroup, DhPrivateKey }
import silk.crypto.modular { Natural }
import silk.core.result { Result }
pub fn main() -> i32 {
let group = DhGroup.named(DhNamedGroup.Modp14)
// Real programs use `DhPrivateKey.generate(&group)` with a secure `Random` provider.
let alice = match DhPrivateKey.fromBytes(&group, b"\x51\x7a\x2c\x09\x33") {
Result<DhPrivateKey, DhError>.Success { value } => value
Result<DhPrivateKey, DhError>.Failure {..} => {
return 1
}
}
let bob = match DhPrivateKey.fromBytes(&group, b"\x07\xe2\x91\x4c\x6d") {
Result<DhPrivateKey, DhError>.Success { value } => value
Result<DhPrivateKey, DhError>.Failure {..} => {
return 1
}
}
let mut alicePublic: [u8; 256] = [0; 256]
let mut bobPublic: [u8; 256] = [0; 256]
alice.writePublicKey(&mut alicePublic)
bob.writePublicKey(&mut bobPublic)
let mut fromAlice: [u8; 256] = [0; 256]
let mut fromBob: [u8; 256] = [0; 256]
match alice.agree(&bobPublic, &mut fromAlice) {
Result<(), DhError>.Success {..} => {}
Result<(), DhError>.Failure {..} => {
return 1
}
}
match bob.agree(&alicePublic, &mut fromBob) {
Result<(), DhError>.Success {..} => {}
Result<(), DhError>.Failure {..} => {
return 1
}
}
let mut i: usize = 0
while i < 256 {
if fromAlice[i] != fromBob[i] {
return 1
}
i = i + 1
}
return 0
}Import as DhPrivateKey with import silk.crypto.dh { DhPrivateKey }.
Public declarations: 5.
DhError#
pub enum DhErrorThe reason that a DH group, secret, or peer key is not valid. It holds no secret bytes.
InvalidPrime#
InvalidPrimeThe prime is even, or not from 512 to 8192 bits.
InvalidGenerator#
InvalidGeneratorThe generator is not from 2 to p - 2.
InvalidSecret#
InvalidSecretThe secret is 0, or not less than p.
KeyTooSmall#
KeyTooSmallThe peer key is 0 or 1. OpenSSL calls this a key that is too small.
KeyTooLarge#
KeyTooLargeThe peer key is p - 1 or more. OpenSSL calls this a key that is too large.
InvalidLength#
InvalidLengthAn output does not have the length of the prime.
Implementation Debug for DhError#
impl Debug for DhErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the name of the variant, such as InvalidPrime.
DhNamedGroup#
pub enum DhNamedGroupA standard DH group. Every one has the generator 2 and a safe prime p = 2q + 1.
Modp1#
Modp1The 768-bit MODP group 1 of RFC 2409. It is too small to be secure; use it only to talk to old systems.
Modp2#
Modp2The 1024-bit MODP group 2 of RFC 2409. It is too small to be secure; use it only to talk to old systems.
Modp5#
Modp5The 1536-bit MODP group 5 of RFC 3526.
Modp14#
Modp14The 2048-bit MODP group 14 of RFC 3526.
Modp15#
Modp15The 3072-bit MODP group 15 of RFC 3526.
Modp16#
Modp16The 4096-bit MODP group 16 of RFC 3526.
Modp17#
Modp17The 6144-bit MODP group 17 of RFC 3526.
Modp18#
Modp18The 8192-bit MODP group 18 of RFC 3526.
Ffdhe2048#
Ffdhe2048The 2048-bit group ffdhe2048 of RFC 7919, for TLS.
Ffdhe3072#
Ffdhe3072The 3072-bit group ffdhe3072 of RFC 7919, for TLS.
Ffdhe4096#
Ffdhe4096The 4096-bit group ffdhe4096 of RFC 7919, for TLS.
Ffdhe6144#
Ffdhe6144The 6144-bit group ffdhe6144 of RFC 7919, for TLS.
Ffdhe8192#
Ffdhe8192The 8192-bit group ffdhe8192 of RFC 7919, for TLS.
Implementation Debug for DhNamedGroup#
impl Debug for DhNamedGroupOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
! WriterError
? &mut WriterWrites the name of the variant, such as Modp14.
DhGroup#
pub struct DhGroupA DH group: a prime modulus p and a generator g.
When to use#
Use DhGroup.named for a standard group, or DhGroup.make for a group that the peer sends.
Details#
The group is a fixed-size Copy value of about 3 KiB that does not allocate.
Implementation Copy for DhGroup#
impl Copy for DhGroupImplementation DhGroup#
impl DhGroupAssociated function named#
pub fn named(group: DhNamedGroup) -> DhGroupReturns the standard group group.
Associated function make#
pub fn make(prime: &[u8], generator: &[u8]) -> Result<DhGroup, DhError>Returns the group with the big-endian prime prime and generator generator.
Leading zero bytes are allowed.
Errors#
InvalidPrimeif the prime is even, or not from 512 to 8192 bits.InvalidGeneratorif the generator is not from 2 top - 2.
Gotchas#
It does not test that prime is prime; DhGroup.check does.
Method prime#
pub fn prime(self: &Self) -> NaturalReturns the prime p, with the word length of the group.
Method generator#
pub fn generator(self: &Self) -> NaturalReturns the generator g, with the word length of the group.
Method primeLength#
pub fn primeLength(self: &Self) -> usizeReturns the length of p in bytes, such as 256 for a 2048-bit group. Public keys and shared
secrets have this length.
Method primeBits#
pub fn primeBits(self: &Self) -> usizeReturns the length of p in bits.
Method check#
pub effect fn check(self: &Self) -> DhCheck
? &mut RandomTests whether p is prime and whether (p - 1) / 2 is prime too, as OpenSSL DH_check
does for a group without a known subgroup order.
It uses silk.crypto.prime with Prime.recommendedRounds and reads random bases from the
ambient Random. For an 8192-bit group it takes seconds, so check a custom group once, and do
not check the named groups, which are safe primes.
DhCheck#
pub struct DhCheckThe result of DhGroup.check: whether the prime of a group is prime and safe.
Details#
Node.js reports the same facts as verifyError: DH_CHECK_P_NOT_PRIME (1) when
DhCheck.prime is false, and DH_CHECK_P_NOT_SAFE_PRIME (2) when DhCheck.safePrime is
false.
Field prime#
pub prime: boolWhether p passed the primality test.
Field safePrime#
pub safePrime: boolWhether p and (p - 1) / 2 both passed the primality test.
DhPrivateKey#
pub struct DhPrivateKeyA DH secret with its group and public key.
When to use#
Make one with DhPrivateKey.generate for each key exchange, send the public key from
DhPrivateKey.writePublicKey, and call DhPrivateKey.agree with the peer key.
Details#
The value is a fixed-size Copy value of about 5 KiB that does not allocate. The secret has the
word length of the group, so every exponentiation in a group takes the same time.
Gotchas#
Pass the shared secret through silk.crypto.hkdf before you use it as a key. The secret is not
erased when it is dropped.
Implementation Copy for DhPrivateKey#
impl Copy for DhPrivateKeyImplementation DhPrivateKey#
impl DhPrivateKeyAssociated function generate#
pub effect fn generate(group: &DhGroup) -> DhPrivateKey
? &mut RandomReturns a new random secret in group, with bytes from the ambient Random.
The secret is uniform from 2 to 2^(n - 1) - 1 for an n-bit prime, as in OpenSSL. The
Random provider must be cryptographically secure. If it fails, the program stops.
Associated function fromBytes#
pub fn fromBytes(group: &DhGroup, secret: &[u8]) -> Result<DhPrivateKey, DhError>Returns the key for the big-endian secret secret in group, as Node.js
setPrivateKey does.
Leading zero bytes are allowed.
Errors#
InvalidSecretif the secret is 0, or not less thanp.
Method group#
pub fn group(self: &Self) -> DhGroupReturns the group of the key.
Method secret#
pub fn secret(self: &Self) -> NaturalReturns the secret x, with the word length of the group.
Gotchas#
The value is the private key. Keep it secret.
Method publicKey#
pub fn publicKey(self: &Self) -> NaturalReturns the public key g^x mod p, with the word length of the group.
Method writePublicKey#
pub fn writePublicKey(self: &Self, output: &mut [u8]) -> ()Writes the public key to all of output, big-endian with leading zero bytes.
Gotchas#
The program traps if output is shorter than DhGroup.primeLength.
Method agree#
pub fn agree(self: &Self, peer: &[u8], output: &mut [u8]) -> Result<(), DhError>Writes the shared secret y^x mod p for the big-endian peer key peer to output,
big-endian and padded with leading zero bytes to the length of p.
Leading zero bytes in peer are allowed. The time does not depend on the secret or on the
value of the peer key.
Errors#
InvalidLengthifoutputis notDhGroup.primeLengthbytes.KeyTooSmallif the peer key is 0 or 1.KeyTooLargeif the peer key isp - 1or more.
output does not change when it fails.
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.