From afea44b33abc96ba7ed10b443a76db6976170db4 Mon Sep 17 00:00:00 2001 From: yet300 Date: Sat, 9 May 2026 15:07:32 +0400 Subject: [PATCH] Convert Curve25519 and Curve448 to Kotlin --- .../noise/southernstorm/crypto/Curve25519.kt | 891 ++++++++-------- .../noise/southernstorm/crypto/Curve448.kt | 993 +++++++++--------- 2 files changed, 890 insertions(+), 994 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt index 4b06562a..fd6c1693 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve25519.kt @@ -19,513 +19,464 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ - -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; +package com.bitchat.android.noise.southernstorm.crypto /** * Implementation of the Curve25519 elliptic curve algorithm. * * This implementation is based on that from arduinolibs: * https://github.com/rweather/arduinolibs - * + * * Differences in this version are due to using 26-bit limbs for the * representation instead of the 8/16/32-bit limbs in the original. * * References: http://cr.yp.to/ecdh.html, RFC 7748 */ -public final class Curve25519 { +class Curve25519 { + private val x_1 = IntArray(NUM_LIMBS_255BIT) + private val x_2 = IntArray(NUM_LIMBS_255BIT) + private val x_3 = IntArray(NUM_LIMBS_255BIT) + private val z_2 = IntArray(NUM_LIMBS_255BIT) + private val z_3 = IntArray(NUM_LIMBS_255BIT) + private val A = IntArray(NUM_LIMBS_255BIT) + private val B = IntArray(NUM_LIMBS_255BIT) + private val C = IntArray(NUM_LIMBS_255BIT) + private val D = IntArray(NUM_LIMBS_255BIT) + private val E = IntArray(NUM_LIMBS_255BIT) + private val AA = IntArray(NUM_LIMBS_255BIT) + private val BB = IntArray(NUM_LIMBS_255BIT) + private val DA = IntArray(NUM_LIMBS_255BIT) + private val CB = IntArray(NUM_LIMBS_255BIT) + private val t1 = LongArray(NUM_LIMBS_510BIT) + private val t2 = IntArray(NUM_LIMBS_510BIT) - // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. - private static final int NUM_LIMBS_255BIT = 10; - private static final int NUM_LIMBS_510BIT = 20; - private int[] x_1; - private int[] x_2; - private int[] x_3; - private int[] z_2; - private int[] z_3; - private int[] A; - private int[] B; - private int[] C; - private int[] D; - private int[] E; - private int[] AA; - private int[] BB; - private int[] DA; - private int[] CB; - private long[] t1; - private int[] t2; + /** + * Destroy all sensitive data in this object. + */ + private fun destroy() { + // Destroy all temporary variables. + x_1.fill(0) + x_2.fill(0) + x_3.fill(0) + z_2.fill(0) + z_3.fill(0) + A.fill(0) + B.fill(0) + C.fill(0) + D.fill(0) + E.fill(0) + AA.fill(0) + BB.fill(0) + DA.fill(0) + CB.fill(0) + t1.fill(0L) + t2.fill(0) + } - /** - * Constructs the temporary state holder for Curve25519 evaluation. - */ - private Curve25519() - { - // Allocate memory for all of the temporary variables we will need. - x_1 = new int [NUM_LIMBS_255BIT]; - x_2 = new int [NUM_LIMBS_255BIT]; - x_3 = new int [NUM_LIMBS_255BIT]; - z_2 = new int [NUM_LIMBS_255BIT]; - z_3 = new int [NUM_LIMBS_255BIT]; - A = new int [NUM_LIMBS_255BIT]; - B = new int [NUM_LIMBS_255BIT]; - C = new int [NUM_LIMBS_255BIT]; - D = new int [NUM_LIMBS_255BIT]; - E = new int [NUM_LIMBS_255BIT]; - AA = new int [NUM_LIMBS_255BIT]; - BB = new int [NUM_LIMBS_255BIT]; - DA = new int [NUM_LIMBS_255BIT]; - CB = new int [NUM_LIMBS_255BIT]; - t1 = new long [NUM_LIMBS_510BIT]; - t2 = new int [NUM_LIMBS_510BIT]; - } - + /** + * Reduces a number modulo 2^255 - 19 where it is known that the + * number can be reduced with only 1 trial subtraction. + * + * @param x The number to reduce, and the result. + */ + private fun reduceQuick(x: IntArray) { + // Perform a trial subtraction of (2^255 - 19) from "x" which is + // equivalent to adding 19 and subtracting 2^255. We add 19 here; + // the subtraction of 2^255 occurs in the next step. + var carry = 19 + for (index in 0 until NUM_LIMBS_255BIT) { + carry += x[index] + t2[index] = carry and 0x03FFFFFF + carry = carry shr 26 + } - /** - * Destroy all sensitive data in this object. - */ - private void destroy() { - // Destroy all temporary variables. - Arrays.fill(x_1, 0); - Arrays.fill(x_2, 0); - Arrays.fill(x_3, 0); - Arrays.fill(z_2, 0); - Arrays.fill(z_3, 0); - Arrays.fill(A, 0); - Arrays.fill(B, 0); - Arrays.fill(C, 0); - Arrays.fill(D, 0); - Arrays.fill(E, 0); - Arrays.fill(AA, 0); - Arrays.fill(BB, 0); - Arrays.fill(DA, 0); - Arrays.fill(CB, 0); - Arrays.fill(t1, 0L); - Arrays.fill(t2, 0); - } + // If there was a borrow, then the original "x" is the correct answer. + // If there was no borrow, then "t2" is the correct answer. Select the + // correct answer but do it in a way that instruction timing will not + // reveal which value was selected. Borrow will occur if bit 21 of + // "t2" is zero. Turn the bit into a selection mask. + val mask = -((t2[NUM_LIMBS_255BIT - 1] shr 21) and 0x01) + val nmask = mask.inv() + t2[NUM_LIMBS_255BIT - 1] = t2[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + for (index in 0 until NUM_LIMBS_255BIT) { + x[index] = (x[index] and nmask) or (t2[index] and mask) + } + } - /** - * Reduces a number modulo 2^255 - 19 where it is known that the - * number can be reduced with only 1 trial subtraction. - * - * @param x The number to reduce, and the result. - */ - private void reduceQuick(int[] x) - { - int index, carry; - - // Perform a trial subtraction of (2^255 - 19) from "x" which is - // equivalent to adding 19 and subtracting 2^255. We add 19 here; - // the subtraction of 2^255 occurs in the next step. - carry = 19; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - t2[index] = carry & 0x03FFFFFF; - carry >>= 26; - } + /** + * Reduce a number modulo 2^255 - 19. + * + * @param result The result. + * @param x The value to be reduced. This array will be + * modified during the reduction. + * @param size The number of limbs in the high order half of x. + */ + private fun reduce(result: IntArray, x: IntArray, size: Int) { + // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will + // either produce the answer we want or it will produce a + // value of the form "answer + j * (2^255 - 19)". There are + // 5 left-over bits in the top-most limb of the bottom half. + var carry = 0 + var limb = x[NUM_LIMBS_255BIT - 1] shr 21 + x[NUM_LIMBS_255BIT - 1] = x[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + for (index in 0 until size) { + limb += x[NUM_LIMBS_255BIT + index] shl 5 + carry += (limb and 0x03FFFFFF) * 19 + x[index] + x[index] = carry and 0x03FFFFFF + limb = limb shr 26 + carry = carry shr 26 + } + if (size < NUM_LIMBS_255BIT) { + // The high order half of the number is short; e.g. for mulA24(). + // Propagate the carry through the rest of the low order part. + for (index in size until NUM_LIMBS_255BIT) { + carry += x[index] + x[index] = carry and 0x03FFFFFF + carry = carry shr 26 + } + } - // If there was a borrow, then the original "x" is the correct answer. - // If there was no borrow, then "t2" is the correct answer. Select the - // correct answer but do it in a way that instruction timing will not - // reveal which value was selected. Borrow will occur if bit 21 of - // "t2" is zero. Turn the bit into a selection mask. - int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01); - int nmask = ~mask; - t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) - x[index] = (x[index] & nmask) | (t2[index] & mask); - } + // The "j" value may still be too large due to the final carry-out. + // We must repeat the reduction. If we already have the answer, + // then this won't do any harm but we must still do the calculation + // to preserve the overall timing. The "j" value will be between + // 0 and 19, which means that the carry we care about is in the + // top 5 bits of the highest limb of the bottom half. + carry = (x[NUM_LIMBS_255BIT - 1] shr 21) * 19 + x[NUM_LIMBS_255BIT - 1] = x[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + for (index in 0 until NUM_LIMBS_255BIT) { + carry += x[index] + result[index] = carry and 0x03FFFFFF + carry = carry shr 26 + } - /** - * Reduce a number modulo 2^255 - 19. - * - * @param result The result. - * @param x The value to be reduced. This array will be - * modified during the reduction. - * @param size The number of limbs in the high order half of x. - */ - private void reduce(int[] result, int[] x, int size) - { - int index, limb, carry; - - // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will - // either produce the answer we want or it will produce a - // value of the form "answer + j * (2^255 - 19)". There are - // 5 left-over bits in the top-most limb of the bottom half. - carry = 0; - limb = x[NUM_LIMBS_255BIT - 1] >> 21; - x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (index = 0; index < size; ++index) { - limb += x[NUM_LIMBS_255BIT + index] << 5; - carry += (limb & 0x03FFFFFF) * 19 + x[index]; - x[index] = carry & 0x03FFFFFF; - limb >>= 26; - carry >>= 26; - } - if (size < NUM_LIMBS_255BIT) { - // The high order half of the number is short; e.g. for mulA24(). - // Propagate the carry through the rest of the low order part. - for (index = size; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - x[index] = carry & 0x03FFFFFF; - carry >>= 26; - } - } + // At this point "x" will either be the answer or it will be the + // answer plus (2^255 - 19). Perform a trial subtraction to + // complete the reduction process. + reduceQuick(result) + } - // The "j" value may still be too large due to the final carry-out. - // We must repeat the reduction. If we already have the answer, - // then this won't do any harm but we must still do the calculation - // to preserve the overall timing. The "j" value will be between - // 0 and 19, which means that the carry we care about is in the - // top 5 bits of the highest limb of the bottom half. - carry = (x[NUM_LIMBS_255BIT - 1] >> 21) * 19; - x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += x[index]; - result[index] = carry & 0x03FFFFFF; - carry >>= 26; - } + /** + * Multiplies two numbers modulo 2^255 - 19. + * + * @param result The result. + * @param x The first number to multiply. + * @param y The second number to multiply. + */ + private fun mul(result: IntArray, x: IntArray, y: IntArray) { + // Multiply the two numbers to create the intermediate result. + var v = x[0].toLong() + for (i in 0 until NUM_LIMBS_255BIT) { + t1[i] = v * y[i] + } + for (i in 1 until NUM_LIMBS_255BIT) { + v = x[i].toLong() + for (j in 0 until (NUM_LIMBS_255BIT - 1)) { + t1[i + j] += v * y[j] + } + t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1] + } - // At this point "x" will either be the answer or it will be the - // answer plus (2^255 - 19). Perform a trial subtraction to - // complete the reduction process. - reduceQuick(result); - } + // Propagate carries and convert back into 26-bit words. + v = t1[0] + t2[0] = v.toInt() and 0x03FFFFFF + for (i in 1 until NUM_LIMBS_510BIT) { + v = (v shr 26) + t1[i] + t2[i] = v.toInt() and 0x03FFFFFF + } - /** - * Multiplies two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to multiply. - * @param y The second number to multiply. - */ - private void mul(int[] result, int[] x, int[] y) - { - int i, j; - - // Multiply the two numbers to create the intermediate result. - long v = x[0]; - for (i = 0; i < NUM_LIMBS_255BIT; ++i) { - t1[i] = v * y[i]; - } - for (i = 1; i < NUM_LIMBS_255BIT; ++i) { - v = x[i]; - for (j = 0; j < (NUM_LIMBS_255BIT - 1); ++j) { - t1[i + j] += v * y[j]; - } - t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1]; - } - - // Propagate carries and convert back into 26-bit words. - v = t1[0]; - t2[0] = ((int)v) & 0x03FFFFFF; - for (i = 1; i < NUM_LIMBS_510BIT; ++i) { - v = (v >> 26) + t1[i]; - t2[i] = ((int)v) & 0x03FFFFFF; - } - - // Reduce the result modulo 2^255 - 19. - reduce(result, t2, NUM_LIMBS_255BIT); - } - - /** - * Squares a number modulo 2^255 - 19. - * - * @param result The result. - * @param x The number to square. - */ - private void square(int[] result, int[] x) - { - mul(result, x, x); - } + // Reduce the result modulo 2^255 - 19. + reduce(result, t2, NUM_LIMBS_255BIT) + } - /** - * Multiplies a number by the a24 constant, modulo 2^255 - 19. - * - * @param result The result. - * @param x The number to multiply by a24. - */ - private void mulA24(int[] result, int[] x) - { - long a24 = 121665; - long carry = 0; - int index; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - carry += a24 * x[index]; - t2[index] = ((int)carry) & 0x03FFFFFF; - carry >>= 26; - } - t2[NUM_LIMBS_255BIT] = ((int)carry) & 0x03FFFFFF; - reduce(result, t2, 1); - } + /** + * Squares a number modulo 2^255 - 19. + * + * @param result The result. + * @param x The number to square. + */ + private fun square(result: IntArray, x: IntArray) { + mul(result, x, x) + } - /** - * Adds two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to add. - * @param y The second number to add. - */ - private void add(int[] result, int[] x, int[] y) - { - int index, carry; - carry = x[0] + y[0]; - result[0] = carry & 0x03FFFFFF; - for (index = 1; index < NUM_LIMBS_255BIT; ++index) { - carry = (carry >> 26) + x[index] + y[index]; - result[index] = carry & 0x03FFFFFF; - } - reduceQuick(result); - } + /** + * Multiplies a number by the a24 constant, modulo 2^255 - 19. + * + * @param result The result. + * @param x The number to multiply by a24. + */ + private fun mulA24(result: IntArray, x: IntArray) { + val a24: Long = 121665 + var carry: Long = 0 + for (index in 0 until NUM_LIMBS_255BIT) { + carry += a24 * x[index] + t2[index] = carry.toInt() and 0x03FFFFFF + carry = carry shr 26 + } + t2[NUM_LIMBS_255BIT] = carry.toInt() and 0x03FFFFFF + reduce(result, t2, 1) + } - /** - * Subtracts two numbers modulo 2^255 - 19. - * - * @param result The result. - * @param x The first number to subtract. - * @param y The second number to subtract. - */ - private void sub(int[] result, int[] x, int[] y) - { - int index, borrow; - - // Subtract y from x to generate the intermediate result. - borrow = 0; - for (index = 0; index < NUM_LIMBS_255BIT; ++index) { - borrow = x[index] - y[index] - ((borrow >> 26) & 0x01); - result[index] = borrow & 0x03FFFFFF; - } + /** + * Adds two numbers modulo 2^255 - 19. + * + * @param result The result. + * @param x The first number to add. + * @param y The second number to add. + */ + private fun add(result: IntArray, x: IntArray, y: IntArray) { + var carry = x[0] + y[0] + result[0] = carry and 0x03FFFFFF + for (index in 1 until NUM_LIMBS_255BIT) { + carry = (carry shr 26) + x[index] + y[index] + result[index] = carry and 0x03FFFFFF + } + reduceQuick(result) + } - // If we had a borrow, then the result has gone negative and we - // have to add 2^255 - 19 to the result to make it positive again. - // The top bits of "borrow" will be all 1's if there is a borrow - // or it will be all 0's if there was no borrow. Easiest is to - // conditionally subtract 19 and then mask off the high bits. - borrow = result[0] - ((-((borrow >> 26) & 0x01)) & 19); - result[0] = borrow & 0x03FFFFFF; - for (index = 1; index < NUM_LIMBS_255BIT; ++index) { - borrow = result[index] - ((borrow >> 26) & 0x01); - result[index] = borrow & 0x03FFFFFF; - } - result[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF; - } - - /** - * Conditional swap of two values. - * - * @param select Set to 1 to swap, 0 to leave as-is. - * @param x The first value. - * @param y The second value. - */ - private static void cswap(int select, int[] x, int[] y) - { - int dummy; - select = -select; - for (int index = 0; index < NUM_LIMBS_255BIT; ++index) { - dummy = select & (x[index] ^ y[index]); - x[index] ^= dummy; - y[index] ^= dummy; - } - } + /** + * Subtracts two numbers modulo 2^255 - 19. + * + * @param result The result. + * @param x The first number to subtract. + * @param y The second number to subtract. + */ + private fun sub(result: IntArray, x: IntArray, y: IntArray) { + // Subtract y from x to generate the intermediate result. + var borrow = 0 + for (index in 0 until NUM_LIMBS_255BIT) { + borrow = x[index] - y[index] - ((borrow shr 26) and 0x01) + result[index] = borrow and 0x03FFFFFF + } - /** - * Raise x to the power of (2^250 - 1). - * - * @param result The result. Must not overlap with x. - * @param x The argument. - */ - private void pow250(int[] result, int[] x) - { - int i, j; - - // The big-endian hexadecimal expansion of (2^250 - 1) is: - // 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF - // - // The naive implementation needs to do 2 multiplications per 1 bit and - // 1 multiplication per 0 bit. We can improve upon this by creating a - // pattern 0000000001 ... 0000000001. If we square and multiply the - // pattern by itself we can turn the pattern into the partial results - // 0000000011 ... 0000000011, 0000000111 ... 0000000111, etc. - // This averages out to about 1.1 multiplications per 1 bit instead of 2. + // If we had a borrow, then the result has gone negative and we + // have to add 2^255 - 19 to the result to make it positive again. + // The top bits of "borrow" will be all 1's if there is a borrow + // or it will be all 0's if there was no borrow. Easiest is to + // conditionally subtract 19 and then mask off the high bits. + borrow = result[0] - ((-((borrow shr 26) and 0x01)) and 19) + result[0] = borrow and 0x03FFFFFF + for (index in 1 until NUM_LIMBS_255BIT) { + borrow = result[index] - ((borrow shr 26) and 0x01) + result[index] = borrow and 0x03FFFFFF + } + result[NUM_LIMBS_255BIT - 1] = result[NUM_LIMBS_255BIT - 1] and 0x001FFFFF + } - // Build a pattern of 250 bits in length of repeated copies of 0000000001. - square(A, x); - for (j = 0; j < 9; ++j) - square(A, A); - mul(result, A, x); - for (i = 0; i < 23; ++i) { - for (j = 0; j < 10; ++j) - square(A, A); - mul(result, result, A); - } + /** + * Raise x to the power of (2^250 - 1). + * + * @param result The result. Must not overlap with x. + * @param x The argument. + */ + private fun pow250(result: IntArray, x: IntArray) { + // The big-endian hexadecimal expansion of (2^250 - 1) is: + // 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF + // + // The naive implementation needs to do 2 multiplications per 1 bit and + // 1 multiplication per 0 bit. We can improve upon this by creating a + // pattern 0000000001 ... 0000000001. If we square and multiply the + // pattern by itself we can turn the pattern into the partial results + // 0000000011 ... 0000000011, 0000000111 ... 0000000111, etc. + // This averages out to about 1.1 multiplications per 1 bit instead of 2. - // Multiply bit-shifted versions of the 0000000001 pattern into - // the result to "fill in" the gaps in the pattern. - square(A, result); - mul(result, result, A); - for (j = 0; j < 8; ++j) { - square(A, A); - mul(result, result, A); - } - } + // Build a pattern of 250 bits in length of repeated copies of 0000000001. + square(A, x) + repeat(9) { + square(A, A) + } + mul(result, A, x) + repeat(23) { + repeat(10) { + square(A, A) + } + mul(result, result, A) + } - /** - * Computes the reciprocal of a number modulo 2^255 - 19. - * - * @param result The result. Must not overlap with x. - * @param x The argument. - */ - private void recip(int[] result, int[] x) - { - // The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19. - // The big-endian hexadecimal expansion of (p - 2) is: - // 7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEB - // Start with the 250 upper bits of the expansion of (p - 2). - pow250(result, x); + // Multiply bit-shifted versions of the 0000000001 pattern into + // the result to "fill in" the gaps in the pattern. + square(A, result) + mul(result, result, A) + repeat(8) { + square(A, A) + mul(result, result, A) + } + } - // Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest. - square(result, result); - square(result, result); - mul(result, result, x); - square(result, result); - square(result, result); - mul(result, result, x); - square(result, result); - mul(result, result, x); - } + /** + * Computes the reciprocal of a number modulo 2^255 - 19. + * + * @param result The result. Must not overlap with x. + * @param x The argument. + */ + private fun recip(result: IntArray, x: IntArray) { + // The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19. + // Start with the 250 upper bits of the expansion of (p - 2). + pow250(result, x) - /** - * Evaluates the curve for every bit in a secret key. - * - * @param s The 32-byte secret key. - */ - private void evalCurve(byte[] s) - { - int sposn = 31; - int sbit = 6; - int svalue = s[sposn] | 0x40; - int swap = 0; - int select; + // Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest. + repeat(2) { square(result, result) } + mul(result, result, x) + repeat(2) { square(result, result) } + mul(result, result, x) + square(result, result) + mul(result, result, x) + } - // Iterate over all 255 bits of "s" from the highest to the lowest. - // We ignore the high bit of the 256-bit representation of "s". - for (;;) { - // Conditional swaps on entry to this bit but only if we - // didn't swap on the previous bit. - select = (svalue >> sbit) & 0x01; - swap ^= select; - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - swap = select; + /** + * Evaluates the curve for every bit in a secret key. + * + * @param s The 32-byte secret key. + */ + private fun evalCurve(s: ByteArray) { + var sposn = 31 + var sbit = 6 + var svalue = s[sposn].toInt() or 0x40 + var swap = 0 - // Evaluate the curve. - add(A, x_2, z_2); // A = x_2 + z_2 - square(AA, A); // AA = A^2 - sub(B, x_2, z_2); // B = x_2 - z_2 - square(BB, B); // BB = B^2 - sub(E, AA, BB); // E = AA - BB - add(C, x_3, z_3); // C = x_3 + z_3 - sub(D, x_3, z_3); // D = x_3 - z_3 - mul(DA, D, A); // DA = D * A - mul(CB, C, B); // CB = C * B - add(x_3, DA, CB); // x_3 = (DA + CB)^2 - square(x_3, x_3); - sub(z_3, DA, CB); // z_3 = x_1 * (DA - CB)^2 - square(z_3, z_3); - mul(z_3, z_3, x_1); - mul(x_2, AA, BB); // x_2 = AA * BB - mulA24(z_2, E); // z_2 = E * (AA + a24 * E) - add(z_2, z_2, AA); - mul(z_2, z_2, E); + // Iterate over all 255 bits of "s" from the highest to the lowest. + // We ignore the high bit of the 256-bit representation of "s". + while (true) { + // Conditional swaps on entry to this bit but only if we + // didn't swap on the previous bit. + val select = (svalue shr sbit) and 0x01 + swap = swap xor select + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + swap = select - // Move onto the next lower bit of "s". - if (sbit > 0) { - --sbit; - } else if (sposn == 0) { - break; - } else if (sposn == 1) { - --sposn; - svalue = s[sposn] & 0xF8; - sbit = 7; - } else { - --sposn; - svalue = s[sposn]; - sbit = 7; - } - } + // Evaluate the curve. + add(A, x_2, z_2) // A = x_2 + z_2 + square(AA, A) // AA = A^2 + sub(B, x_2, z_2) // B = x_2 - z_2 + square(BB, B) // BB = B^2 + sub(E, AA, BB) // E = AA - BB + add(C, x_3, z_3) // C = x_3 + z_3 + sub(D, x_3, z_3) // D = x_3 - z_3 + mul(DA, D, A) // DA = D * A + mul(CB, C, B) // CB = C * B + add(x_3, DA, CB) // x_3 = (DA + CB)^2 + square(x_3, x_3) + sub(z_3, DA, CB) // z_3 = x_1 * (DA - CB)^2 + square(z_3, z_3) + mul(z_3, z_3, x_1) + mul(x_2, AA, BB) // x_2 = AA * BB + mulA24(z_2, E) // z_2 = E * (AA + a24 * E) + add(z_2, z_2, AA) + mul(z_2, z_2, E) - // Final conditional swaps. - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - } + // Move onto the next lower bit of "s". + if (sbit > 0) { + --sbit + } else if (sposn == 0) { + break + } else if (sposn == 1) { + --sposn + svalue = s[sposn].toInt() and 0xF8 + sbit = 7 + } else { + --sposn + svalue = s[sposn].toInt() + sbit = 7 + } + } - /** - * Evaluates the Curve25519 curve. - * - * @param result Buffer to place the result of the evaluation into. - * @param offset Offset into the result buffer. - * @param privateKey The private key to use in the evaluation. - * @param publicKey The public key to use in the evaluation, or null - * if the base point of the curve should be used. - */ - public static void eval(byte[] result, int offset, byte[] privateKey, byte[] publicKey) - { - Curve25519 state = new Curve25519(); - try { - // Unpack the public key value. If null, use 9 as the base point. - Arrays.fill(state.x_1, 0); - if (publicKey != null) { - // Convert the input value from little-endian into 26-bit limbs. - for (int index = 0; index < 32; ++index) { - int bit = (index * 8) % 26; - int word = (index * 8) / 26; - int value = publicKey[index] & 0xFF; - if (bit <= (26 - 8)) { - state.x_1[word] |= value << bit; - } else { - state.x_1[word] |= value << bit; - state.x_1[word] &= 0x03FFFFFF; - state.x_1[word + 1] |= value >> (26 - bit); - } - } + // Final conditional swaps. + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + } - // Just in case, we reduce the number modulo 2^255 - 19 to - // make sure that it is in range of the field before we start. - // This eliminates values between 2^255 - 19 and 2^256 - 1. - state.reduceQuick(state.x_1); - state.reduceQuick(state.x_1); - } else { - state.x_1[0] = 9; - } + companion object { + // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. + private const val NUM_LIMBS_255BIT = 10 + private const val NUM_LIMBS_510BIT = 20 - // Initialize the other temporary variables. - Arrays.fill(state.x_2, 0); // x_2 = 1 - state.x_2[0] = 1; - Arrays.fill(state.z_2, 0); // z_2 = 0 - System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1 - Arrays.fill(state.z_3, 0); // z_3 = 1 - state.z_3[0] = 1; - - // Evaluate the curve for every bit of the private key. - state.evalCurve(privateKey); + /** + * Conditional swap of two values. + * + * @param select Set to 1 to swap, 0 to leave as-is. + * @param x The first value. + * @param y The second value. + */ + private fun cswap(select: Int, x: IntArray, y: IntArray) { + val s = -select + for (index in 0 until NUM_LIMBS_255BIT) { + val dummy = s and (x[index] xor y[index]) + x[index] = x[index] xor dummy + y[index] = y[index] xor dummy + } + } - // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19. - state.recip(state.z_3, state.z_2); - state.mul(state.x_2, state.x_2, state.z_3); + /** + * Evaluates the Curve25519 curve. + * + * @param result Buffer to place the result of the evaluation into. + * @param offset Offset into the result buffer. + * @param privateKey The private key to use in the evaluation. + * @param publicKey The public key to use in the evaluation, or null + * if the base point of the curve should be used. + */ + @JvmStatic + fun eval(result: ByteArray, offset: Int, privateKey: ByteArray, publicKey: ByteArray?) { + val state = Curve25519() + try { + // Unpack the public key value. If null, use 9 as the base point. + state.x_1.fill(0) + if (publicKey != null) { + // Convert the input value from little-endian into 26-bit limbs. + for (index in 0 until 32) { + val bit = (index * 8) % 26 + val word = (index * 8) / 26 + val value = publicKey[index].toInt() and 0xFF + if (bit <= (26 - 8)) { + state.x_1[word] = state.x_1[word] or (value shl bit) + } else { + state.x_1[word] = state.x_1[word] or (value shl bit) + state.x_1[word] = state.x_1[word] and 0x03FFFFFF + state.x_1[word + 1] = state.x_1[word + 1] or (value shr (26 - bit)) + } + } - // Convert x_2 into little-endian in the result buffer. - for (int index = 0; index < 32; ++index) { - int bit = (index * 8) % 26; - int word = (index * 8) / 26; - if (bit <= (26 - 8)) - result[offset + index] = (byte)(state.x_2[word] >> bit); - else - result[offset + index] = (byte)((state.x_2[word] >> bit) | (state.x_2[word + 1] << (26 - bit))); - } - } finally { - // Clean up all temporary state before we exit. - state.destroy(); - } - } + // Just in case, we reduce the number modulo 2^255 - 19 to + // make sure that it is in range of the field before we start. + // This eliminates values between 2^255 - 19 and 2^256 - 1. + state.reduceQuick(state.x_1) + state.reduceQuick(state.x_1) + } else { + state.x_1[0] = 9 + } + + // Initialize the other temporary variables. + state.x_2.fill(0) // x_2 = 1 + state.x_2[0] = 1 + state.z_2.fill(0) // z_2 = 0 + state.x_1.copyInto(state.x_3) // x_3 = x_1 + state.z_3.fill(0) // z_3 = 1 + state.z_3[0] = 1 + + + // Evaluate the curve for every bit of the private key. + state.evalCurve(privateKey) + + // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19. + state.recip(state.z_3, state.z_2) + state.mul(state.x_2, state.x_2, state.z_3) + + // Convert x_2 into little-endian in the result buffer. + for (index in 0 until 32) { + val bit = (index * 8) % 26 + val word = (index * 8) / 26 + if (bit <= (26 - 8)) { + result[offset + index] = (state.x_2[word] shr bit).toByte() + } else { + result[offset + index] = + ((state.x_2[word] shr bit) or (state.x_2[word + 1] shl (26 - bit))).toByte() + } + } + } finally { + // Clean up all temporary state before we exit. + state.destroy() + } + } + } } diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt index 715b8549..4dfbe3ea 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/crypto/Curve448.kt @@ -19,7 +19,6 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ - /* Portions of this code were extracted from the p448/arch_32 field @@ -52,360 +51,128 @@ THE SOFTWARE. ---- */ +package com.bitchat.android.noise.southernstorm.crypto -package com.bitchat.android.noise.southernstorm.crypto; - -import java.util.Arrays; /** * Implementation of the Curve448 elliptic curve algorithm. * * Reference: RFC 7748 */ -public final class Curve448 { +class Curve448 private constructor() { + // Numbers modulo 2^448 - 2^224 - 1 are broken up into sixteen 28-bit words. + private val x_1 = IntArray(16) + private val x_2 = IntArray(16) + private val x_3 = IntArray(16) + private val z_2 = IntArray(16) + private val z_3 = IntArray(16) + private val A = IntArray(16) + private val B = IntArray(16) + private val C = IntArray(16) + private val D = IntArray(16) + private val E = IntArray(16) + private val AA = IntArray(16) + private val BB = IntArray(16) + private val DA = IntArray(16) + private val CB = IntArray(16) + private val aa = IntArray(8) + private val bb = IntArray(8) - // Numbers modulo 2^448 - 2^224 - 1 are broken up into sixteen 28-bit words. - private int[] x_1; - private int[] x_2; - private int[] x_3; - private int[] z_2; - private int[] z_3; - private int[] A; - private int[] B; - private int[] C; - private int[] D; - private int[] E; - private int[] AA; - private int[] BB; - private int[] DA; - private int[] CB; - private int[] aa; - private int[] bb; - /** - * Constructs the temporary state holder for Curve448 evaluation. - */ - private Curve448() - { - // Allocate memory for all of the temporary variables we will need. - x_1 = new int [16]; - x_2 = new int [16]; - x_3 = new int [16]; - z_2 = new int [16]; - z_3 = new int [16]; - A = new int [16]; - B = new int [16]; - C = new int [16]; - D = new int [16]; - E = new int [16]; - AA = new int [16]; - BB = new int [16]; - DA = new int [16]; - CB = new int [16]; - aa = new int [8]; - bb = new int [8]; - } + /** + * Destroy all sensitive data in this object. + */ + private fun destroy() { + // Destroy all temporary variables. + x_1.fill(0) + x_2.fill(0) + x_3.fill(0) + z_2.fill(0) + z_3.fill(0) + A.fill(0) + B.fill(0) + C.fill(0) + D.fill(0) + E.fill(0) + AA.fill(0) + BB.fill(0) + DA.fill(0) + CB.fill(0) + aa.fill(0) + bb.fill(0) + } - /** - * Destroy all sensitive data in this object. - */ - private void destroy() { - // Destroy all temporary variables. - Arrays.fill(x_1, 0); - Arrays.fill(x_2, 0); - Arrays.fill(x_3, 0); - Arrays.fill(z_2, 0); - Arrays.fill(z_3, 0); - Arrays.fill(A, 0); - Arrays.fill(B, 0); - Arrays.fill(C, 0); - Arrays.fill(D, 0); - Arrays.fill(E, 0); - Arrays.fill(AA, 0); - Arrays.fill(BB, 0); - Arrays.fill(DA, 0); - Arrays.fill(CB, 0); - Arrays.fill(aa, 0); - Arrays.fill(bb, 0); - } + // p448_mul() + private fun mul(c: IntArray, a: IntArray, b: IntArray) { + var accum0 = 0L + var accum1 = 0L + val mask = (1 shl 28) - 1 - /* Beginning of code imported from Ed448-Goldilocks */ - - private static long widemul_32(int a, int b) - { - return ((long)a) * b; - } + for (i in 0 until 8) { + aa[i] = a[i] + a[i + 8] + bb[i] = b[i] + b[i + 8] + } - // p448_mul() - private void mul(int[] c, int[] a, int[] b) - { - long accum0 = 0, accum1 = 0, accum2 = 0; - int mask = (1<<28) - 1; + for (j in 0 until 8) { + var accum2 = 0L - int i,j; - for (i=0; i<8; i++) { - aa[i] = a[i] + a[i+8]; - bb[i] = b[i] + b[i+8]; - } + for (i in 0..j) { + accum2 += widemul_32(a[j - i], b[i]) + accum1 += widemul_32(aa[j - i], bb[i]) + accum0 += widemul_32(a[8 + j - i], b[8 + i]) + } - for (j=0; j<8; j++) { - accum2 = 0; + accum1 -= accum2 + accum0 += accum2 + accum2 = 0 - for (i=0; i<=j; i++) { - accum2 += widemul_32(a[j-i],b[i]); - accum1 += widemul_32(aa[j-i],bb[i]); - accum0 += widemul_32(a[8+j-i], b[8+i]); - } + for (i in (j + 1) until 8) { + accum0 -= widemul_32(a[8 + j - i], b[i]) + accum2 += widemul_32(aa[8 + j - i], bb[i]) + accum1 += widemul_32(a[16 + j - i], b[8 + i]) + } - accum1 -= accum2; - accum0 += accum2; - accum2 = 0; + accum1 += accum2 + accum0 += accum2 - for (; i<8; i++) { - accum0 -= widemul_32(a[8+j-i], b[i]); - accum2 += widemul_32(aa[8+j-i], bb[i]); - accum1 += widemul_32(a[16+j-i], b[8+i]); - } + c[j] = accum0.toInt() and mask + c[j + 8] = accum1.toInt() and mask - accum1 += accum2; - accum0 += accum2; + accum0 = accum0 ushr 28 + accum1 = accum1 ushr 28 + } - c[j] = ((int)(accum0)) & mask; - c[j+8] = ((int)(accum1)) & mask; + accum0 += accum1 + accum0 += c[8].toLong() + accum1 += c[0].toLong() + c[8] = accum0.toInt() and mask + c[0] = accum1.toInt() and mask - accum0 >>>= 28; - accum1 >>>= 28; - } + accum0 = accum0 ushr 28 + accum1 = accum1 ushr 28 + c[9] += accum0.toInt() + c[1] += accum1.toInt() + } - accum0 += accum1; - accum0 += c[8]; - accum1 += c[0]; - c[8] = ((int)(accum0)) & mask; - c[0] = ((int)(accum1)) & mask; + /* End of code imported from Ed448-Goldilocks */ + /** + * Squares a number modulo 2^448 - 2^224 - 1. + * + * @param result The result. + * @param x The number to square. + */ + private fun square(result: IntArray, x: IntArray) { + mul(result, x, x) + } - accum0 >>>= 28; - accum1 >>>= 28; - c[9] += ((int)(accum0)); - c[1] += ((int)(accum1)); - } - - // p448_mulw() - private static void mulw(int[] c, int[] a, long b) - { - int bhi = (int)(b>>28), blo = ((int)b) & ((1<<28)-1); - - long accum0, accum8; - int mask = (1<<28) - 1; - - int i; - - accum0 = widemul_32(blo, a[0]); - accum8 = widemul_32(blo, a[8]); - accum0 += widemul_32(bhi, a[15]); - accum8 += widemul_32(bhi, a[15] + a[7]); - - c[0] = ((int)accum0) & mask; accum0 >>>= 28; - c[8] = ((int)accum8) & mask; accum8 >>>= 28; - - for (i=1; i<8; i++) { - accum0 += widemul_32(blo, a[i]); - accum8 += widemul_32(blo, a[i+8]); - - accum0 += widemul_32(bhi, a[i-1]); - accum8 += widemul_32(bhi, a[i+7]); - - c[i] = ((int)accum0) & mask; accum0 >>>= 28; - c[i+8] = ((int)accum8) & mask; accum8 >>>= 28; - } - - accum0 += accum8 + c[8]; - c[8] = ((int)accum0) & mask; - c[9] += accum0 >>> 28; - - accum8 += c[0]; - c[0] = ((int)accum8) & mask; - c[1] += accum8 >>> 28; - } - - // p448_weak_reduce - private static void weak_reduce(int[] a) - { - int mask = (1<<28) - 1; - int tmp = a[15] >>> 28; - int i; - a[8] += tmp; - for (i=15; i>0; i--) { - a[i] = (a[i] & mask) + (a[i-1]>>>28); - } - a[0] = (a[0] & mask) + tmp; - } - - // p448_strong_reduce - private static void strong_reduce(int[] a) - { - int mask = (1<<28) - 1; - - /* first, clear high */ - a[8] += a[15]>>>28; - a[0] += a[15]>>>28; - a[15] &= mask; - - /* now the total is less than 2^448 - 2^(448-56) + 2^(448-56+8) < 2p */ - - /* compute total_value - p. No need to reduce mod p. */ - - long scarry = 0; - int i; - for (i=0; i<16; i++) { - scarry = scarry + (a[i] & 0xFFFFFFFFL) - ((i==8)?mask-1:mask); - a[i] = (int)(scarry & mask); - scarry >>= 28; - } - - /* uncommon case: it was >= p, so now scarry = 0 and this = x - * common case: it was < p, so now scarry = -1 and this = x - p + 2^448 - * so let's add back in p. will carry back off the top for 2^448. - */ - - int scarry_mask = (int)(scarry & mask); - long carry = 0; - - /* add it back */ - for (i=0; i<16; i++) { - carry = carry + (a[i] & 0xFFFFFFFFL) + ((i==8)?(scarry_mask&~1):scarry_mask); - a[i] = (int)(carry & mask); - carry >>>= 28; - } - } - - // field_add() - private static void add(int[] out, int[] a, int[] b) - { - for (int i = 0; i < 16; ++i) - out[i] = a[i] + b[i]; - weak_reduce(out); - } - - // field_sub() - private static void sub(int[] out, int[] a, int[] b) - { - int i; - - // p448_sub_RAW(out, a, b) - for (i = 0; i < 16; ++i) - out[i] = a[i] - b[i]; - - // p448_bias(out, 2) - int co1 = ((1 << 28) - 1) * 2; - int co2 = co1 - 2; - for (i = 0; i < 16; ++i) { - if (i != 8) - out[i] += co1; - else - out[i] += co2; - } - - weak_reduce(out); - } - - // p448_serialize() - private static void serialize(byte[] serial, int offset, int[] x) - { - int i,j; - for (i=0; i<8; i++) { - long limb = x[2*i] + (((long)x[2*i+1])<<28); - for (j=0; j<7; j++) { - serial[offset+7*i+j] = (byte)limb; - limb >>= 8; - } - } - } - - private static int is_zero(int x) - { - long xx = x & 0xFFFFFFFFL; - xx--; - return (int)(xx >> 32); - } - - // p448_deserialize() - private static int deserialize(int[] x, byte[] serial, int offset) - { - int i,j; - for (i=0; i<8; i++) { - long out = 0; - for (j=0; j<7; j++) { - out |= (serial[offset+7*i+j] & 0xFFL)<<(8*j); - } - x[2*i] = ((int)out) & ((1<<28)-1); - x[2*i+1] = (int)(out >>> 28); - } - - /* Check for reduction. - * - * The idea is to create a variable ge which is all ones (rather, 56 ones) - * if and only if the low $i$ words of $x$ are >= those of p. - * - * Remember p = little_endian(1111,1111,1111,1111,1110,1111,1111,1111) - */ - int ge = -1, mask = (1<<28)-1; - for (i=0; i<8; i++) { - ge &= x[i]; - } - - /* At this point, ge = 1111 iff bottom are all 1111. Now propagate if 1110, or set if 1111 */ - ge = (ge & (x[8] + 1)) | is_zero(x[8] ^ mask); - - /* Propagate the rest */ - for (i=9; i<16; i++) { - ge &= x[i]; - } - - return ~is_zero(ge ^ mask); - } - - /* End of code imported from Ed448-Goldilocks */ - - /** - * Squares a number modulo 2^448 - 2^224 - 1. - * - * @param result The result. - * @param x The number to square. - */ - private void square(int[] result, int[] x) - { - mul(result, x, x); - } - - /** - * Conditional swap of two values. - * - * @param select Set to 1 to swap, 0 to leave as-is. - * @param x The first value. - * @param y The second value. - */ - private static void cswap(int select, int[] x, int[] y) - { - int dummy; - select = -select; - for (int index = 0; index < 16; ++index) { - dummy = select & (x[index] ^ y[index]); - x[index] ^= dummy; - y[index] ^= dummy; - } - } - - /** - * Computes the reciprocal of a number modulo 2^448 - 2^224 - 1. - * - * @param result The result. Must not overlap with z_2. - * @param z_2 The argument. - */ - private void recip(int[] result, int[] z_2) - { - int posn; - - /* Compute z_2 ^ (p - 2) + /** + * Computes the reciprocal of a number modulo 2^448 - 2^224 - 1. + * + * @param result The result. Must not overlap with z_2. + * @param z_2 The argument. + */ + private fun recip(result: IntArray, z_2: IntArray) { + /* Compute z_2 ^ (p - 2) The value p - 2 is: FF...FEFF...FD, which from highest to lowest is 223 one bits, followed by a zero bit, followed by 222 one bits, @@ -419,204 +186,382 @@ public final class Curve448 { 11110000 and then multiply by the 1111 pattern to get 11111111. We then repeat that to turn 11111111 into 1111111111111111, etc. */ - square(B, z_2); /* Set A to a 4 bit pattern */ - mul(A, B, z_2); - square(B, A); - mul(A, B, z_2); - square(B, A); - mul(A, B, z_2); - square(B, A); /* Set C to a 6 bit pattern */ - mul(C, B, z_2); - square(B, C); - mul(C, B, z_2); - square(B, C); /* Set A to a 8 bit pattern */ - mul(A, B, z_2); - square(B, A); - mul(A, B, z_2); - square(E, A); /* Set E to a 16 bit pattern */ - square(B, E); - for (posn = 1; posn < 4; ++posn) { - square(E, B); - square(B, E); - } - mul(E, B, A); - square(AA, E); /* Set AA to a 32 bit pattern */ - square(B, AA); - for (posn = 1; posn < 8; ++posn) { - square(AA, B); - square(B, AA); - } - mul(AA, B, E); - square(BB, AA); /* Set BB to a 64 bit pattern */ - square(B, BB); - for (posn = 1; posn < 16; ++posn) { - square(BB, B); - square(B, BB); - } - mul(BB, B, AA); - square(DA, BB); /* Set DA to a 128 bit pattern */ - square(B, DA); - for (posn = 1; posn < 32; ++posn) { - square(DA, B); - square(B, DA); - } - mul(DA, B, BB); - square(CB, DA); /* Set CB to a 192 bit pattern */ - square(B, CB); /* 192 = 128 + 64 */ - for (posn = 1; posn < 32; ++posn) { - square(CB, B); - square(B, CB); - } - mul(CB, B, BB); - square(DA, CB); /* Set DA to a 208 bit pattern */ - square(B, DA); /* 208 = 128 + 64 + 16 */ - for (posn = 1; posn < 8; ++posn) { - square(DA, B); - square(B, DA); - } - mul(DA, B, E); - square(CB, DA); /* Set CB to a 216 bit pattern */ - square(B, CB); /* 216 = 128 + 64 + 16 + 8 */ - for (posn = 1; posn < 4; ++posn) { - square(CB, B); - square(B, CB); - } - mul(CB, B, A); - square(DA, CB); /* Set DA to a 222 bit pattern */ - square(B, DA); /* 222 = 128 + 64 + 16 + 8 + 6 */ - for (posn = 1; posn < 3; ++posn) { - square(DA, B); - square(B, DA); - } - mul(DA, B, C); - square(CB, DA); /* Set CB to a 224 bit pattern */ - mul(B, CB, z_2); /* CB = DA|1|0 */ - square(CB, B); - square(BB, CB); /* Set BB to a 446 bit pattern */ - square(B, BB); /* BB = DA|1|0|DA */ - for (posn = 1; posn < 111; ++posn) { - square(BB, B); - square(B, BB); - } - mul(BB, B, DA); - square(B, BB); /* Set result to a 448 bit pattern */ - square(BB, B); /* result = DA|1|0|DA|01 */ - mul(result, BB, z_2); - } + square(B, z_2) /* Set A to a 4 bit pattern */ + mul(A, B, z_2) + square(B, A) + mul(A, B, z_2) + square(B, A) + mul(A, B, z_2) + square(B, A) /* Set C to a 6 bit pattern */ + mul(C, B, z_2) + square(B, C) + mul(C, B, z_2) + square(B, C) /* Set A to a 8 bit pattern */ + mul(A, B, z_2) + square(B, A) + mul(A, B, z_2) + square(E, A) /* Set E to a 16 bit pattern */ + square(B, E) + repeat(3) { + square(E, B) + square(B, E) + } + mul(E, B, A) + square(AA, E) /* Set AA to a 32 bit pattern */ + square(B, AA) + repeat(7) { + square(AA, B) + square(B, AA) + } + mul(AA, B, E) + square(BB, AA) /* Set BB to a 64 bit pattern */ + square(B, BB) + repeat(15) { + square(BB, B) + square(B, BB) + } + mul(BB, B, AA) + square(DA, BB) /* Set DA to a 128 bit pattern */ + square(B, DA) + repeat(31) { + square(DA, B) + square(B, DA) + } + mul(DA, B, BB) + square(CB, DA) /* Set CB to a 192 bit pattern */ + square(B, CB) /* 192 = 128 + 64 */ + repeat(31) { + square(CB, B) + square(B, CB) + } + mul(CB, B, BB) + square(DA, CB) /* Set DA to a 208 bit pattern */ + square(B, DA) /* 208 = 128 + 64 + 16 */ + repeat(7) { + square(DA, B) + square(B, DA) + } + mul(DA, B, E) + square(CB, DA) /* Set CB to a 216 bit pattern */ + square(B, CB) /* 216 = 128 + 64 + 16 + 8 */ + repeat(3) { + square(CB, B) + square(B, CB) + } + mul(CB, B, A) + square(DA, CB) /* Set DA to a 222 bit pattern */ + square(B, DA) /* 222 = 128 + 64 + 16 + 8 + 6 */ + repeat(2) { + square(DA, B) + square(B, DA) + } + mul(DA, B, C) + square(CB, DA) /* Set CB to a 224 bit pattern */ + mul(B, CB, z_2) /* CB = DA|1|0 */ + square(CB, B) + square(BB, CB) /* Set BB to a 446 bit pattern */ + square(B, BB) /* BB = DA|1|0|DA */ + repeat(110) { + square(BB, B) + square(B, BB) + } + mul(BB, B, DA) + square(B, BB) /* Set result to a 448 bit pattern */ + square(BB, B) /* result = DA|1|0|DA|01 */ + mul(result, BB, z_2) + } - /** - * Evaluates the curve for every bit in a secret key. - * - * @param s The 56-byte secret key. - */ - private void evalCurve(byte[] s) - { - int sposn = 55; - int sbit = 7; - int svalue = s[sposn] | 0x80; - int swap = 0; - int select; + /** + * Evaluates the curve for every bit in a secret key. + * + * @param s The 56-byte secret key. + */ + private fun evalCurve(s: ByteArray) { + var sposn = 55 + var sbit = 7 + var svalue = s[sposn].toInt() or 0x80 + var swap = 0 - // Iterate over all 448 bits of "s" from the highest to the lowest. - for (;;) { - // Conditional swaps on entry to this bit but only if we - // didn't swap on the previous bit. - select = (svalue >> sbit) & 0x01; - swap ^= select; - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - swap = select; + // Iterate over all 448 bits of "s" from the highest to the lowest. + while (true) { + // Conditional swaps on entry to this bit but only if we + // didn't swap on the previous bit. + val select = (svalue shr sbit) and 0x01 + swap = swap xor select + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + swap = select - // Evaluate the curve. - add(A, x_2, z_2); // A = x_2 + z_2 - square(AA, A); // AA = A^2 - sub(B, x_2, z_2); // B = x_2 - z_2 - square(BB, B); // BB = B^2 - sub(E, AA, BB); // E = AA - BB - add(C, x_3, z_3); // C = x_3 + z_3 - sub(D, x_3, z_3); // D = x_3 - z_3 - mul(DA, D, A); // DA = D * A - mul(CB, C, B); // CB = C * B - add(z_2, DA, CB); // x_3 = (DA + CB)^2 - square(x_3, z_2); - sub(z_2, DA, CB); // z_3 = x_1 * (DA - CB)^2 - square(x_2, z_2); - mul(z_3, x_1, x_2); - mul(x_2, AA, BB); // x_2 = AA * BB - mulw(z_2, E, 39081); // z_2 = E * (AA + a24 * E) - add(A, AA, z_2); - mul(z_2, E, A); + // Evaluate the curve. + add(A, x_2, z_2) // A = x_2 + z_2 + square(AA, A) // AA = A^2 + sub(B, x_2, z_2) // B = x_2 - z_2 + square(BB, B) // BB = B^2 + sub(E, AA, BB) // E = AA - BB + add(C, x_3, z_3) // C = x_3 + z_3 + sub(D, x_3, z_3) // D = x_3 - z_3 + mul(DA, D, A) // DA = D * A + mul(CB, C, B) // CB = C * B + add(z_2, DA, CB) // x_3 = (DA + CB)^2 + square(x_3, z_2) + sub(z_2, DA, CB) // z_3 = x_1 * (DA - CB)^2 + square(x_2, z_2) + mul(z_3, x_1, x_2) + mul(x_2, AA, BB) // x_2 = AA * BB + mulw(z_2, E, 39081) // z_2 = E * (AA + a24 * E) + add(A, AA, z_2) + mul(z_2, E, A) - // Move onto the next lower bit of "s". - if (sbit > 0) { - --sbit; - } else if (sposn == 0) { - break; - } else if (sposn == 1) { - --sposn; - svalue = s[sposn] & 0xFC; - sbit = 7; - } else { - --sposn; - svalue = s[sposn]; - sbit = 7; - } - } + // Move onto the next lower bit of "s". + if (sbit > 0) { + --sbit + } else if (sposn == 0) { + break + } else if (sposn == 1) { + --sposn + svalue = s[sposn].toInt() and 0xFC + sbit = 7 + } else { + --sposn + svalue = s[sposn].toInt() + sbit = 7 + } + } - // Final conditional swaps. - cswap(swap, x_2, x_3); - cswap(swap, z_2, z_3); - } + // Final conditional swaps. + cswap(swap, x_2, x_3) + cswap(swap, z_2, z_3) + } - /** - * Evaluates the Curve448 curve. - * - * @param result Buffer to place the result of the evaluation into. - * @param offset Offset into the result buffer. - * @param privateKey The private key to use in the evaluation. - * @param publicKey The public key to use in the evaluation, or null - * if the base point of the curve should be used. - * @return Returns true if the curve evaluation was successful, - * false if the publicKey value is out of range. - */ - public static boolean eval(byte[] result, int offset, byte[] privateKey, byte[] publicKey) - { - Curve448 state = new Curve448(); - int success = -1; - try { - // Unpack the public key value. If null, use 5 as the base point. - Arrays.fill(state.x_1, 0); - if (publicKey != null) { - // Convert the input value from little-endian into 28-bit limbs. - // It is possible that the public key is out of range. If so, - // delay reporting that state until the function completes. - success = deserialize(state.x_1, publicKey, 0); - } else { - state.x_1[0] = 5; - } + companion object { + /* Beginning of code imported from Ed448-Goldilocks */ + private fun widemul_32(a: Int, b: Int): Long = (a.toLong()) * b - // Initialize the other temporary variables. - Arrays.fill(state.x_2, 0); // x_2 = 1 - state.x_2[0] = 1; - Arrays.fill(state.z_2, 0); // z_2 = 0 - System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1 - Arrays.fill(state.z_3, 0); // z_3 = 1 - state.z_3[0] = 1; - - // Evaluate the curve for every bit of the private key. - state.evalCurve(privateKey); + // p448_mulw() + private fun mulw(c: IntArray, a: IntArray, b: Long) { + val bhi = (b shr 28).toInt() + val blo = b.toInt() and ((1 shl 28) - 1) - // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^448 - 2^224 - 1. - state.recip(state.z_3, state.z_2); - state.mul(state.x_1, state.x_2, state.z_3); + var accum0 = widemul_32(blo, a[0]) + var accum8 = widemul_32(blo, a[8]) + accum0 += widemul_32(bhi, a[15]) + accum8 += widemul_32(bhi, a[15] + a[7]) - // Convert x_2 into little-endian in the result buffer. - strong_reduce(state.x_1); - serialize(result, offset, state.x_1); - } finally { - // Clean up all temporary state before we exit. - state.destroy(); - } - return (success & 0x01) != 0; - } + val mask = (1 shl 28) - 1 + + c[0] = accum0.toInt() and mask + accum0 = accum0 ushr 28 + c[8] = accum8.toInt() and mask + accum8 = accum8 ushr 28 + + for (i in 1 until 8) { + accum0 += widemul_32(blo, a[i]) + accum8 += widemul_32(blo, a[i + 8]) + + accum0 += widemul_32(bhi, a[i - 1]) + accum8 += widemul_32(bhi, a[i + 7]) + + c[i] = accum0.toInt() and mask + accum0 = accum0 ushr 28 + c[i + 8] = accum8.toInt() and mask + accum8 = accum8 ushr 28 + } + + accum0 += accum8 + c[8] + c[8] = accum0.toInt() and mask + c[9] += (accum0 ushr 28).toInt() + + accum8 += c[0].toLong() + c[0] = accum8.toInt() and mask + c[1] += (accum8 ushr 28).toInt() + } + + + // p448_weak_reduce + private fun weak_reduce(a: IntArray) { + val mask = (1 shl 28) - 1 + val tmp = a[15] ushr 28 + a[8] += tmp + for (i in 15 downTo 1) { + a[i] = (a[i] and mask) + (a[i - 1] ushr 28) + } + a[0] = (a[0] and mask) + tmp + } + + // p448_strong_reduce + private fun strong_reduce(a: IntArray) { + val mask = (1 shl 28) - 1 + + /* first, clear high */ + a[8] += a[15] ushr 28 + a[0] += a[15] ushr 28 + a[15] = a[15] and mask + + var scarry = 0L + for (i in 0 until 16) { + scarry = scarry + (a[i].toLong() and 0xFFFFFFFFL) - (if (i == 8) mask - 1 else mask) + a[i] = (scarry and mask.toLong()).toInt() + scarry = scarry shr 28 + } + + val scarryMask = (scarry and mask.toLong()).toInt() + var carry = 0L + + for (i in 0 until 16) { + carry += (a[i].toLong() and 0xFFFFFFFFL) + (if (i == 8) scarryMask and 1.inv() else scarryMask) + a[i] = (carry and mask.toLong()).toInt() + carry = carry ushr 28 + } + } + + + // field_add() + private fun add(out: IntArray, a: IntArray, b: IntArray) { + for (i in 0..15) out[i] = a[i] + b[i] + weak_reduce(out) + } + + // field_sub() + private fun sub(out: IntArray, a: IntArray, b: IntArray) { + for (i in 0 until 16) { + out[i] = a[i] - b[i] + } + + val co1 = ((1 shl 28) - 1) * 2 + val co2 = co1 - 2 + for (i in 0 until 16) { + if (i != 8) out[i] += co1 else out[i] += co2 + } + + weak_reduce(out) + } + + // p448_serialize() + private fun serialize(serial: ByteArray, offset: Int, x: IntArray) { + for (i in 0 until 8) { + var limb = x[2 * i].toLong() + (x[2 * i + 1].toLong() shl 28) + for (j in 0 until 7) { + serial[offset + 7 * i + j] = limb.toByte() + limb = limb shr 8 + } + } + } + + + private fun is_zero(x: Int): Int { + var xx = x.toLong() and 0xFFFFFFFFL + xx-- + return (xx shr 32).toInt() + } + + // p448_deserialize() + private fun deserialize(x: IntArray, serial: ByteArray, offset: Int): Int { + for (i in 0 until 8) { + var out = 0L + for (j in 0 until 7) { + out = out or ((serial[offset + 7 * i + j].toLong() and 0xFFL) shl (8 * j)) + } + x[2 * i] = out.toInt() and ((1 shl 28) - 1) + x[2 * i + 1] = (out ushr 28).toInt() + } + + /* Check for reduction. + * + * The idea is to create a variable ge which is all ones (rather, 56 ones) + * if and only if the low $i$ words of $x$ are >= those of p. + * + * Remember p = little_endian(1111,1111,1111,1111,1110,1111,1111,1111) + */ + var ge = -1 + val mask = (1 shl 28) - 1 + for (i in 0 until 8) { + ge = ge and x[i] + } + + /* At this point, ge = 1111 iff bottom are all 1111. Now propagate if 1110, or set if 1111 */ + ge = (ge and (x[8] + 1)) or is_zero(x[8] xor mask) + + /* Propagate the rest */ + for (i in 9 until 16) { + ge = ge and x[i] + } + + + return is_zero(ge xor mask).inv() + } + + /** + * Conditional swap of two values. + * + * @param select Set to 1 to swap, 0 to leave as-is. + * @param x The first value. + * @param y The second value. + */ + private fun cswap(select: Int, x: IntArray, y: IntArray) { + val s = -select + for (index in 0..15) { + val dummy = s and (x[index] xor y[index]) + x[index] = x[index] xor dummy + y[index] = y[index] xor dummy + } + } + + /** + * Evaluates the Curve448 curve. + * + * @param result Buffer to place the result of the evaluation into. + * @param offset Offset into the result buffer. + * @param privateKey The private key to use in the evaluation. + * @param publicKey The public key to use in the evaluation, or null + * if the base point of the curve should be used. + * @return Returns true if the curve evaluation was successful, + * false if the publicKey value is out of range. + */ + @JvmStatic + fun eval( + result: ByteArray, + offset: Int, + privateKey: ByteArray, + publicKey: ByteArray? + ): Boolean { + val state = Curve448() + var success = -1 + try { + // Unpack the public key value. If null, use 5 as the base point. + state.x_1.fill(0) + if (publicKey != null) { + // Convert the input value from little-endian into 28-bit limbs. + // It is possible that the public key is out of range. If so, + // delay reporting that state until the function completes. + success = deserialize(state.x_1, publicKey, 0) + } else { + state.x_1[0] = 5 + } + + // Initialize the other temporary variables. + state.x_2.fill(0) + state.x_2[0] = 1 + state.z_2.fill(0) + state.x_1.copyInto(state.x_3) + state.z_3.fill(0) + state.z_3[0] = 1 + + + // Evaluate the curve for every bit of the private key. + state.evalCurve(privateKey) + + // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^448 - 2^224 - 1. + state.recip(state.z_3, state.z_2) + state.mul(state.x_1, state.x_2, state.z_3) + + // Convert x_2 into little-endian in the result buffer. + strong_reduce(state.x_1) + serialize(result, offset, state.x_1) + } finally { + // Clean up all temporary state before we exit. + state.destroy() + } + return (success and 0x01) != 0 + } + } }