diff --git a/amplifyframework/src/commonMain/kotlin/com/jump/sdk/amplifyframework/SRPHelper.kt b/amplifyframework/src/commonMain/kotlin/com/jump/sdk/amplifyframework/SRPHelper.kt index b65849d..57f1357 100644 --- a/amplifyframework/src/commonMain/kotlin/com/jump/sdk/amplifyframework/SRPHelper.kt +++ b/amplifyframework/src/commonMain/kotlin/com/jump/sdk/amplifyframework/SRPHelper.kt @@ -27,6 +27,7 @@ import org.kotlincrypto.macs.hmac.sha2.HmacSHA256 import kotlin.coroutines.cancellation.CancellationException import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi +import kotlinx.coroutines.delay private const val EPHEMERAL_KEY_LENGTH = 1024 private const val HEX = 16 @@ -47,9 +48,12 @@ private const val HEX_N = // precomputed: k = H(g|N) private val kByteArray = byteArrayOf(83, -126, -126, -60, 53, 71, 66, -41, -53, -67, -30, 53, -97, -49, 103, -7, -11, -77, -90, -80, -121, -111, -27, 1, 27, 67, -72, -91, -74, 109, -98, -26) + // precomputed: N = BigInteger.parseString(HEX_N, 16) private val nByteArray = byteArrayOf(0, -1, -1, -1, -1, -1, -1, -1, -1, -55, 15, -38, -94, 33, 104, -62, 52, -60, -58, 98, -117, -128, -36, 28, -47, 41, 2, 78, 8, -118, 103, -52, 116, 2, 11, -66, -90, 59, 19, -101, 34, 81, 74, 8, 121, -114, 52, 4, -35, -17, -107, 25, -77, -51, 58, 67, 27, 48, 43, 10, 109, -14, 95, 20, 55, 79, -31, 53, 109, 109, 81, -62, 69, -28, -123, -75, 118, 98, 94, 126, -58, -12, 76, 66, -23, -90, 55, -19, 107, 11, -1, 92, -74, -12, 6, -73, -19, -18, 56, 107, -5, 90, -119, -97, -91, -82, -97, 36, 17, 124, 75, 31, -26, 73, 40, 102, 81, -20, -28, 91, 61, -62, 0, 124, -72, -95, 99, -65, 5, -104, -38, 72, 54, 28, 85, -45, -102, 105, 22, 63, -88, -3, 36, -49, 95, -125, 101, 93, 35, -36, -93, -83, -106, 28, 98, -13, 86, 32, -123, 82, -69, -98, -43, 41, 7, 112, -106, -106, 109, 103, 12, 53, 78, 74, -68, -104, 4, -15, 116, 108, 8, -54, 24, 33, 124, 50, -112, 94, 70, 46, 54, -50, 59, -29, -98, 119, 44, 24, 14, -122, 3, -101, 39, -125, -94, -20, 7, -94, -113, -75, -59, 93, -16, 111, 76, 82, -55, -34, 43, -53, -10, -107, 88, 23, 24, 57, -107, 73, 124, -22, -107, 106, -27, 21, -46, 38, 24, -104, -6, 5, 16, 21, 114, -114, 90, -118, -86, -60, 45, -83, 51, 23, 13, 4, 80, 122, 51, -88, 85, 33, -85, -33, 28, -70, 100, -20, -5, -123, 4, 88, -37, -17, 10, -118, -22, 113, 87, 93, 6, 12, 125, -77, -105, 15, -123, -90, -31, -28, -57, -85, -11, -82, -116, -37, 9, 51, -41, 30, -116, -108, -32, 74, 37, 97, -99, -50, -29, -46, 38, 26, -46, -18, 107, -15, 47, -6, 6, -39, -118, 8, 100, -40, 118, 2, 115, 62, -56, 106, 100, 82, 31, 43, 24, 23, 123, 32, 12, -69, -31, 23, 87, 122, 97, 93, 108, 119, 9, -120, -64, -70, -39, 70, -30, 8, -30, 79, -96, 116, -27, -85, 49, 67, -37, 91, -4, -32, -3, 16, -114, 75, -126, -47, 32, -87, 58, -46, -54, -1, -1, -1, -1, -1, -1, -1, -1) +private enum class SrpGenerationState { NOT_STARTED, STARTED, COMPLETED } + @OptIn(ExperimentalEncodingApi::class) @Suppress("TooManyFunctions") class SRPHelper(userPool: String) { @@ -62,8 +66,9 @@ class SRPHelper(userPool: String) { private val random = SecureRandom() private val k: BigInteger = BigInteger.fromTwosComplementByteArray(kByteArray) - lateinit var privateA: BigInteger - lateinit var publicA: ModularBigInteger + private var srpState: SrpGenerationState = SrpGenerationState.NOT_STARTED + private lateinit var privateA: BigInteger + private lateinit var publicA: ModularBigInteger var timestamp: String = nowAsFormattedString() internal set @@ -79,14 +84,28 @@ class SRPHelper(userPool: String) { } // Generate client private 'a' and public 'A' values - suspend fun getPublicA(): String { - do { - privateA = BigInteger.fromByteArray(random.nextBytesOf(EPHEMERAL_KEY_LENGTH), Sign.POSITIVE).mod(N) - // A = (g ^ a) % N - publicA = g.pow(privateA) - } while (publicA.residue == BigInteger.ZERO) - return publicA.toString(HEX) - } + suspend fun getPublicA(): String = + when (srpState) { + SrpGenerationState.NOT_STARTED -> { + do { + privateA = BigInteger + .fromByteArray( + source = random.nextBytesOf(EPHEMERAL_KEY_LENGTH), + sign = Sign.POSITIVE, + ) + .mod(N) + // A = (g ^ a) % N + publicA = g.pow(privateA) + } while (publicA.residue == BigInteger.ZERO) + publicA.toString(HEX) + } + + SrpGenerationState.STARTED -> { + do { delay(10) } while (srpState != SrpGenerationState.COMPLETED) + publicA.toString(HEX) + } + SrpGenerationState.COMPLETED -> publicA.toString(HEX) + } // @TestOnly internal fun modN(value: BigInteger): BigInteger = value.mod(N) @@ -219,4 +238,3 @@ class SRPHelper(userPool: String) { return Base64.encode(m1Signature) } } -