/// Unpack a 32 byte / 256 bit scalar into 5 52-bit limbs. public static UnpackedScalar from_bytes(byte[] bytes) { var words = new ulong[4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 8; j++) { words[i] |= ((ulong)bytes[(i * 8) + j]) << (j * 8); } } var mask = (((ulong)1) << 52) - 1; var top_mask = (((ulong)1) << 48) - 1; var s = UnpackedScalar.zero(); var ss = s.value.Span; ss[0] = words[0] & mask; ss[1] = ((words[0] >> 52) | (words[1] << 12)) & mask; ss[2] = ((words[1] >> 40) | (words[2] << 24)) & mask; ss[3] = ((words[2] >> 28) | (words[3] << 36)) & mask; ss[4] = (words[3] >> 16) & top_mask; return(s); }
/// Compute `a - b` (mod l) public static UnpackedScalar sub(UnpackedScalar a, UnpackedScalar b) { var difference = UnpackedScalar.zero(); var differencesp = difference.value.Span; var mask = ((ulong)1 << 52) - 1; var asp = a.value.Span; var bsp = b.value.Span; // a - b ulong borrow = 0; for (int i = 0; i < 5; i++) { borrow = asp[i] - (bsp[i] + (borrow >> 63)); differencesp[i] = borrow & mask; } // conditionally add l if the difference is negative var underflow_mask = ((borrow >> 63) ^ 1) - (1); ulong carry = 0; for (int i = 0; i < 5; i++) { carry = (carry >> 52) + differencesp[i] + (Constant.L.value.Span[i] & underflow_mask); differencesp[i] = carry & mask; } return(difference); }
/// Reduce this `Scalar` modulo \\(\ell\\). public Scalar reduce() { var x = unpack(); var xR = UnpackedScalar.mul_internal(x.Value, Constant.R.Value); var x_mod_l = UnpackedScalar.montgomery_reduce(xR); return(x_mod_l.pack()); }
/// Compute `limbs/R` (mod l), where R is the Montgomery modulus 2^260 public static UnpackedScalar montgomery_reduce(UInt128[] limbs) { (UInt128, ulong) part1(UInt128 sum) { var p = ((ulong)sum) * (Constant.LFACTOR) & (((ulong)1 << 52) - 1); return((sum + m(p, Constant.L.value.Span[0])) >> 52, p); } (UInt128, ulong) part2(UInt128 sum) { var w = ((ulong)sum) & (((ulong)1 << 52) - 1); return(sum >> 52, w); } // note: l3 is zero, so its multiplies can be skipped var l = Constant.L; var ls = l.value.Span; // the first half computes the Montgomery adjustment factor n, and begins adding n*l to make limbs divisible by R UInt128 carry; ulong n0; (carry, n0) = part1(limbs[0]); ulong n1; (carry, n1) = part1(carry + limbs[1] + m(n0, ls[1])); ulong n2; (carry, n2) = part1(carry + limbs[2] + m(n0, ls[2]) + m(n1, ls[1])); ulong n3; (carry, n3) = part1(carry + limbs[3] + m(n1, ls[2]) + m(n2, ls[1])); ulong n4; (carry, n4) = part1(carry + limbs[4] + m(n0, ls[4]) + m(n2, ls[2]) + m(n3, ls[1])); // limbs is divisible by R now, so we can divide by R by simply storing the upper half as the result ulong r0; (carry, r0) = part2(carry + limbs[5] + m(n1, ls[4]) + m(n3, ls[2]) + m(n4, ls[1])); ulong r1; (carry, r1) = part2(carry + limbs[6] + m(n2, ls[4]) + m(n4, ls[2])); ulong r2; (carry, r2) = part2(carry + limbs[7] + m(n3, ls[4])); ulong r3; (carry, r3) = part2(carry + limbs[8] + m(n4, ls[4])); var r4 = (ulong)carry; // result may be >= l, so attempt to subtract l return(UnpackedScalar.sub(new UnpackedScalar(new ulong[] { r0, r1, r2, r3, r4 }), l)); }
/// Takes a Scalar64 out of Montgomery form, i.e. computes `a/R (mod l)` public UnpackedScalar from_montgomery() { var limbs = new UInt128[9]; for (int i = 0; i < 5; i++) { limbs[i] = value.Span[i]; } return(UnpackedScalar.montgomery_reduce(limbs)); }
/// Compute `a + b` (mod l) public static UnpackedScalar add(UnpackedScalar a, UnpackedScalar b) { var sum = UnpackedScalar.zero(); var sums = sum.value.Span; var mask = ((ulong)1 << 52) - 1; var asp = a.value.Span; var bsp = b.value.Span; // a + b ulong carry = 0; for (int i = 0; i < 5; i++) { carry = asp[i] + bsp[i] + (carry >> 52); sums[i] = carry & mask; } // subtract l if the sum is >= l return(UnpackedScalar.sub(sum, Constant.L)); }
/// Reduce a 64 byte / 512 bit scalar mod l public static UnpackedScalar from_bytes_wide(byte[] bytes) { var words = new ulong[8]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { words[i] |= ((ulong)bytes[(i * 8) + j]) << (j * 8); } } var mask = (((ulong)1) << 52) - 1; var lo = UnpackedScalar.zero(); var hi = UnpackedScalar.zero(); var los = lo.value.Span; var his = hi.value.Span; los[0] = words[0] & mask; los[1] = ((words[0] >> 52) | (words[1] << 12)) & mask; los[2] = ((words[1] >> 40) | (words[2] << 24)) & mask; los[3] = ((words[2] >> 28) | (words[3] << 36)) & mask; los[4] = ((words[3] >> 16) | (words[4] << 48)) & mask; his[0] = (words[4] >> 4) & mask; his[1] = ((words[4] >> 56) | (words[5] << 8)) & mask; his[2] = ((words[5] >> 44) | (words[6] << 20)) & mask; his[3] = ((words[6] >> 32) | (words[7] << 32)) & mask; his[4] = words[7] >> 20; lo = UnpackedScalar.montgomery_mul(lo, Constant.R); // (lo * R) / R = lo hi = UnpackedScalar.montgomery_mul(hi, Constant.RR); // (hi * R^2) / R = hi * R return(UnpackedScalar.add(hi, lo)); }
///// Write this scalar in radix 16, with coefficients in \\([-8,8)\\), ///// i.e., compute \\(a\_i\\) such that ///// $$ ///// a = a\_0 + a\_1 16\^1 + \cdots + a_{63} 16\^{63}, ///// $$ ///// with \\(-8 \leq a_i < 8\\) for \\(0 \leq i < 63\\) and \\(-8 \leq a_{63} \leq 8\\). //pub(crate) fn to_radix_16(&self) -> [i8; 64] { // debug_assert!(self[31] <= 127); // let mut output = [0i8; 64]; // // Step 1: change radix. // // Convert from radix 256 (bytes) to radix 16 (nibbles) // #[inline(always)] // fn bot_half(x: u8) -> u8 { (x >> 0) & 15 } // #[inline(always)] // fn top_half(x: u8) -> u8 { (x >> 4) & 15 } // for i in 0..32 { // output[2*i ] = bot_half(self[i]) as i8; // output[2*i+1] = top_half(self[i]) as i8; // } // // Precondition note: since self[31] <= 127, output[63] <= 7 // // Step 2: recenter coefficients from [0,16) to [-8,8) // for i in 0..63 { // let carry = (output[i] + 8) >> 4; // output[i ] -= carry << 4; // output[i+1] += carry; // } // // Precondition note: output[63] is not recentered. It // // increases by carry <= 1. Thus output[63] <= 8. // output //} // Unpack this `Scalar` to an `UnpackedScalar` for faster arithmetic. public UnpackedScalar unpack() { return(UnpackedScalar.from_bytes(bytes.ToArray())); }
///// Given a slice of nonzero (possibly secret) `Scalar`s, ///// compute their inverses in a batch. ///// ///// # Return ///// ///// Each element of `inputs` is replaced by its inverse. ///// ///// The product of all inverses is returned. ///// ///// # Warning ///// ///// All input `Scalars` **MUST** be nonzero. If you cannot ///// *prove* that this is the case, you **SHOULD NOT USE THIS ///// FUNCTION**. ///// ///// # Example ///// ///// ``` ///// # extern crate curve25519_dalek; ///// # use curve25519_dalek::scalar::Scalar; ///// # fn main() { ///// let mut scalars = [ ///// Scalar::from(3u64), ///// Scalar::from(5u64), ///// Scalar::from(7u64), ///// Scalar::from(11u64), ///// ]; ///// ///// let allinv = Scalar::batch_invert(&mut scalars); ///// ///// assert_eq!(allinv, Scalar::from(3*5*7*11u64).invert()); ///// assert_eq!(scalars[0], Scalar::from(3u64).invert()); ///// assert_eq!(scalars[1], Scalar::from(5u64).invert()); ///// assert_eq!(scalars[2], Scalar::from(7u64).invert()); ///// assert_eq!(scalars[3], Scalar::from(11u64).invert()); ///// # } ///// ``` public static Scalar batch_invert(Scalar[] inputs) { // This code is essentially identical to the FieldElement // implementation, and is documented there. Unfortunately, // it's not easy to write it generically, since here we want // to use `UnpackedScalar`s internally, and `Scalar`s // externally, but there's no corresponding distinction for // field elements. //use clear_on_drop::ClearOnDrop; //use clear_on_drop::clear::ZeroSafe; // Mark UnpackedScalars as zeroable. //unsafe impl ZeroSafe for UnpackedScalar {} var n = inputs.Length; var one = Scalar.one().unpack().to_montgomery(); // Wrap the scratch storage in a ClearOnDrop to wipe it when // we pass out of scope. Span <Scalar> scratch_vec = new Scalar[n]; scratch_vec.Fill(Scalar.one()); // Keep an accumulator of all of the previous products var acc = Scalar.one().unpack().to_montgomery(); // Pass through the input vector, recording the previous // products in the scratch space for (int i = 0; i < n; i++) { var input = scratch_vec[n]; //scratch = acc; // Avoid unnecessary Montgomery multiplication in second pass by // keeping inputs in Montgomery form var tmp = input.unpack().to_montgomery(); input = tmp.pack(); acc = UnpackedScalar.montgomery_mul(acc, tmp); } // acc is nonzero iff all inputs are nonzero Debug.Assert(acc.pack() != zero()); // Compute the inverse of all products acc = acc.montgomery_invert().from_montgomery(); // We need to return the product of all inverses later var ret = acc.pack(); // Pass through the vector backwards to compute the inverses // in place for (int i = 0; i < n; i++) { var scratch = scratch_vec[n]; var input = inputs[n]; var tmp = UnpackedScalar.montgomery_mul(acc, input.unpack()); input = UnpackedScalar.montgomery_mul(acc, scratch.unpack()).pack(); acc = tmp; } return(ret); }
public static Scalar operator -(Scalar rhs) { return(UnpackedScalar.sub(one().unpack(), rhs.unpack()).pack()); }
// Add public static Scalar operator +(Scalar lhs, Scalar rhs) { return(UnpackedScalar.add(lhs.unpack(), rhs.unpack()).pack()); }
/// Construct a `Scalar` by reducing a 512-bit little-endian integer /// modulo the group order \\( \ell \\). public static Scalar from_bytes_mod_order_wide(byte[] input) { return(UnpackedScalar.from_bytes_wide(input).pack()); }
/// Compute `a * b` (mod l) public static UnpackedScalar mul(UnpackedScalar a, UnpackedScalar b) { var ab = UnpackedScalar.montgomery_reduce(UnpackedScalar.mul_internal(a.value.Span, b.value.Span)); return(UnpackedScalar.montgomery_reduce(UnpackedScalar.mul_internal(ab.value.Span, Constant.RR.value.Span))); }
/// Compute `a^2` (mod l) // XXX we don't expose square() via the Scalar API public UnpackedScalar square() { var aa = UnpackedScalar.montgomery_reduce(UnpackedScalar.square_internal(value.Span)); return(UnpackedScalar.montgomery_reduce(UnpackedScalar.mul_internal(aa.value.Span, Constant.RR.value.Span))); }
/// Puts a Scalar64 in to Montgomery form, i.e. computes `a*R (mod l)` public UnpackedScalar to_montgomery() { return(UnpackedScalar.montgomery_mul(this, Constant.RR)); }
/// Compute `(a^2) / R` (mod l) in Montgomery form, where R is the Montgomery modulus 2^260 public UnpackedScalar montgomery_square() { return(UnpackedScalar.montgomery_reduce(UnpackedScalar.square_internal(value.Span))); }
/// Compute `(a * b) / R` (mod l), where R is the Montgomery modulus 2^260 public static UnpackedScalar montgomery_mul(UnpackedScalar a, UnpackedScalar b) { return(UnpackedScalar.montgomery_reduce(UnpackedScalar.mul_internal(a.value.Span, b.value.Span))); }