Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions lib/crypto/src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Instruction dispatch is public. Each function here reports whether an
//! instruction path ran; when none did, the caller computes the same result
//! Instruction dispatch is public. Each dispatch function here reports whether
//! an instruction path ran; when none did, the caller computes the same result
//! portably. UEFI assembly saves and restores every used XMM register;
//! ordinary hosts use the target's ABI. Hosted AVX paths additionally require
//! CPUID support and OS-enabled extended state in XCR0.
Expand Down Expand Up @@ -34,6 +34,26 @@ macro_rules! hosted {
}};
}

/// `value`, through an assembly block LLVM cannot see into. The block is a
/// comment: it emits no instruction and keeps the word in its register.
#[cfg(not(miri))]
#[inline(always)]
pub(crate) fn value_barrier(mut value: u64) -> u64 {
// The template reads and writes only the operand's register.
unsafe {
core::arch::asm!("/* {0} */", inout(reg) value, options(pure, nomem, nostack, preserves_flags))
};
value
}

/// Miri runs no assembly; a volatile read of the local stands in.
#[cfg(miri)]
#[inline(always)]
pub(crate) fn value_barrier(value: u64) -> u64 {
// The local word remains live for this volatile read.
unsafe { core::ptr::read_volatile(&value) }
}

/// GHASH key powers, (H^(i+1))' at index i in the instruction paths' form.
pub(crate) type Powers = [u128; 16];

Expand Down
8 changes: 4 additions & 4 deletions lib/crypto/src/ct.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! Content-independent comparisons and selection. Lengths are public.
//!
//! The volatile barrier prevents LLVM from retaining a boolean's provenance;
//! deployed machine code must still be inspected and tested for timing leakage.
//! The register barrier prevents LLVM from retaining a boolean's provenance,
//! which would let it turn a masked select into a branch; deployed machine
//! code must still be inspected and tested for timing leakage.

#[inline(always)]
pub(crate) fn opaque(value: u64) -> u64 {
// The local word remains live for this volatile read.
unsafe { core::ptr::read_volatile(&value) }
crate::arch::value_barrier(value)
}

#[inline(always)]
Expand Down
62 changes: 19 additions & 43 deletions lib/crypto/src/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,19 @@
//! the only disclosed result is whether at least one candidate was valid.

