silk.crypto.modular.

Constant-time arithmetic on unsigned integers of up to 8192 bits, and modulo an odd number.

When to use#

Use it to build public-key cryptography that works on large numbers, such as RSA and finite field Diffie-Hellman. silk.crypto.rsa uses it. For a fixed curve, use that curve's module, such as silk.crypto.p256, which is faster.

Details#

A Natural holds a value below 2^8192 in 32-bit words. It also has a length in words, which Natural.fromBytes takes from the number of input bytes, not from the value. A Modulus holds an odd number of 3 or more and the constants for Montgomery multiplication modulo it.

Every operation takes the same time for all values of the same lengths: it has no branch and no memory access that depends on a value. The time depends only on the word lengths of the inputs and the bit length of the modulus. Modulus.powerPublic is the one exception: its time depends on the exponent, so use it only for a public exponent.

Errors#

  • ModularError for a value that does not fit, a subtraction below zero, or a modulus that is even or below 3.

Gotchas#

The operations of a Modulus need inputs that are less than the modulus. Reduce a value with Modulus.reduce first if it may be larger. The values stay in memory after use; nothing erases them.

Examples#

Compute a power modulo a prime#

silk
import silk.core.ownership { Copy }
import silk.core.result { Result }
import silk.crypto.modular { ModularError, Modulus, Natural }

pub fn main() -> i32 {
  let modulus = match Modulus.make(&Natural.fromU64(1000003)) {
    Result<Modulus, ModularError>.Success { value } => move value
    Result<Modulus, ModularError>.Failure { error: _ } => {
      return 1
    }
  }
  // 2^1000002 is 1 modulo the prime 1000003.
  let result = modulus.power(&Natural.fromU64(2), &Natural.fromU64(1000002))
  let mut bytes: [u8; 4] = [0; 4]
  match result.writeBytes(&mut bytes) {
    Result<(), ModularError>.Success { value: _ } => {}
    Result<(), ModularError>.Failure { error: _ } => {
      return 2
    }
  }
  if bytes[0] != 0 || bytes[1] != 0 || bytes[2] != 0 || bytes[3] != 1 {
    return 3
  }
  return 0
}

Import as Modulus with import silk.crypto.modular { Modulus }.

Public declarations: 4.

NATURAL_WORDS#

silk
pub const NATURAL_WORDS: usize

The number of 32-bit words in a Natural: 256 words hold 8192 bits.

ModularError#

silk
pub enum ModularError

The reason that a modular operation failed.

TooLarge#

silk
TooLarge

A value does not fit in 8192 bits, or in the output.

Negative#

silk
Negative

A subtraction would give a value below zero.

InvalidModulus#

silk
InvalidModulus

A modulus is even or below 3.

Implementation Debug for ModularError#

silk
impl Debug for ModularError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

Writes the name of the variant, such as TooLarge.

Natural#

silk
pub struct Natural

An unsigned integer below 2^8192 with a length in 32-bit words.

When to use#

Use it for the values of public-key cryptography: keys, messages, and signatures as numbers.

Details#

The length sets how many words the operations read, and so how long they take. It comes from the size of the input, not from the value: Natural.fromBytes with 256 bytes gives 64 words even when the value is small. Results of arithmetic have the length that holds any result of inputs of those lengths.

Gotchas#

It is a fixed-size value of about 1 KiB. Pass it by reference.

Implementation Copy for Natural#

silk
impl Copy for Natural

Modulus#

silk
pub struct Modulus

An odd modulus of 3 or more, with the constants for Montgomery multiplication modulo it.

When to use#

Make one with Modulus.make for each modulus, such as an RSA modulus, an RSA prime, or a Diffie-Hellman prime, and keep it for all the operations with that modulus.

Details#

Making a modulus of k words computes 2^(64 * k) modulo it, which takes about as long as two multiplications of k-word numbers per bit of the modulus. The operations then take inputs below the modulus and return results below it, with the word length of the modulus.

Gotchas#

It is a fixed-size value of about 2 KiB. Pass it by reference.

Implementation Copy for Modulus#

silk
impl Copy for Modulus

Implementation Natural#

silk
impl Natural

Associated function zero#

silk
pub fn zero() -> Natural

Returns 0 with a length of 0 words.

Associated function fromU64#

silk
pub fn fromU64(value: u64) -> Natural

Returns value with a length of 2 words.

Associated function fromBytes#

silk
pub fn fromBytes(bytes: &[u8]) -> Result<Natural, ModularError>

Returns the big-endian bytes as a number with a length of bytes.length / 4 words, rounded up.

Leading zero bytes are allowed. The time depends only on bytes.length.

Errors#

  • TooLarge if the value is 2^8192 or more.

Method writeBytes#

silk
pub fn writeBytes(self: &Natural, output: &mut [u8]) -> Result<(), ModularError>

Writes the value big-endian to all of output, with leading zero bytes as needed.

The time depends only on output.length and the length of the value.

Errors#

  • TooLarge if the value does not fit in output.length bytes. output may then hold part of the value.

Method wordLength#

silk
pub fn wordLength(self: &Natural) -> usize

Returns the length in 32-bit words, which sets the time of the operations.

Method bitLength#

