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.
This commit is contained in:
Severiano Jaramillo 2024-04-12 17:06:38 -07:00
parent 53bc55ec9f
commit c690d839da
4 changed files with 86 additions and 25 deletions

View file

@ -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<UInt> = 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<UInt> {
return (0 until 256).map { index ->
(0 until 8).fold(index.toUInt()) { crc, _ ->

View file

@ -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<String>, 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<String>, 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:

View file

@ -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(" ")
}

View file

@ -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)
}
}