macro_rules! curve_api {
($module:ident,$limbs:literal,$bytes:literal,$encoded:literal,$hash:ident) => {
($module:ident, $curve:ident, $limbs:literal, $bytes:literal, $encoded:literal, $hash:ident) => {
pub mod $module {
use crate::{
Error, Random,
hash::$hash,
montgomery::{Mod, V},
nist::{Curve, Point},
nist::{$curve, Point, Scalar},
secret::Secret,
};
pub struct PublicKey {
curve: Curve<$limbs>,
point: Point<$limbs>,
point: Point<$curve, $limbs>,
encoded: [u8; $encoded],
}
pub struct SecretKey {
curve: Curve<$limbs>,
scalar: V<$limbs>,
}
pub struct SecretKey(Scalar<$curve, $limbs>);
pub struct SharedSecret(Secret<$bytes>);
impl SharedSecret {
pub fn as_bytes(&self) -> &[u8; $bytes] {
Expand All @@ -30,14 +25,9 @@ macro_rules! curve_api {
}
impl PublicKey {
pub fn from_sec1(input: &[u8]) -> Result<Self, Error> {
let curve = crate::nist::$module();
let point = curve.decode(input)?;
let point = Point::decode(input)?;
let encoded = input.try_into().map_err(|_| Error::Length)?;
Ok(Self {
curve,
point,
encoded,
})
Ok(Self { point, encoded })
}
pub fn as_sec1(&self) -> &[u8; $encoded] {
&self.encoded
Expand All @@ -50,49 +40,35 @@ macro_rules! curve_api {
digest: &[u8; $bytes],
der_signature: &[u8],
) -> Result<(), Error> {
self.curve.verify(&self.point, digest, der_signature)
self.point.verify(digest, der_signature)
}
}
impl SecretKey {
pub fn from_bytes(bytes: [u8; $bytes]) -> Result<Self, Error> {
let bytes = Secret(bytes);
let curve = crate::nist::$module();
let scalar = curve.scalar(&bytes.0)?;
Ok(Self { curve, scalar })
Ok(Self(Scalar::from_bytes(&bytes.0)?))
}
pub fn generate(random: &mut impl Random) -> Result<Self, Error> {
let curve = crate::nist::$module();
let mut scalar = V::zero();
let mut selected = 0;
for _ in 0..4 {
let mut candidate = Secret([0; $bytes]);
random.fill(&mut candidate.0)?;
let candidate = Mod::from_be(&candidate.0);
let valid = curve.n.valid(&candidate) & (1 ^ candidate.eq(&V::zero()));
scalar = V::select(&scalar, &candidate, valid & (1 ^ selected));
selected |= valid;
}
if selected != 1 {
return Err(Error::Entropy);
}
Ok(Self { curve, scalar })
let mut candidates = Secret([0; 4 * $bytes]);
random.fill(&mut candidates.0)?;
Ok(Self(Scalar::first_valid(&candidates.0)?))
}
pub fn public_key(&self) -> PublicKey {
let point = self.curve.base().mul(&self.scalar, &self.curve);
// The base point's multiple by a nonzero scalar below n
// is finite and on the curve.
let point = Point::base().mul(&self.0);
let mut encoded = [0; $encoded];
point
.encode(&self.curve, &mut encoded)
.expect("nonzero prime-order scalar");
PublicKey::from_sec1(&encoded).expect("curve arithmetic invariant")
point.encode(&mut encoded).expect("nonzero prime-order scalar");
PublicKey { point, encoded }
}
pub fn agree(self, peer: &PublicKey) -> Result<SharedSecret, Error> {
let mut out = Secret([0; $bytes]);
self.curve.agree(&self.scalar, &peer.point, &mut out.0)?;
peer.point.agree(&self.0, &mut out.0);
Ok(SharedSecret(out))
}
}
}
};
}
curve_api!(p256, 4, 32, 65, Sha256);
curve_api!(p384, 6, 48, 97, Sha384);
curve_api!(p256, P256, 4, 32, 65, Sha256);
curve_api!(p384, P384, 6, 48, 97, Sha384);
63 changes: 53 additions & 10 deletions lib/crypto/src/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,34 @@
use crate::{Error, ct, field25519::F, hash::Sha512, scalar25519::S};
#[cfg(feature = "sign")]
use crate::{Random, secret::Secret};
const D: [u8; 32] = [
/// 2d for the curve's d = -121665/121666 (RFC 8032 section 5.1).
const D2: F = F::from_bytes(&[
0x59, 0xf1, 0xb2, 0x26, 0x94, 0x9b, 0xd6, 0xeb, 0x56, 0xb1, 0x83, 0x82, 0x9a, 0x14, 0xe0, 0x00,
0x30, 0xd1, 0xf3, 0xee, 0xf2, 0x80, 0x8e, 0x19, 0xe7, 0xfc, 0xdf, 0x56, 0xdc, 0xd9, 0x06, 0x24,
]);
const D: F = F::from_bytes(&[
0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, 0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, 0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52,
];
const SQRT_M1: [u8; 32] = [
]);
const SQRT_M1: F = F::from_bytes(&[
0xb0, 0xa0, 0x0e, 0x4a, 0x27, 0x1b, 0xee, 0xc4, 0x78, 0xe4, 0x2f, 0xad, 0x06, 0x18, 0x43, 0x2f,
0xa7, 0xd7, 0xfb, 0x3d, 0x99, 0x00, 0x4d, 0x2b, 0x0b, 0xdf, 0xc1, 0x4f, 0x80, 0x24, 0x83, 0x2b,
]);
/// The base point B's encoding (RFC 8032 section 5.1): y = 4/5, x positive.
const B_ENCODED: [u8; 32] = [
0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
];
const B_X: F = F::from_bytes(&[
0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69,
0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21,
]);
const B_Y: F = F::from_bytes(&B_ENCODED);
/// x * y of B.
const B_T: F = F::from_bytes(&[
0xa3, 0xdd, 0xb7, 0xa5, 0xb3, 0x8a, 0xde, 0x6d, 0xf5, 0x52, 0x51, 0x77, 0x80, 0x9f, 0xf0, 0x20,
0x7d, 0xe3, 0xab, 0x64, 0x8e, 0x4e, 0xea, 0x66, 0x65, 0x76, 0x8b, 0xd7, 0x0f, 0x5f, 0x87, 0x67,
]);
struct Point {
x: F,
y: F,
Expand All @@ -31,9 +51,12 @@ impl Point {
}
}
fn base() -> Self {
let mut b = [0x66; 32];
b[0] = 0x58;
Self::decode(&b).expect("Ed25519 base point")
Self {
x: B_X,
y: B_Y,
z: F::one(),
t: B_T,
}
}
fn decode(input: &[u8; 32]) -> Result<Self, Error> {
let mut b = *input;
Expand All @@ -45,13 +68,13 @@ impl Point {
}
let y2 = y.square();
let u = y2.sub(&F::one());
let v = F::from_bytes(&D).mul(&y2).add(&F::one());
let v = D.mul(&y2).add(&F::one());
let q = u.mul(&v.invert());
let mut exponent = [0xff; 32];
exponent[0] = 0xfe;
exponent[31] = 0x0f;
let root = q.pow(&exponent, 252);
let alt = root.mul(&F::from_bytes(&SQRT_M1));
let alt = root.mul(&SQRT_M1);
let root = F::select(&alt, &root, root.square().eq(&q));
if root.square().eq(&q) == 0 || (root.eq(&F::zero()) == 1 && sign == 1) {
return Err(Error::InvalidPoint);
Expand All @@ -76,8 +99,9 @@ impl Point {
fn add(&self, q: &Self) -> Self {
let a = self.y.sub(&self.x).mul(&q.y.sub(&q.x));
let b = self.y.add(&self.x).mul(&q.y.add(&q.x));
let c = self.t.mul(&q.t).mul(&F::from_bytes(&D)).mul(&F::small(2));
let d = self.z.mul(&q.z).mul(&F::small(2));
let c = self.t.mul(&q.t).mul(&D2);
let zz = self.z.mul(&q.z);
let d = zz.add(&zz);
let e = b.sub(&a);
let f = d.sub(&c);
let g = d.add(&c);
Expand Down Expand Up @@ -208,3 +232,22 @@ impl SigningKey {
Ok(out)
}
}

#[cfg(test)]
mod tests {
use super::*;

/// The compile-time constants are what their definitions compute.
#[test]
fn constants_match_their_definitions() {
let decoded = Point::decode(&B_ENCODED).unwrap();
let base = Point::base();
for (a, b) in [(&decoded.x, &base.x), (&decoded.y, &base.y), (&decoded.t, &base.t)] {
assert_eq!(a.bytes(), b.bytes());
}
assert_eq!(D.add(&D).bytes(), D2.bytes());
let d = F::small(121665).neg().mul(&F::small(121666).invert());
assert_eq!(d.bytes(), D.bytes());
assert_eq!(SQRT_M1.square().bytes(), F::one().neg().bytes());
}
}
9 changes: 6 additions & 3 deletions lib/crypto/src/field25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ impl F {
pub(crate) fn small(x: u32) -> Self {
Self([u64::from(x), 0, 0, 0, 0])
}
pub(crate) fn from_bytes(b: &[u8; 32]) -> Self {
/// The low 255 bits of a little-endian encoding.
pub(crate) const fn from_bytes(b: &[u8; 32]) -> Self {
let mut v = [0u64; 5];
for i in 0..255 {
v[i / 51] |= u64::from((b[i / 8] >> (i % 8)) & 1) << (i % 51)
let mut i = 0;
while i < 255 {
v[i / 51] |= (((b[i / 8] >> (i % 8)) & 1) as u64) << (i % 51);
i += 1;
}
Self(v)
}
Expand Down
Loading