silk
pub fn bitLength(self: &Natural) -> usize

Returns the number of significant bits: 0 for 0, and n for a value from 2^(n - 1) to 2^n - 1. The time depends only on the length.

Method bit#

silk
pub fn bit(self: &Natural, index: usize) -> u32

Returns bit index, counted from the least significant bit, as 0 or 1. Bits at or above the length are 0.

Method isZero#

silk
pub fn isZero(self: &Natural) -> bool

Returns whether the value is 0. The time depends only on the length.

Method equals#

silk
pub fn equals(self: &Natural, other: &Natural) -> bool

Returns whether the two values are equal. The time depends only on the lengths.

Method lessThan#

silk
pub fn lessThan(self: &Natural, other: &Natural) -> bool

Returns whether this value is less than other. The time depends only on the lengths.

Method remainder#

silk
pub fn remainder(self: &Natural, divisor: &Natural) -> Result<Natural, ModularError>

Returns the value modulo divisor, with the length of divisor.

The divisor can be even. The time depends only on the two lengths.

Errors#

  • InvalidModulus if divisor is 0.

Method resize#

silk
pub fn resize(self: &Natural, length: usize) -> Result<Natural, ModularError>

Returns the value with a length of length words.

Errors#

  • TooLarge if length is above 256 or the value does not fit in length words.

Method shiftRight#

silk
pub fn shiftRight(self: &Natural, count: usize) -> Natural

Returns the value divided by 2^count, rounded down, with the same length. The time depends only on count and the length.

Method divideWord#

silk
pub fn divideWord(self: &Natural, divisor: u32) -> Natural

Returns the value divided by divisor, rounded down, with the same length. The time depends only on the length.

Gotchas#

The program traps if divisor is 0.

Method remainderWord#

silk
pub fn remainderWord(self: &Natural, divisor: u32) -> u32

Returns the remainder of the value divided by divisor. The time depends only on the length.

Gotchas#

The program traps if divisor is 0.

Method add#

silk
pub fn add(self: &Natural, other: &Natural) -> Result<Natural, ModularError>

Returns the sum, with a length of one word more than the longer input, up to 256 words.

Errors#

  • TooLarge if the sum is 2^8192 or more.

Method subtract#

silk
pub fn subtract(self: &Natural, other: &Natural) -> Result<Natural, ModularError>

Returns this value minus other, with the length of the longer input.

Errors#

  • Negative if other is larger.

Method multiply#

silk
pub fn multiply(self: &Natural, other: &Natural) -> Result<Natural, ModularError>

Returns the product, with the sum of the input lengths, up to 256 words.

Errors#

  • TooLarge if the product is 2^8192 or more.

Implementation Modulus#

silk
impl Modulus

Associated function make#

silk
pub fn make(value: &Natural) -> Result<Modulus, ModularError>

Returns the modulus value, which must be odd and 3 or more.

The word length of the modulus is that of its value without leading zero words, so the time of this call and of later operations depends on the bit length of the value, but not on its other bits.

Errors#

  • InvalidModulus if value is even or below 3.

Method natural#

silk
pub fn natural(self: &Modulus) -> Natural

Returns a copy of the modulus as a number, with the word length of the modulus.

Method bitLength#

silk
pub fn bitLength(self: &Modulus) -> usize

Returns the number of significant bits of the modulus.

Method wordLength#

silk
pub fn wordLength(self: &Modulus) -> usize

Returns the word length of the modulus and of the results of its operations.

Method reduce#

silk
pub fn reduce(self: &Modulus, value: &Natural) -> Natural

Returns value modulo the modulus, for any value. The time depends on the length of value.

Method add#

silk
pub fn add(self: &Modulus, left: &Natural, right: &Natural) -> Natural

Returns left + right modulo the modulus. Both must be less than the modulus.

Method subtract#

silk
pub fn subtract(self: &Modulus, left: &Natural, right: &Natural) -> Natural

Returns left - right modulo the modulus. Both must be less than the modulus.

Method multiply#

silk
pub fn multiply(self: &Modulus, left: &Natural, right: &Natural) -> Natural

Returns left * right modulo the modulus. Both must be less than the modulus.

Method power#

silk
pub fn power(self: &Modulus, base: &Natural, exponent: &Natural) -> Natural

Returns base to the power exponent modulo the modulus. base must be less than the modulus; exponent can be any value.

The time depends only on the bit length of the modulus and the length of exponent, so the base and the exponent can be secret. A zero exponent gives 1.

Method powerPublic#

silk
pub fn powerPublic(self: &Modulus, base: &Natural, exponent: &Natural) -> Natural

Returns base to the power exponent modulo the modulus, faster than Modulus.power for a short exponent. base must be less than the modulus.

Gotchas#

Its time depends on the bits of exponent, so use it only for a public exponent, such as an RSA public exponent. The time does not depend on base.

Method invertPrime#

silk
pub fn invertPrime(self: &Modulus, value: &Natural) -> Natural

Returns the inverse of value modulo the modulus, which must be prime: the number that gives 1 when multiplied by value. value must be less than the modulus. 0 gives 0.

It computes value to the power modulus - 2, in the time of Modulus.power.

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.

View the source of this page