예제 #1
0
        /// 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);
        }
예제 #2
0
        /// 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);
        }
예제 #3
0
        /// 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());
        }
예제 #4
0
        /// 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));
        }
예제 #5
0
        /// 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));
        }
예제 #6
0
        /// 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));
        }
예제 #7
0
        /// 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));
        }
예제 #8
0
        ///// 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()));
        }
예제 #9
0
        ///// 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);
        }
예제 #10
0
 public static Scalar operator -(Scalar rhs)
 {
     return(UnpackedScalar.sub(one().unpack(), rhs.unpack()).pack());
 }
예제 #11
0
        // Add

        public static Scalar operator +(Scalar lhs, Scalar rhs)
        {
            return(UnpackedScalar.add(lhs.unpack(), rhs.unpack()).pack());
        }
예제 #12
0
 /// 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());
 }
예제 #13
0
        /// 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)));
        }
예제 #14
0
        /// 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)));
        }
예제 #15
0
        /// 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));
        }
예제 #16
0
        /// 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)));
        }
예제 #17
0
        /// 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)));
        }