/// 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); }
/// 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 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)); }