From c690d839daea797853c0e5ce5dcd1c367ea4df81 Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Fri, 12 Apr 2024 17:06:38 -0700 Subject: [PATCH] Add verifyChecksum method to Mnemonics class. - The verifyChecksum() method uses the calculateChecksumIndex() method internally to obtain the checksum index, which is then used to confirm that the checksum word is correct. - Added tests for all verifyChecksum() scenarios. - Created MnemonicsDataProvider to more easily provide the data used in MnemonicsTest. --- .../net/agorise/shared/crypto/crc/Crc32.kt | 10 +++- .../shared/stargate/mnemonics/Mnemonics.kt | 33 ++++++------ .../mnemonics/MnemonicsDataProvider.kt | 17 +++++++ .../stargate/mnemonics/MnemonicsTest.kt | 51 ++++++++++++++++--- 4 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsDataProvider.kt diff --git a/shared/crypto/src/commonMain/kotlin/net/agorise/shared/crypto/crc/Crc32.kt b/shared/crypto/src/commonMain/kotlin/net/agorise/shared/crypto/crc/Crc32.kt index 0f9121c..fd4484e 100644 --- a/shared/crypto/src/commonMain/kotlin/net/agorise/shared/crypto/crc/Crc32.kt +++ b/shared/crypto/src/commonMain/kotlin/net/agorise/shared/crypto/crc/Crc32.kt @@ -1,11 +1,16 @@ package net.agorise.shared.crypto.crc /** + * Provides functionality to calculate CRC-32 checksums. + * * 0xEDB88320u is the reversed representation of the IEEE CRC-32 polynomial: 0x04C11DB7 */ -class Crc32(private val polynomial: UInt = 0xEDB88320u) { +class Crc32(polynomial: UInt = 0xEDB88320u) { internal val lookupTable: List = populateLookupTable(polynomial) + /** + * Calculates the CRC-32 checksum on the given byte array. + */ fun crc32(bytes: ByteArray): UInt { var crc = INITIAL_CRC32 for (byte in bytes) { @@ -15,6 +20,9 @@ class Crc32(private val polynomial: UInt = 0xEDB88320u) { return crc.inv() } + /** + * Populates the lookup table with the given polynomial to calculate CRC-32 checksums faster. + */ private fun populateLookupTable(polynomial: UInt): List { return (0 until 256).map { index -> (0 until 8).fold(index.toUInt()) { crc, _ -> diff --git a/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/Mnemonics.kt b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/Mnemonics.kt index 69be353..8e09182 100644 --- a/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/Mnemonics.kt +++ b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/Mnemonics.kt @@ -99,24 +99,23 @@ class Mnemonics { ) /** - * Verifies that the + * Obtains that the checksum index and verifies it corresponds to the checksum word. */ -// private fun verifyChecksum(words: Array, prefixLen: Int): Boolean { -// val seedLength = SEED_LENGTH -// -// if (words.size != seedLength + 1) { -// return false // Checksum word is not present, we cannot verify -// } -// -// val (checksumIndex, err) = calculateChecksumIndex(words.sliceArray(0 until words.size - 1), prefixLen) -// if (err != null) { -// return false -// } -// val calculatedChecksumWord = words[checksumIndex] -// val checksumWord = words[seedLength] -// -// return calculatedChecksumWord == checksumWord -// } + internal fun verifyChecksum(words: List, prefixLen: Int): Boolean { + if (words.size != SEED_LENGTH + 1) { + return false // Checksum word is not present, we cannot verify + } + + val result = calculateChecksumIndex(words.dropLast(1), prefixLen) + + // Obtain checksum index, or return false if checksum index is not present + val checksumIndex = result.getOrNull()?.toInt() ?: return false + + val calculatedChecksumWord = words[checksumIndex] + val checksumWord = words[SEED_LENGTH] + + return calculatedChecksumWord == checksumWord + } /** * Calculates a checksum using CRC-32 algorithm on the seed (SEED_LENGTH words) as follows: diff --git a/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsDataProvider.kt b/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsDataProvider.kt new file mode 100644 index 0000000..7993895 --- /dev/null +++ b/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsDataProvider.kt @@ -0,0 +1,17 @@ +package net.agorise.shared.stargate.mnemonics + +object MnemonicsDataProvider { + val invalidSizeSeed = "invalid size seed".split(" ") + val invalidChecksumSeed = + ("sequence atlas unveil summon pebbles tuesday beer rudely snake rockets different " + + "fuselage woven tagged bested dented vegan hover rapid fawns obvious muppet " + + "randomly seasons summon").split(" ") + val validEnglishSeed = + ("sequence atlas unveil summon pebbles tuesday beer rudely snake rockets different " + + "fuselage woven tagged bested dented vegan hover rapid fawns obvious muppet " + + "randomly seasons randomly").split(" ") + val validSpanishSeed = + ("perfil lujo faja puma favor pedir detalle doble carbón neón paella cuarto ánimo cuento " + + "conga correr dental moneda león donar entero logro realidad acceso doble") + .split(" ") +} diff --git a/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsTest.kt b/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsTest.kt index e3a9188..dfeb817 100644 --- a/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsTest.kt +++ b/shared/stargate/src/commonTest/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsTest.kt @@ -2,16 +2,17 @@ package net.agorise.shared.stargate.mnemonics import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertTrue class MnemonicsTest { private val mnemonics = Mnemonics() @Test - fun `given an invalid seed - when calculateChecksumIndex is called - then result is failure`() { - val invalidSeed = "invalid seed".split(" ") + fun `given an invalid size seed - when calculateChecksumIndex is called - then result is failure`() { + val invalidSizeSeed = MnemonicsDataProvider.invalidSizeSeed - val result = mnemonics.calculateChecksumIndex(invalidSeed, 3) + val result = mnemonics.calculateChecksumIndex(invalidSizeSeed, 3) assertTrue(result.isFailure) } @@ -19,9 +20,9 @@ class MnemonicsTest { @Test fun `given a valid english seed - when calculateChecksumIndex is called - then result is correct`() { val expectedChecksumIndex = 22u - val seed = "sequence atlas unveil summon pebbles tuesday beer rudely snake rockets different fuselage woven tagged bested dented vegan hover rapid fawns obvious muppet randomly seasons randomly".split(" ").dropLast(1) + val validEnglishSeed = MnemonicsDataProvider.validEnglishSeed.dropLast(1) - val result = mnemonics.calculateChecksumIndex(seed, 3) + val result = mnemonics.calculateChecksumIndex(validEnglishSeed, 3) val actualChecksumIndex = result.getOrThrow() assertEquals(expectedChecksumIndex, actualChecksumIndex) @@ -30,11 +31,47 @@ class MnemonicsTest { @Test fun `given a valid spanish seed - when calculateChecksumIndex is called - then result is correct`() { val expectedChecksumIndex = 7u - val seed = "perfil lujo faja puma favor pedir detalle doble carbón neón paella cuarto ánimo cuento conga correr dental moneda león donar entero logro realidad acceso doble".split(" ").dropLast(1) + val validSpanishSeed = MnemonicsDataProvider.validSpanishSeed.dropLast(1) - val result = mnemonics.calculateChecksumIndex(seed, 4) + val result = mnemonics.calculateChecksumIndex(validSpanishSeed, 4) val actualChecksumIndex = result.getOrThrow() assertEquals(expectedChecksumIndex, actualChecksumIndex) } + + @Test + fun `given an invalid size seed - when verifyChecksum is called - then result is false`() { + val invalidSizeSeed = MnemonicsDataProvider.invalidSizeSeed + + val result = mnemonics.verifyChecksum(invalidSizeSeed, 3) + + assertFalse(result) + } + + @Test + fun `given an invalid checksum seed - when verifyChecksum is called - then result is false`() { + val invalidChecksumSeed = MnemonicsDataProvider.invalidChecksumSeed + + val result = mnemonics.verifyChecksum(invalidChecksumSeed, 3) + + assertFalse(result) + } + + @Test + fun `given a valid english seed - when verifyChecksum is called - then result is true`() { + val validEnglishSeed = MnemonicsDataProvider.validEnglishSeed + + val result = mnemonics.verifyChecksum(validEnglishSeed, 3) + + assertTrue(result) + } + + @Test + fun `given a valid spanish seed - when verifyChecksum is called - then result is true`() { + val validSpanishSeed = MnemonicsDataProvider.validSpanishSeed + + val result = mnemonics.verifyChecksum(validSpanishSeed, 4) + + assertTrue(result) + } }