aboutsummaryrefslogtreecommitdiff
path: root/passgen
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2021-12-09 23:23:46 +0530
committerHarsh Shandilya <me@msfjarvis.dev>2021-12-21 02:13:24 +0530
commit16392b75814a71a4d78b3598cd3364baeb0b446e (patch)
tree2ca56bce0682664cf0bfc7bee0c58511e40907b8 /passgen
parent67f8eb840425ce287041d5ac5094c7831779f4d7 (diff)
diceware: add Die class and tests
Diffstat (limited to 'passgen')
-rw-r--r--passgen/diceware/api/diceware.api10
-rw-r--r--passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/Die.kt30
-rw-r--r--passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/RandomIntGenerator.kt15
-rw-r--r--passgen/diceware/src/test/kotlin/dev/msfjarvis/aps/passgen/diceware/DieTest.kt47
4 files changed, 102 insertions, 0 deletions
diff --git a/passgen/diceware/api/diceware.api b/passgen/diceware/api/diceware.api
new file mode 100644
index 00000000..8d16ae8f
--- /dev/null
+++ b/passgen/diceware/api/diceware.api
@@ -0,0 +1,10 @@
+public final class dev/msfjarvis/aps/passgen/diceware/Die {
+ public fun <init> (ILdev/msfjarvis/aps/passgen/diceware/RandomIntGenerator;)V
+ public final fun roll ()I
+ public final fun rollMultiple (I)I
+}
+
+public abstract interface class dev/msfjarvis/aps/passgen/diceware/RandomIntGenerator {
+ public abstract fun get (Lkotlin/ranges/IntRange;)I
+}
+
diff --git a/passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/Die.kt b/passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/Die.kt
new file mode 100644
index 00000000..d6e5aa23
--- /dev/null
+++ b/passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/Die.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.passgen.diceware
+
+import javax.inject.Inject
+
+/** Basic implementation of a die with configurable number of sides. */
+public class Die
+@Inject
+constructor(
+ private val sides: Int,
+ private val random: RandomIntGenerator,
+) {
+
+ /** Roll the die to return a single number. */
+ public fun roll(): Int {
+ return random.get(1..sides)
+ }
+
+ /**
+ * Roll the die multiple times, concatenating each result to obtain a number with [iterations]
+ * digits.
+ */
+ public fun rollMultiple(iterations: Int): Int {
+ return StringBuilder().apply { repeat(iterations) { append(roll()) } }.toString().toInt()
+ }
+}
diff --git a/passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/RandomIntGenerator.kt b/passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/RandomIntGenerator.kt
new file mode 100644
index 00000000..7f957419
--- /dev/null
+++ b/passgen/diceware/src/main/kotlin/dev/msfjarvis/aps/passgen/diceware/RandomIntGenerator.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.passgen.diceware
+
+/**
+ * SAM interface that takes in an [IntRange] and returns a randomly chosen [Int] within its bounds.
+ * This is used as a replacement for [kotlin.random.Random] since there is no CSPRNG-backed
+ * implementation of it in the Kotlin stdlib.
+ */
+public fun interface RandomIntGenerator {
+ public fun get(range: IntRange): Int
+}
diff --git a/passgen/diceware/src/test/kotlin/dev/msfjarvis/aps/passgen/diceware/DieTest.kt b/passgen/diceware/src/test/kotlin/dev/msfjarvis/aps/passgen/diceware/DieTest.kt
new file mode 100644
index 00000000..69a355c5
--- /dev/null
+++ b/passgen/diceware/src/test/kotlin/dev/msfjarvis/aps/passgen/diceware/DieTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.passgen.diceware
+
+import kotlin.random.Random
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class DieTest {
+
+ /** Pre-seeded [Random] instance to ensure tests are deterministic. */
+ private val random = Random(1_00_000)
+
+ private val intGenerator = RandomIntGenerator { it.random(random) }
+
+ @Test
+ fun test_one_roll() {
+ val die = Die(6, intGenerator)
+ assertEquals(5, die.roll())
+ }
+
+ @Test
+ fun test_multiple_rolls() {
+ val die = Die(6, intGenerator)
+ assertEquals(526242, die.rollMultiple(6))
+ }
+
+ @Test
+ fun test_consecutive_rolls() {
+ val die = Die(6, intGenerator)
+ assertEquals(5, die.roll())
+ assertEquals(2, die.roll())
+ assertEquals(6, die.roll())
+ assertEquals(2, die.roll())
+ assertEquals(4, die.roll())
+ assertEquals(2, die.roll())
+ }
+
+ @Test
+ fun test_100_sides() {
+ val die = Die(100, intGenerator)
+ assertEquals(67, die.roll())
+ }
+}