From 7dab5227dccbd016fc33284ba65baa2d019c52ac Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Thu, 11 Apr 2024 19:49:51 -0700 Subject: [PATCH] Introduce CRC-32 validation functionality. - Created a new shared module 'crypto' that will contain crytography specific functionality. - Added a class to validate data using the CRC-32 algorithm. We had to create a class to do this from scratch because there is no readily made library available for Kotlin Multiplatform yet. - Configured unit tests in the crypto module to confirm that the CRC-32 functionality is correctly implemented. - Introduced a Mnemonics class that contains logic to convert mnemonics to a valid Dero key and viceversa. This functionality is not complete yet. - Introduced Mnemonics support for two languages for now, English and Spanish. Adding support for more languages is a matter of adding a new Mnemonics[Language] instance. We can do that later when necessary. --- gradle/libs.versions.toml | 7 +- settings.gradle.kts | 1 + shared/crypto/build.gradle.kts | 44 + .../net/agorise/shared/crypto/crc/Crc32.kt | 33 + .../agorise/shared/crypto/crc/Crc32Test.kt | 36 + shared/stargate/build.gradle.kts | 2 + .../shared/stargate/mnemonics/Mnemonics.kt | 153 ++ .../stargate/mnemonics/MnemonicsEnglish.kt | 1635 +++++++++++++++++ .../stargate/mnemonics/MnemonicsLanguage.kt | 16 + .../stargate/mnemonics/MnemonicsSpanish.kt | 1635 +++++++++++++++++ 10 files changed, 3560 insertions(+), 2 deletions(-) create mode 100644 shared/crypto/build.gradle.kts create mode 100644 shared/crypto/src/commonMain/kotlin/net/agorise/shared/crypto/crc/Crc32.kt create mode 100644 shared/crypto/src/commonTest/kotlin/net/agorise/shared/crypto/crc/Crc32Test.kt create mode 100644 shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/Mnemonics.kt create mode 100644 shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsEnglish.kt create mode 100644 shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsLanguage.kt create mode 100644 shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsSpanish.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e7b5f62..ec1688e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,21 +7,24 @@ androidx-activityCompose = "1.8.2" compose = "1.6.4" compose-plugin = "1.6.1" coroutines = "1.8.0" +cryptography = "0.3.0" kotlin = "1.9.23" ktor = "2.3.9" multiplatform-settings = "1.1.1" voyager = "1.0.0" [libraries] -androidx-activity-compose = { group = "androidx.activity", name ="activity-compose", version.ref = "androidx-activityCompose" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activityCompose" } compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose" } compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose" } coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "coroutines" } +cryptography-bigint = { group = "dev.whyoleg.cryptography", name = "cryptography-bigint", version.ref = "cryptography" } +kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" } ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } ktor-client-websockets = { group = "io.ktor", name = "ktor-client-websockets", version.ref = "ktor" } -multiplatform-settings = { group="com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatform-settings" } +multiplatform-settings = { group = "com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatform-settings" } voyager-navigator = { group = "cafe.adriel.voyager", name = "voyager-navigator", version.ref = "voyager" } voyager-screenmodel = { group = "cafe.adriel.voyager", name = "voyager-screenmodel", version.ref = "voyager" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 8afceb6..56aca6f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,5 +19,6 @@ dependencyResolutionManagement { } include(":composeApp") +include(":shared:crypto") include(":shared:preferences") include(":shared:stargate") diff --git a/shared/crypto/build.gradle.kts b/shared/crypto/build.gradle.kts new file mode 100644 index 0000000..7a742e3 --- /dev/null +++ b/shared/crypto/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) +} + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "11" + } + } + } + + jvm() + + iosX64() + iosArm64() + iosSimulatorArm64() + + sourceSets { + commonMain.dependencies { + implementation(libs.cryptography.bigint) + } + + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } +} + +android { + namespace = "net.agorise.shared.crypto" + compileSdk = libs.versions.android.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.android.minSdk.get().toInt() + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} 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 new file mode 100644 index 0000000..0f9121c --- /dev/null +++ b/shared/crypto/src/commonMain/kotlin/net/agorise/shared/crypto/crc/Crc32.kt @@ -0,0 +1,33 @@ +package net.agorise.shared.crypto.crc + +/** + * 0xEDB88320u is the reversed representation of the IEEE CRC-32 polynomial: 0x04C11DB7 + */ +class Crc32(private val polynomial: UInt = 0xEDB88320u) { + internal val lookupTable: List = populateLookupTable(polynomial) + + fun crc32(bytes: ByteArray): UInt { + var crc = INITIAL_CRC32 + for (byte in bytes) { + val index = ((crc.toInt() xor byte.toInt()) and 0xFF) + crc = (crc shr 8) xor lookupTable[index] + } + return crc.inv() + } + + private fun populateLookupTable(polynomial: UInt): List { + return (0 until 256).map { index -> + (0 until 8).fold(index.toUInt()) { crc, _ -> + if (crc and 1u != 0u) { + (crc shr 1) xor polynomial + } else { + crc shr 1 + } + } + } + } + + companion object { + private const val INITIAL_CRC32 = 0xFFFFFFFFu + } +} diff --git a/shared/crypto/src/commonTest/kotlin/net/agorise/shared/crypto/crc/Crc32Test.kt b/shared/crypto/src/commonTest/kotlin/net/agorise/shared/crypto/crc/Crc32Test.kt new file mode 100644 index 0000000..840609e --- /dev/null +++ b/shared/crypto/src/commonTest/kotlin/net/agorise/shared/crypto/crc/Crc32Test.kt @@ -0,0 +1,36 @@ +package net.agorise.shared.crypto.crc + +import kotlin.test.Test +import kotlin.test.assertEquals + +class Crc32Test { + @Test + fun `given default crc32 - when init is called - then correct lookup table is generated`() { + val crc32 = Crc32() + + // Just confirm some of the values in the lookup table + assertEquals(0x00000000u, crc32.lookupTable[0]) + assertEquals(0x77073096u, crc32.lookupTable[1]) + assertEquals(0x5edef90eu, crc32.lookupTable[120]) + assertEquals(0x2d02ef8du, crc32.lookupTable[255]) + } + + @Test + fun `given a string - when crc32 is called - then result is correct`() { + val stringToResultMap = mapOf( + "a" to 0xe8b7be43u, + "abc" to 0x352441c2u, + "Kee rocks!!" to 0x60039468u, + ) + + val crc32 = Crc32() + + for ((string, expectedResult) in stringToResultMap) { + val bytes = string.encodeToByteArray() + + val actualResult = crc32.crc32(bytes) + + assertEquals(expectedResult, actualResult) + } + } +} diff --git a/shared/stargate/build.gradle.kts b/shared/stargate/build.gradle.kts index 5a47abe..41ade1a 100644 --- a/shared/stargate/build.gradle.kts +++ b/shared/stargate/build.gradle.kts @@ -23,6 +23,8 @@ kotlin { implementation(libs.ktor.client.cio) implementation(libs.ktor.client.core) implementation(libs.ktor.client.websockets) + + implementation(libs.cryptography.bigint) } } } 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 new file mode 100644 index 0000000..be2bd17 --- /dev/null +++ b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/Mnemonics.kt @@ -0,0 +1,153 @@ +package net.agorise.shared.stargate.mnemonics + +import dev.whyoleg.cryptography.bigint.BigInt +import dev.whyoleg.cryptography.bigint.toBigInt + +/** + * Provides functionality to create and validate mnemonics. + * + * Ported from https://github.com/deroproject/derohe/blob/main/walletapi/mnemonics/mnemonics.go + */ +class Mnemonics { + private val languages: List = listOf( + mnemonicsEnglish, + mnemonicsSpanish, + ) + + init { + // Validate that the list of languages have the correct number of words + for (language in languages) { + if (language.words.size != WORDS_SIZE) { + println("${language.nameEnglish} language has ${language.words.size} words, but should have $WORDS_SIZE") + } + } + } + + /** + * Returns the list of languages that are supported to create/validate a seed + */ + fun getSupportedLanguages(): List { + val supportedLanguages = mutableListOf() + for (language in languages) { + supportedLanguages.add(language.name) + } + + return supportedLanguages + } + + /** + * Given a list of words, returns the associated key if the words are valid or an exception otherwise + */ + fun getKeyFromWords(words: List): Result { + // The list must contain SEED_LENGTH + 1 words. The SEED_LENGTH + 1 word is the checksum + if (words.size != (SEED_LENGTH + 1)) { + return Result.failure(Exception("Invalid seed")) + } + + val wordsLanguageAndIndices = getWordsLanguageAndIndices(words) + ?: return Result.failure(Exception("Seed not found in any language")) + + val languageName = languages[wordsLanguageAndIndices.languageIndex].name + + + val key = byteArrayOf() + + return Result.success("".toBigInt()) // TODO Remove hardcoded value + } + + /** + * Returns the words language and the indices where each word was found. All words must be + * from the same language. + */ + private fun getWordsLanguageAndIndices(words: List): WordsLanguageAndIndices? { + for (i in languages.indices) { + val indices = mutableListOf() + + // Using map of words to find the words faster + val languageWordsMap = languages[i].words.withIndex().associate { (index, word) -> + word to index + } + + var foundAllWords = true + + // Loop through user supplied words + for (j in words.indices) { + val index = languageWordsMap[words[j]] + if (index != null) { + indices.add(index.toULong()) + } else { + foundAllWords = false + break + } + } + + if (foundAllWords) { + val wordsListCount = languages[i].words.size.toULong() + return WordsLanguageAndIndices(indices, i, wordsListCount) + } + } + + return null + } + + private data class WordsLanguageAndIndices( + val indices: List, + val languageIndex: Int, + val wordListCount: ULong, + ) + + /** + * Verifies that the + */ +// 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 +// } +// +// /** +// * Calculates a checksum (using CRC algorithm) on first 24 words. +// */ +// @OptIn(ExperimentalUnsignedTypes::class) +// private fun calculateChecksumIndex(words: Array, prefixLen: Int): Result { +// val trimmedRunes = mutableListOf() +// +// if (words.size != SEED_LENGTH) { +// return Result.failure(Exception("Words not equal to seed length")) +// } +// +// for (word in words) { +// val trimmedWord = if (word.length > prefixLen) { +// word.substring(0, prefixLen) +// } else { +// word +// } +// trimmedRunes.addAll(trimmedWord.toCharArray().toList()) +// } +// +// val checksum = CRC32().apply { +// update(trimmedRunes.toCharArray().concatToString().toByteArray(Charsets.UTF_8)) +// }.value +// +// return Result.success(checksum % SEED_LENGTH.toUInt()) +// } + + companion object { + // Each language should have exactly this number of words + private const val WORDS_SIZE = 1626 + + // Checksum seeds are 24 + 1 = 25 words long + private const val SEED_LENGTH = 24 + } +} diff --git a/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsEnglish.kt b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsEnglish.kt new file mode 100644 index 0000000..527309e --- /dev/null +++ b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsEnglish.kt @@ -0,0 +1,1635 @@ +package net.agorise.shared.stargate.mnemonics + +val mnemonicsEnglish = MnemonicsLanguage( + name = "English", + nameEnglish = "English (US)", + uniquePrefixLength = 3, + words = listOf( + "abbey", + "abducts", + "ability", + "ablaze", + "abnormal", + "abort", + "abrasive", + "absorb", + "abyss", + "academy", + "aces", + "aching", + "acidic", + "acoustic", + "acquire", + "across", + "actress", + "acumen", + "adapt", + "addicted", + "adept", + "adhesive", + "adjust", + "adopt", + "adrenalin", + "adult", + "adventure", + "aerial", + "afar", + "affair", + "afield", + "afloat", + "afoot", + "afraid", + "after", + "against", + "agenda", + "aggravate", + "agile", + "aglow", + "agnostic", + "agony", + "agreed", + "ahead", + "aided", + "ailments", + "aimless", + "airport", + "aisle", + "ajar", + "akin", + "alarms", + "album", + "alchemy", + "alerts", + "algebra", + "alkaline", + "alley", + "almost", + "aloof", + "alpine", + "already", + "also", + "altitude", + "alumni", + "always", + "amaze", + "ambush", + "amended", + "amidst", + "ammo", + "amnesty", + "among", + "amply", + "amused", + "anchor", + "android", + "anecdote", + "angled", + "ankle", + "annoyed", + "answers", + "antics", + "anvil", + "anxiety", + "anybody", + "apart", + "apex", + "aphid", + "aplomb", + "apology", + "apply", + "apricot", + "aptitude", + "aquarium", + "arbitrary", + "archer", + "ardent", + "arena", + "argue", + "arises", + "army", + "around", + "arrow", + "arsenic", + "artistic", + "ascend", + "ashtray", + "aside", + "asked", + "asleep", + "aspire", + "assorted", + "asylum", + "athlete", + "atlas", + "atom", + "atrium", + "attire", + "auburn", + "auctions", + "audio", + "august", + "aunt", + "austere", + "autumn", + "avatar", + "avidly", + "avoid", + "awakened", + "awesome", + "awful", + "awkward", + "awning", + "awoken", + "axes", + "axis", + "axle", + "aztec", + "azure", + "baby", + "bacon", + "badge", + "baffles", + "bagpipe", + "bailed", + "bakery", + "balding", + "bamboo", + "banjo", + "baptism", + "basin", + "batch", + "bawled", + "bays", + "because", + "beer", + "befit", + "begun", + "behind", + "being", + "below", + "bemused", + "benches", + "berries", + "bested", + "betting", + "bevel", + "beware", + "beyond", + "bias", + "bicycle", + "bids", + "bifocals", + "biggest", + "bikini", + "bimonthly", + "binocular", + "biology", + "biplane", + "birth", + "biscuit", + "bite", + "biweekly", + "blender", + "blip", + "bluntly", + "boat", + "bobsled", + "bodies", + "bogeys", + "boil", + "boldly", + "bomb", + "border", + "boss", + "both", + "bounced", + "bovine", + "bowling", + "boxes", + "boyfriend", + "broken", + "brunt", + "bubble", + "buckets", + "budget", + "buffet", + "bugs", + "building", + "bulb", + "bumper", + "bunch", + "business", + "butter", + "buying", + "buzzer", + "bygones", + "byline", + "bypass", + "cabin", + "cactus", + "cadets", + "cafe", + "cage", + "cajun", + "cake", + "calamity", + "camp", + "candy", + "casket", + "catch", + "cause", + "cavernous", + "cease", + "cedar", + "ceiling", + "cell", + "cement", + "cent", + "certain", + "chlorine", + "chrome", + "cider", + "cigar", + "cinema", + "circle", + "cistern", + "citadel", + "civilian", + "claim", + "click", + "clue", + "coal", + "cobra", + "cocoa", + "code", + "coexist", + "coffee", + "cogs", + "cohesive", + "coils", + "colony", + "comb", + "cool", + "copy", + "corrode", + "costume", + "cottage", + "cousin", + "cowl", + "criminal", + "cube", + "cucumber", + "cuddled", + "cuffs", + "cuisine", + "cunning", + "cupcake", + "custom", + "cycling", + "cylinder", + "cynical", + "dabbing", + "dads", + "daft", + "dagger", + "daily", + "damp", + "dangerous", + "dapper", + "darted", + "dash", + "dating", + "dauntless", + "dawn", + "daytime", + "dazed", + "debut", + "decay", + "dedicated", + "deepest", + "deftly", + "degrees", + "dehydrate", + "deity", + "dejected", + "delayed", + "demonstrate", + "dented", + "deodorant", + "depth", + "desk", + "devoid", + "dewdrop", + "dexterity", + "dialect", + "dice", + "diet", + "different", + "digit", + "dilute", + "dime", + "dinner", + "diode", + "diplomat", + "directed", + "distance", + "ditch", + "divers", + "dizzy", + "doctor", + "dodge", + "does", + "dogs", + "doing", + "dolphin", + "domestic", + "donuts", + "doorway", + "dormant", + "dosage", + "dotted", + "double", + "dove", + "down", + "dozen", + "dreams", + "drinks", + "drowning", + "drunk", + "drying", + "dual", + "dubbed", + "duckling", + "dude", + "duets", + "duke", + "dullness", + "dummy", + "dunes", + "duplex", + "duration", + "dusted", + "duties", + "dwarf", + "dwelt", + "dwindling", + "dying", + "dynamite", + "dyslexic", + "each", + "eagle", + "earth", + "easy", + "eating", + "eavesdrop", + "eccentric", + "echo", + "eclipse", + "economics", + "ecstatic", + "eden", + "edgy", + "edited", + "educated", + "eels", + "efficient", + "eggs", + "egotistic", + "eight", + "either", + "eject", + "elapse", + "elbow", + "eldest", + "eleven", + "elite", + "elope", + "else", + "eluded", + "emails", + "ember", + "emerge", + "emit", + "emotion", + "empty", + "emulate", + "energy", + "enforce", + "enhanced", + "enigma", + "enjoy", + "enlist", + "enmity", + "enough", + "enraged", + "ensign", + "entrance", + "envy", + "epoxy", + "equip", + "erase", + "erected", + "erosion", + "error", + "eskimos", + "espionage", + "essential", + "estate", + "etched", + "eternal", + "ethics", + "etiquette", + "evaluate", + "evenings", + "evicted", + "evolved", + "examine", + "excess", + "exhale", + "exit", + "exotic", + "exquisite", + "extra", + "exult", + "fabrics", + "factual", + "fading", + "fainted", + "faked", + "fall", + "family", + "fancy", + "farming", + "fatal", + "faulty", + "fawns", + "faxed", + "fazed", + "feast", + "february", + "federal", + "feel", + "feline", + "females", + "fences", + "ferry", + "festival", + "fetches", + "fever", + "fewest", + "fiat", + "fibula", + "fictional", + "fidget", + "fierce", + "fifteen", + "fight", + "films", + "firm", + "fishing", + "fitting", + "five", + "fixate", + "fizzle", + "fleet", + "flippant", + "flying", + "foamy", + "focus", + "foes", + "foggy", + "foiled", + "folding", + "fonts", + "foolish", + "fossil", + "fountain", + "fowls", + "foxes", + "foyer", + "framed", + "friendly", + "frown", + "fruit", + "frying", + "fudge", + "fuel", + "fugitive", + "fully", + "fuming", + "fungal", + "furnished", + "fuselage", + "future", + "fuzzy", + "gables", + "gadget", + "gags", + "gained", + "galaxy", + "gambit", + "gang", + "gasp", + "gather", + "gauze", + "gave", + "gawk", + "gaze", + "gearbox", + "gecko", + "geek", + "gels", + "gemstone", + "general", + "geometry", + "germs", + "gesture", + "getting", + "geyser", + "ghetto", + "ghost", + "giant", + "giddy", + "gifts", + "gigantic", + "gills", + "gimmick", + "ginger", + "girth", + "giving", + "glass", + "gleeful", + "glide", + "gnaw", + "gnome", + "goat", + "goblet", + "godfather", + "goes", + "goggles", + "going", + "goldfish", + "gone", + "goodbye", + "gopher", + "gorilla", + "gossip", + "gotten", + "gourmet", + "governing", + "gown", + "greater", + "grunt", + "guarded", + "guest", + "guide", + "gulp", + "gumball", + "guru", + "gusts", + "gutter", + "guys", + "gymnast", + "gypsy", + "gyrate", + "habitat", + "hacksaw", + "haggled", + "hairy", + "hamburger", + "happens", + "hashing", + "hatchet", + "haunted", + "having", + "hawk", + "haystack", + "hazard", + "hectare", + "hedgehog", + "heels", + "hefty", + "height", + "hemlock", + "hence", + "heron", + "hesitate", + "hexagon", + "hickory", + "hiding", + "highway", + "hijack", + "hiker", + "hills", + "himself", + "hinder", + "hippo", + "hire", + "history", + "hitched", + "hive", + "hoax", + "hobby", + "hockey", + "hoisting", + "hold", + "honked", + "hookup", + "hope", + "hornet", + "hospital", + "hotel", + "hounded", + "hover", + "howls", + "hubcaps", + "huddle", + "huge", + "hull", + "humid", + "hunter", + "hurried", + "husband", + "huts", + "hybrid", + "hydrogen", + "hyper", + "iceberg", + "icing", + "icon", + "identity", + "idiom", + "idled", + "idols", + "igloo", + "ignore", + "iguana", + "illness", + "imagine", + "imbalance", + "imitate", + "impel", + "inactive", + "inbound", + "incur", + "industrial", + "inexact", + "inflamed", + "ingested", + "initiate", + "injury", + "inkling", + "inline", + "inmate", + "innocent", + "inorganic", + "input", + "inquest", + "inroads", + "insult", + "intended", + "inundate", + "invoke", + "inwardly", + "ionic", + "irate", + "iris", + "irony", + "irritate", + "island", + "isolated", + "issued", + "italics", + "itches", + "items", + "itinerary", + "itself", + "ivory", + "jabbed", + "jackets", + "jaded", + "jagged", + "jailed", + "jamming", + "january", + "jargon", + "jaunt", + "javelin", + "jaws", + "jazz", + "jeans", + "jeers", + "jellyfish", + "jeopardy", + "jerseys", + "jester", + "jetting", + "jewels", + "jigsaw", + "jingle", + "jittery", + "jive", + "jobs", + "jockey", + "jogger", + "joining", + "joking", + "jolted", + "jostle", + "journal", + "joyous", + "jubilee", + "judge", + "juggled", + "juicy", + "jukebox", + "july", + "jump", + "junk", + "jury", + "justice", + "juvenile", + "kangaroo", + "karate", + "keep", + "kennel", + "kept", + "kernels", + "kettle", + "keyboard", + "kickoff", + "kidneys", + "king", + "kiosk", + "kisses", + "kitchens", + "kiwi", + "knapsack", + "knee", + "knife", + "knowledge", + "knuckle", + "koala", + "laboratory", + "ladder", + "lagoon", + "lair", + "lakes", + "lamb", + "language", + "laptop", + "large", + "last", + "later", + "launching", + "lava", + "lawsuit", + "layout", + "lazy", + "lectures", + "ledge", + "leech", + "left", + "legion", + "leisure", + "lemon", + "lending", + "leopard", + "lesson", + "lettuce", + "lexicon", + "liar", + "library", + "licks", + "lids", + "lied", + "lifestyle", + "light", + "likewise", + "lilac", + "limits", + "linen", + "lion", + "lipstick", + "liquid", + "listen", + "lively", + "loaded", + "lobster", + "locker", + "lodge", + "lofty", + "logic", + "loincloth", + "long", + "looking", + "lopped", + "lordship", + "losing", + "lottery", + "loudly", + "love", + "lower", + "loyal", + "lucky", + "luggage", + "lukewarm", + "lullaby", + "lumber", + "lunar", + "lurk", + "lush", + "luxury", + "lymph", + "lynx", + "lyrics", + "macro", + "madness", + "magically", + "mailed", + "major", + "makeup", + "malady", + "mammal", + "maps", + "masterful", + "match", + "maul", + "maverick", + "maximum", + "mayor", + "maze", + "meant", + "mechanic", + "medicate", + "meeting", + "megabyte", + "melting", + "memoir", + "menu", + "merger", + "mesh", + "metro", + "mews", + "mice", + "midst", + "mighty", + "mime", + "mirror", + "misery", + "mittens", + "mixture", + "moat", + "mobile", + "mocked", + "mohawk", + "moisture", + "molten", + "moment", + "money", + "moon", + "mops", + "morsel", + "mostly", + "motherly", + "mouth", + "movement", + "mowing", + "much", + "muddy", + "muffin", + "mugged", + "mullet", + "mumble", + "mundane", + "muppet", + "mural", + "musical", + "muzzle", + "myriad", + "mystery", + "myth", + "nabbing", + "nagged", + "nail", + "names", + "nanny", + "napkin", + "narrate", + "nasty", + "natural", + "nautical", + "navy", + "nearby", + "necklace", + "needed", + "negative", + "neither", + "neon", + "nephew", + "nerves", + "nestle", + "network", + "neutral", + "never", + "newt", + "nexus", + "nibs", + "niche", + "niece", + "nifty", + "nightly", + "nimbly", + "nineteen", + "nirvana", + "nitrogen", + "nobody", + "nocturnal", + "nodes", + "noises", + "nomad", + "noodles", + "northern", + "nostril", + "noted", + "nouns", + "novelty", + "nowhere", + "nozzle", + "nuance", + "nucleus", + "nudged", + "nugget", + "nuisance", + "null", + "number", + "nuns", + "nurse", + "nutshell", + "nylon", + "oaks", + "oars", + "oasis", + "oatmeal", + "obedient", + "object", + "obliged", + "obnoxious", + "observant", + "obtains", + "obvious", + "occur", + "ocean", + "october", + "odds", + "odometer", + "offend", + "often", + "oilfield", + "ointment", + "okay", + "older", + "olive", + "olympics", + "omega", + "omission", + "omnibus", + "onboard", + "oncoming", + "oneself", + "ongoing", + "onion", + "online", + "onslaught", + "onto", + "onward", + "oozed", + "opacity", + "opened", + "opposite", + "optical", + "opus", + "orange", + "orbit", + "orchid", + "orders", + "organs", + "origin", + "ornament", + "orphans", + "oscar", + "ostrich", + "otherwise", + "otter", + "ouch", + "ought", + "ounce", + "ourselves", + "oust", + "outbreak", + "oval", + "oven", + "owed", + "owls", + "owner", + "oxidant", + "oxygen", + "oyster", + "ozone", + "pact", + "paddles", + "pager", + "pairing", + "palace", + "pamphlet", + "pancakes", + "paper", + "paradise", + "pastry", + "patio", + "pause", + "pavements", + "pawnshop", + "payment", + "peaches", + "pebbles", + "peculiar", + "pedantic", + "peeled", + "pegs", + "pelican", + "pencil", + "people", + "pepper", + "perfect", + "pests", + "petals", + "phase", + "pheasants", + "phone", + "phrases", + "physics", + "piano", + "picked", + "pierce", + "pigment", + "piloted", + "pimple", + "pinched", + "pioneer", + "pipeline", + "pirate", + "pistons", + "pitched", + "pivot", + "pixels", + "pizza", + "playful", + "pledge", + "pliers", + "plotting", + "plus", + "plywood", + "poaching", + "pockets", + "podcast", + "poetry", + "point", + "poker", + "polar", + "ponies", + "pool", + "popular", + "portents", + "possible", + "potato", + "pouch", + "poverty", + "powder", + "pram", + "present", + "pride", + "problems", + "pruned", + "prying", + "psychic", + "public", + "puck", + "puddle", + "puffin", + "pulp", + "pumpkins", + "punch", + "puppy", + "purged", + "push", + "putty", + "puzzled", + "pylons", + "pyramid", + "python", + "queen", + "quick", + "quote", + "rabbits", + "racetrack", + "radar", + "rafts", + "rage", + "railway", + "raking", + "rally", + "ramped", + "randomly", + "rapid", + "rarest", + "rash", + "rated", + "ravine", + "rays", + "razor", + "react", + "rebel", + "recipe", + "reduce", + "reef", + "refer", + "regular", + "reheat", + "reinvest", + "rejoices", + "rekindle", + "relic", + "remedy", + "renting", + "reorder", + "repent", + "request", + "reruns", + "rest", + "return", + "reunion", + "revamp", + "rewind", + "rhino", + "rhythm", + "ribbon", + "richly", + "ridges", + "rift", + "rigid", + "rims", + "ringing", + "riots", + "ripped", + "rising", + "ritual", + "river", + "roared", + "robot", + "rockets", + "rodent", + "rogue", + "roles", + "romance", + "roomy", + "roped", + "roster", + "rotate", + "rounded", + "rover", + "rowboat", + "royal", + "ruby", + "rudely", + "ruffled", + "rugged", + "ruined", + "ruling", + "rumble", + "runway", + "rural", + "rustled", + "ruthless", + "sabotage", + "sack", + "sadness", + "safety", + "saga", + "sailor", + "sake", + "salads", + "sample", + "sanity", + "sapling", + "sarcasm", + "sash", + "satin", + "saucepan", + "saved", + "sawmill", + "saxophone", + "sayings", + "scamper", + "scenic", + "school", + "science", + "scoop", + "scrub", + "scuba", + "seasons", + "second", + "sedan", + "seeded", + "segments", + "seismic", + "selfish", + "semifinal", + "sensible", + "september", + "sequence", + "serving", + "session", + "setup", + "seventh", + "sewage", + "shackles", + "shelter", + "shipped", + "shocking", + "shrugged", + "shuffled", + "shyness", + "siblings", + "sickness", + "sidekick", + "sieve", + "sifting", + "sighting", + "silk", + "simplest", + "sincerely", + "sipped", + "siren", + "situated", + "sixteen", + "sizes", + "skater", + "skew", + "skirting", + "skulls", + "skydive", + "slackens", + "sleepless", + "slid", + "slower", + "slug", + "smash", + "smelting", + "smidgen", + "smog", + "smuggled", + "snake", + "sneeze", + "sniff", + "snout", + "snug", + "soapy", + "sober", + "soccer", + "soda", + "software", + "soggy", + "soil", + "solved", + "somewhere", + "sonic", + "soothe", + "soprano", + "sorry", + "southern", + "sovereign", + "sowed", + "soya", + "space", + "speedy", + "sphere", + "spiders", + "splendid", + "spout", + "sprig", + "spud", + "spying", + "square", + "stacking", + "stellar", + "stick", + "stockpile", + "strained", + "stunning", + "stylishly", + "subtly", + "succeed", + "suddenly", + "suede", + "suffice", + "sugar", + "suitcase", + "sulking", + "summon", + "sunken", + "superior", + "surfer", + "sushi", + "suture", + "swagger", + "swept", + "swiftly", + "sword", + "swung", + "syllabus", + "symptoms", + "syndrome", + "syringe", + "system", + "taboo", + "tacit", + "tadpoles", + "tagged", + "tail", + "taken", + "talent", + "tamper", + "tanks", + "tapestry", + "tarnished", + "tasked", + "tattoo", + "taunts", + "tavern", + "tawny", + "taxi", + "teardrop", + "technical", + "tedious", + "teeming", + "tell", + "template", + "tender", + "tepid", + "tequila", + "terminal", + "testing", + "tether", + "textbook", + "thaw", + "theatrics", + "thirsty", + "thorn", + "threaten", + "thumbs", + "thwart", + "ticket", + "tidy", + "tiers", + "tiger", + "tilt", + "timber", + "tinted", + "tipsy", + "tirade", + "tissue", + "titans", + "toaster", + "tobacco", + "today", + "toenail", + "toffee", + "together", + "toilet", + "token", + "tolerant", + "tomorrow", + "tonic", + "toolbox", + "topic", + "torch", + "tossed", + "total", + "touchy", + "towel", + "toxic", + "toyed", + "trash", + "trendy", + "tribal", + "trolling", + "truth", + "trying", + "tsunami", + "tubes", + "tucks", + "tudor", + "tuesday", + "tufts", + "tugs", + "tuition", + "tulips", + "tumbling", + "tunnel", + "turnip", + "tusks", + "tutor", + "tuxedo", + "twang", + "tweezers", + "twice", + "twofold", + "tycoon", + "typist", + "tyrant", + "ugly", + "ulcers", + "ultimate", + "umbrella", + "umpire", + "unafraid", + "unbending", + "uncle", + "under", + "uneven", + "unfit", + "ungainly", + "unhappy", + "union", + "unjustly", + "unknown", + "unlikely", + "unmask", + "unnoticed", + "unopened", + "unplugs", + "unquoted", + "unrest", + "unsafe", + "until", + "unusual", + "unveil", + "unwind", + "unzip", + "upbeat", + "upcoming", + "update", + "upgrade", + "uphill", + "upkeep", + "upload", + "upon", + "upper", + "upright", + "upstairs", + "uptight", + "upwards", + "urban", + "urchins", + "urgent", + "usage", + "useful", + "usher", + "using", + "usual", + "utensils", + "utility", + "utmost", + "utopia", + "uttered", + "vacation", + "vague", + "vain", + "value", + "vampire", + "vane", + "vapidly", + "vary", + "vastness", + "vats", + "vaults", + "vector", + "veered", + "vegan", + "vehicle", + "vein", + "velvet", + "venomous", + "verification", + "vessel", + "veteran", + "vexed", + "vials", + "vibrate", + "victim", + "video", + "viewpoint", + "vigilant", + "viking", + "village", + "vinegar", + "violin", + "vipers", + "virtual", + "visited", + "vitals", + "vivid", + "vixen", + "vocal", + "vogue", + "voice", + "volcano", + "vortex", + "voted", + "voucher", + "vowels", + "voyage", + "vulture", + "wade", + "waffle", + "wagtail", + "waist", + "waking", + "wallets", + "wanted", + "warped", + "washing", + "water", + "waveform", + "waxing", + "wayside", + "weavers", + "website", + "wedge", + "weekday", + "weird", + "welders", + "went", + "wept", + "were", + "western", + "wetsuit", + "whale", + "when", + "whipped", + "whole", + "wickets", + "width", + "wield", + "wife", + "wiggle", + "wildly", + "winter", + "wipeout", + "wiring", + "wise", + "withdrawn", + "wives", + "wizard", + "wobbly", + "woes", + "woken", + "wolf", + "womanly", + "wonders", + "woozy", + "worry", + "wounded", + "woven", + "wrap", + "wrist", + "wrong", + "yacht", + "yahoo", + "yanks", + "yard", + "yawning", + "yearbook", + "yellow", + "yesterday", + "yeti", + "yields", + "yodel", + "yoga", + "younger", + "yoyo", + "zapped", + "zeal", + "zebra", + "zero", + "zesty", + "zigzags", + "zinger", + "zippers", + "zodiac", + "zombie", + "zones", + "zoom", + ), +) diff --git a/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsLanguage.kt b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsLanguage.kt new file mode 100644 index 0000000..e5fae86 --- /dev/null +++ b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsLanguage.kt @@ -0,0 +1,16 @@ +package net.agorise.shared.stargate.mnemonics + +/** + * Represents a valid language for mnemonics. + * + * @param name The name of the language + * @param nameEnglish The name of the language in english + * @param uniquePrefixLength Number of utf8 chars (not bytes) to use for checksum + * @param words 1626 valid words for mnemonics + */ +data class MnemonicsLanguage( + val name: String, + val nameEnglish: String, + val uniquePrefixLength: Int, + val words: List, +) diff --git a/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsSpanish.kt b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsSpanish.kt new file mode 100644 index 0000000..d07d737 --- /dev/null +++ b/shared/stargate/src/commonMain/kotlin/net/agorise/shared/stargate/mnemonics/MnemonicsSpanish.kt @@ -0,0 +1,1635 @@ +package net.agorise.shared.stargate.mnemonics + +val mnemonicsSpanish = MnemonicsLanguage( + name = "Español", + nameEnglish = "Spanish", + uniquePrefixLength = 4, + words = listOf( + "ábaco", + "abdomen", + "abeja", + "abierto", + "abogado", + "abono", + "aborto", + "abrazo", + "abrir", + "abuelo", + "abuso", + "acabar", + "academia", + "acceso", + "acción", + "aceite", + "acelga", + "acento", + "aceptar", + "ácido", + "aclarar", + "acné", + "acoger", + "acoso", + "activo", + "acto", + "actriz", + "actuar", + "acudir", + "acuerdo", + "acusar", + "adicto", + "admitir", + "adoptar", + "adorno", + "aduana", + "adulto", + "aéreo", + "afectar", + "afición", + "afinar", + "afirmar", + "ágil", + "agitar", + "agonía", + "agosto", + "agotar", + "agregar", + "agrio", + "agua", + "agudo", + "águila", + "aguja", + "ahogo", + "ahorro", + "aire", + "aislar", + "ajedrez", + "ajeno", + "ajuste", + "alacrán", + "alambre", + "alarma", + "alba", + "álbum", + "alcalde", + "aldea", + "alegre", + "alejar", + "alerta", + "aleta", + "alfiler", + "alga", + "algodón", + "aliado", + "aliento", + "alivio", + "alma", + "almeja", + "almíbar", + "altar", + "alteza", + "altivo", + "alto", + "altura", + "alumno", + "alzar", + "amable", + "amante", + "amapola", + "amargo", + "amasar", + "ámbar", + "ámbito", + "ameno", + "amigo", + "amistad", + "amor", + "amparo", + "amplio", + "ancho", + "anciano", + "ancla", + "andar", + "andén", + "anemia", + "ángulo", + "anillo", + "ánimo", + "anís", + "anotar", + "antena", + "antiguo", + "antojo", + "anual", + "anular", + "anuncio", + "añadir", + "añejo", + "año", + "apagar", + "aparato", + "apetito", + "apio", + "aplicar", + "apodo", + "aporte", + "apoyo", + "aprender", + "aprobar", + "apuesta", + "apuro", + "arado", + "araña", + "arar", + "árbitro", + "árbol", + "arbusto", + "archivo", + "arco", + "arder", + "ardilla", + "arduo", + "área", + "árido", + "aries", + "armonía", + "arnés", + "aroma", + "arpa", + "arpón", + "arreglo", + "arroz", + "arruga", + "arte", + "artista", + "asa", + "asado", + "asalto", + "ascenso", + "asegurar", + "aseo", + "asesor", + "asiento", + "asilo", + "asistir", + "asno", + "asombro", + "áspero", + "astilla", + "astro", + "astuto", + "asumir", + "asunto", + "atajo", + "ataque", + "atar", + "atento", + "ateo", + "ático", + "atleta", + "átomo", + "atraer", + "atroz", + "atún", + "audaz", + "audio", + "auge", + "aula", + "aumento", + "ausente", + "autor", + "aval", + "avance", + "avaro", + "ave", + "avellana", + "avena", + "avestruz", + "avión", + "aviso", + "ayer", + "ayuda", + "ayuno", + "azafrán", + "azar", + "azote", + "azúcar", + "azufre", + "azul", + "baba", + "babor", + "bache", + "bahía", + "baile", + "bajar", + "balanza", + "balcón", + "balde", + "bambú", + "banco", + "banda", + "baño", + "barba", + "barco", + "barniz", + "barro", + "báscula", + "bastón", + "basura", + "batalla", + "batería", + "batir", + "batuta", + "baúl", + "bazar", + "bebé", + "bebida", + "bello", + "besar", + "beso", + "bestia", + "bicho", + "bien", + "bingo", + "blanco", + "bloque", + "blusa", + "boa", + "bobina", + "bobo", + "boca", + "bocina", + "boda", + "bodega", + "boina", + "bola", + "bolero", + "bolsa", + "bomba", + "bondad", + "bonito", + "bono", + "bonsái", + "borde", + "borrar", + "bosque", + "bote", + "botín", + "bóveda", + "bozal", + "bravo", + "brazo", + "brecha", + "breve", + "brillo", + "brinco", + "brisa", + "broca", + "broma", + "bronce", + "brote", + "bruja", + "brusco", + "bruto", + "buceo", + "bucle", + "bueno", + "buey", + "bufanda", + "bufón", + "búho", + "buitre", + "bulto", + "burbuja", + "burla", + "burro", + "buscar", + "butaca", + "buzón", + "caballo", + "cabeza", + "cabina", + "cabra", + "cacao", + "cadáver", + "cadena", + "caer", + "café", + "caída", + "caimán", + "caja", + "cajón", + "cal", + "calamar", + "calcio", + "caldo", + "calidad", + "calle", + "calma", + "calor", + "calvo", + "cama", + "cambio", + "camello", + "camino", + "campo", + "cáncer", + "candil", + "canela", + "canguro", + "canica", + "canto", + "caña", + "cañón", + "caoba", + "caos", + "capaz", + "capitán", + "capote", + "captar", + "capucha", + "cara", + "carbón", + "cárcel", + "careta", + "carga", + "cariño", + "carne", + "carpeta", + "carro", + "carta", + "casa", + "casco", + "casero", + "caspa", + "castor", + "catorce", + "catre", + "caudal", + "causa", + "cazo", + "cebolla", + "ceder", + "cedro", + "celda", + "célebre", + "celoso", + "célula", + "cemento", + "ceniza", + "centro", + "cerca", + "cerdo", + "cereza", + "cero", + "cerrar", + "certeza", + "césped", + "cetro", + "chacal", + "chaleco", + "champú", + "chancla", + "chapa", + "charla", + "chico", + "chiste", + "chivo", + "choque", + "choza", + "chuleta", + "chupar", + "ciclón", + "ciego", + "cielo", + "cien", + "cierto", + "cifra", + "cigarro", + "cima", + "cinco", + "cine", + "cinta", + "ciprés", + "circo", + "ciruela", + "cisne", + "cita", + "ciudad", + "clamor", + "clan", + "claro", + "clase", + "clave", + "cliente", + "clima", + "clínica", + "cobre", + "cocción", + "cochino", + "cocina", + "coco", + "código", + "codo", + "cofre", + "coger", + "cohete", + "cojín", + "cojo", + "cola", + "colcha", + "colegio", + "colgar", + "colina", + "collar", + "colmo", + "columna", + "combate", + "comer", + "comida", + "cómodo", + "compra", + "conde", + "conejo", + "conga", + "conocer", + "consejo", + "contar", + "copa", + "copia", + "corazón", + "corbata", + "corcho", + "cordón", + "corona", + "correr", + "coser", + "cosmos", + "costa", + "cráneo", + "cráter", + "crear", + "crecer", + "creído", + "crema", + "cría", + "crimen", + "cripta", + "crisis", + "cromo", + "crónica", + "croqueta", + "crudo", + "cruz", + "cuadro", + "cuarto", + "cuatro", + "cubo", + "cubrir", + "cuchara", + "cuello", + "cuento", + "cuerda", + "cuesta", + "cueva", + "cuidar", + "culebra", + "culpa", + "culto", + "cumbre", + "cumplir", + "cuna", + "cuneta", + "cuota", + "cupón", + "cúpula", + "curar", + "curioso", + "curso", + "curva", + "cutis", + "dama", + "danza", + "dar", + "dardo", + "dátil", + "deber", + "débil", + "década", + "decir", + "dedo", + "defensa", + "definir", + "dejar", + "delfín", + "delgado", + "delito", + "demora", + "denso", + "dental", + "deporte", + "derecho", + "derrota", + "desayuno", + "deseo", + "desfile", + "desnudo", + "destino", + "desvío", + "detalle", + "detener", + "deuda", + "día", + "diablo", + "diadema", + "diamante", + "diana", + "diario", + "dibujo", + "dictar", + "diente", + "dieta", + "diez", + "difícil", + "digno", + "dilema", + "diluir", + "dinero", + "directo", + "dirigir", + "disco", + "diseño", + "disfraz", + "diva", + "divino", + "doble", + "doce", + "dolor", + "domingo", + "don", + "donar", + "dorado", + "dormir", + "dorso", + "dos", + "dosis", + "dragón", + "droga", + "ducha", + "duda", + "duelo", + "dueño", + "dulce", + "dúo", + "duque", + "durar", + "dureza", + "duro", + "ébano", + "ebrio", + "echar", + "eco", + "ecuador", + "edad", + "edición", + "edificio", + "editor", + "educar", + "efecto", + "eficaz", + "eje", + "ejemplo", + "elefante", + "elegir", + "elemento", + "elevar", + "elipse", + "élite", + "elixir", + "elogio", + "eludir", + "embudo", + "emitir", + "emoción", + "empate", + "empeño", + "empleo", + "empresa", + "enano", + "encargo", + "enchufe", + "encía", + "enemigo", + "enero", + "enfado", + "enfermo", + "engaño", + "enigma", + "enlace", + "enorme", + "enredo", + "ensayo", + "enseñar", + "entero", + "entrar", + "envase", + "envío", + "época", + "equipo", + "erizo", + "escala", + "escena", + "escolar", + "escribir", + "escudo", + "esencia", + "esfera", + "esfuerzo", + "espada", + "espejo", + "espía", + "esposa", + "espuma", + "esquí", + "estar", + "este", + "estilo", + "estufa", + "etapa", + "eterno", + "ética", + "etnia", + "evadir", + "evaluar", + "evento", + "evitar", + "exacto", + "examen", + "exceso", + "excusa", + "exento", + "exigir", + "exilio", + "existir", + "éxito", + "experto", + "explicar", + "exponer", + "extremo", + "fábrica", + "fábula", + "fachada", + "fácil", + "factor", + "faena", + "faja", + "falda", + "fallo", + "falso", + "faltar", + "fama", + "familia", + "famoso", + "faraón", + "farmacia", + "farol", + "farsa", + "fase", + "fatiga", + "fauna", + "favor", + "fax", + "febrero", + "fecha", + "feliz", + "feo", + "feria", + "feroz", + "fértil", + "fervor", + "festín", + "fiable", + "fianza", + "fiar", + "fibra", + "ficción", + "ficha", + "fideo", + "fiebre", + "fiel", + "fiera", + "fiesta", + "figura", + "fijar", + "fijo", + "fila", + "filete", + "filial", + "filtro", + "fin", + "finca", + "fingir", + "finito", + "firma", + "flaco", + "flauta", + "flecha", + "flor", + "flota", + "fluir", + "flujo", + "flúor", + "fobia", + "foca", + "fogata", + "fogón", + "folio", + "folleto", + "fondo", + "forma", + "forro", + "fortuna", + "forzar", + "fosa", + "foto", + "fracaso", + "frágil", + "franja", + "frase", + "fraude", + "freír", + "freno", + "fresa", + "frío", + "frito", + "fruta", + "fuego", + "fuente", + "fuerza", + "fuga", + "fumar", + "función", + "funda", + "furgón", + "furia", + "fusil", + "fútbol", + "futuro", + "gacela", + "gafas", + "gaita", + "gajo", + "gala", + "galería", + "gallo", + "gamba", + "ganar", + "gancho", + "ganga", + "ganso", + "garaje", + "garza", + "gasolina", + "gastar", + "gato", + "gavilán", + "gemelo", + "gemir", + "gen", + "género", + "genio", + "gente", + "geranio", + "gerente", + "germen", + "gesto", + "gigante", + "gimnasio", + "girar", + "giro", + "glaciar", + "globo", + "gloria", + "gol", + "golfo", + "goloso", + "golpe", + "goma", + "gordo", + "gorila", + "gorra", + "gota", + "goteo", + "gozar", + "grada", + "gráfico", + "grano", + "grasa", + "gratis", + "grave", + "grieta", + "grillo", + "gripe", + "gris", + "grito", + "grosor", + "grúa", + "grueso", + "grumo", + "grupo", + "guante", + "guapo", + "guardia", + "guerra", + "guía", + "guiño", + "guion", + "guiso", + "guitarra", + "gusano", + "gustar", + "haber", + "hábil", + "hablar", + "hacer", + "hacha", + "hada", + "hallar", + "hamaca", + "harina", + "haz", + "hazaña", + "hebilla", + "hebra", + "hecho", + "helado", + "helio", + "hembra", + "herir", + "hermano", + "héroe", + "hervir", + "hielo", + "hierro", + "hígado", + "higiene", + "hijo", + "himno", + "historia", + "hocico", + "hogar", + "hoguera", + "hoja", + "hombre", + "hongo", + "honor", + "honra", + "hora", + "hormiga", + "horno", + "hostil", + "hoyo", + "hueco", + "huelga", + "huerta", + "hueso", + "huevo", + "huida", + "huir", + "humano", + "húmedo", + "humilde", + "humo", + "hundir", + "huracán", + "hurto", + "icono", + "ideal", + "idioma", + "ídolo", + "iglesia", + "iglú", + "igual", + "ilegal", + "ilusión", + "imagen", + "imán", + "imitar", + "impar", + "imperio", + "imponer", + "impulso", + "incapaz", + "índice", + "inerte", + "infiel", + "informe", + "ingenio", + "inicio", + "inmenso", + "inmune", + "innato", + "insecto", + "instante", + "interés", + "íntimo", + "intuir", + "inútil", + "invierno", + "ira", + "iris", + "ironía", + "isla", + "islote", + "jabalí", + "jabón", + "jamón", + "jarabe", + "jardín", + "jarra", + "jaula", + "jazmín", + "jefe", + "jeringa", + "jinete", + "jornada", + "joroba", + "joven", + "joya", + "juerga", + "jueves", + "juez", + "jugador", + "jugo", + "juguete", + "juicio", + "junco", + "jungla", + "junio", + "juntar", + "júpiter", + "jurar", + "justo", + "juvenil", + "juzgar", + "kilo", + "koala", + "labio", + "lacio", + "lacra", + "lado", + "ladrón", + "lagarto", + "lágrima", + "laguna", + "laico", + "lamer", + "lámina", + "lámpara", + "lana", + "lancha", + "langosta", + "lanza", + "lápiz", + "largo", + "larva", + "lástima", + "lata", + "látex", + "latir", + "laurel", + "lavar", + "lazo", + "leal", + "lección", + "leche", + "lector", + "leer", + "legión", + "legumbre", + "lejano", + "lengua", + "lento", + "leña", + "león", + "leopardo", + "lesión", + "letal", + "letra", + "leve", + "leyenda", + "libertad", + "libro", + "licor", + "líder", + "lidiar", + "lienzo", + "liga", + "ligero", + "lima", + "límite", + "limón", + "limpio", + "lince", + "lindo", + "línea", + "lingote", + "lino", + "linterna", + "líquido", + "liso", + "lista", + "litera", + "litio", + "litro", + "llaga", + "llama", + "llanto", + "llave", + "llegar", + "llenar", + "llevar", + "llorar", + "llover", + "lluvia", + "lobo", + "loción", + "loco", + "locura", + "lógica", + "logro", + "lombriz", + "lomo", + "lonja", + "lote", + "lucha", + "lucir", + "lugar", + "lujo", + "luna", + "lunes", + "lupa", + "lustro", + "luto", + "luz", + "maceta", + "macho", + "madera", + "madre", + "maduro", + "maestro", + "mafia", + "magia", + "mago", + "maíz", + "maldad", + "maleta", + "malla", + "malo", + "mamá", + "mambo", + "mamut", + "manco", + "mando", + "manejar", + "manga", + "maniquí", + "manjar", + "mano", + "manso", + "manta", + "mañana", + "mapa", + "máquina", + "mar", + "marco", + "marea", + "marfil", + "margen", + "marido", + "mármol", + "marrón", + "martes", + "marzo", + "masa", + "máscara", + "masivo", + "matar", + "materia", + "matiz", + "matriz", + "máximo", + "mayor", + "mazorca", + "mecha", + "medalla", + "medio", + "médula", + "mejilla", + "mejor", + "melena", + "melón", + "memoria", + "menor", + "mensaje", + "mente", + "menú", + "mercado", + "merengue", + "mérito", + "mes", + "mesón", + "meta", + "meter", + "método", + "metro", + "mezcla", + "miedo", + "miel", + "miembro", + "miga", + "mil", + "milagro", + "militar", + "millón", + "mimo", + "mina", + "minero", + "mínimo", + "minuto", + "miope", + "mirar", + "misa", + "miseria", + "misil", + "mismo", + "mitad", + "mito", + "mochila", + "moción", + "moda", + "modelo", + "moho", + "mojar", + "molde", + "moler", + "molino", + "momento", + "momia", + "monarca", + "moneda", + "monja", + "monto", + "moño", + "morada", + "morder", + "moreno", + "morir", + "morro", + "morsa", + "mortal", + "mosca", + "mostrar", + "motivo", + "mover", + "móvil", + "mozo", + "mucho", + "mudar", + "mueble", + "muela", + "muerte", + "muestra", + "mugre", + "mujer", + "mula", + "muleta", + "multa", + "mundo", + "muñeca", + "mural", + "muro", + "músculo", + "museo", + "musgo", + "música", + "muslo", + "nácar", + "nación", + "nadar", + "naipe", + "naranja", + "nariz", + "narrar", + "nasal", + "natal", + "nativo", + "natural", + "náusea", + "naval", + "nave", + "navidad", + "necio", + "néctar", + "negar", + "negocio", + "negro", + "neón", + "nervio", + "neto", + "neutro", + "nevar", + "nevera", + "nicho", + "nido", + "niebla", + "nieto", + "niñez", + "niño", + "nítido", + "nivel", + "nobleza", + "noche", + "nómina", + "noria", + "norma", + "norte", + "nota", + "noticia", + "novato", + "novela", + "novio", + "nube", + "nuca", + "núcleo", + "nudillo", + "nudo", + "nuera", + "nueve", + "nuez", + "nulo", + "número", + "nutria", + "oasis", + "obeso", + "obispo", + "objeto", + "obra", + "obrero", + "observar", + "obtener", + "obvio", + "oca", + "ocaso", + "océano", + "ochenta", + "ocho", + "ocio", + "ocre", + "octavo", + "octubre", + "oculto", + "ocupar", + "ocurrir", + "odiar", + "odio", + "odisea", + "oeste", + "ofensa", + "oferta", + "oficio", + "ofrecer", + "ogro", + "oído", + "oír", + "ojo", + "ola", + "oleada", + "olfato", + "olivo", + "olla", + "olmo", + "olor", + "olvido", + "ombligo", + "onda", + "onza", + "opaco", + "opción", + "ópera", + "opinar", + "oponer", + "optar", + "óptica", + "opuesto", + "oración", + "orador", + "oral", + "órbita", + "orca", + "orden", + "oreja", + "órgano", + "orgía", + "orgullo", + "oriente", + "origen", + "orilla", + "oro", + "orquesta", + "oruga", + "osadía", + "oscuro", + "osezno", + "oso", + "ostra", + "otoño", + "otro", + "oveja", + "óvulo", + "óxido", + "oxígeno", + "oyente", + "ozono", + "pacto", + "padre", + "paella", + "página", + "pago", + "país", + "pájaro", + "palabra", + "palco", + "paleta", + "pálido", + "palma", + "paloma", + "palpar", + "pan", + "panal", + "pánico", + "pantera", + "pañuelo", + "papá", + "papel", + "papilla", + "paquete", + "parar", + "parcela", + "pared", + "parir", + "paro", + "párpado", + "parque", + "párrafo", + "parte", + "pasar", + "paseo", + "pasión", + "paso", + "pasta", + "pata", + "patio", + "patria", + "pausa", + "pauta", + "pavo", + "payaso", + "peatón", + "pecado", + "pecera", + "pecho", + "pedal", + "pedir", + "pegar", + "peine", + "pelar", + "peldaño", + "pelea", + "peligro", + "pellejo", + "pelo", + "peluca", + "pena", + "pensar", + "peñón", + "peón", + "peor", + "pepino", + "pequeño", + "pera", + "percha", + "perder", + "pereza", + "perfil", + "perico", + "perla", + "permiso", + "perro", + "persona", + "pesa", + "pesca", + "pésimo", + "pestaña", + "pétalo", + "petróleo", + "pez", + "pezuña", + "picar", + "pichón", + "pie", + "piedra", + "pierna", + "pieza", + "pijama", + "pilar", + "piloto", + "pimienta", + "pino", + "pintor", + "pinza", + "piña", + "piojo", + "pipa", + "pirata", + "pisar", + "piscina", + "piso", + "pista", + "pitón", + "pizca", + "placa", + "plan", + "plata", + "playa", + "plaza", + "pleito", + "pleno", + "plomo", + "pluma", + "plural", + "pobre", + "poco", + "poder", + "podio", + "poema", + "poesía", + "poeta", + "polen", + "policía", + "pollo", + "polvo", + "pomada", + "pomelo", + "pomo", + "pompa", + "poner", + "porción", + "portal", + "posada", + "poseer", + "posible", + "poste", + "potencia", + "potro", + "pozo", + "prado", + "precoz", + "pregunta", + "premio", + "prensa", + "preso", + "previo", + "primo", + "príncipe", + "prisión", + "privar", + "proa", + "probar", + "proceso", + "producto", + "proeza", + "profesor", + "programa", + "prole", + "promesa", + "pronto", + "propio", + "próximo", + "prueba", + "público", + "puchero", + "pudor", + "pueblo", + "puerta", + "puesto", + "pulga", + "pulir", + "pulmón", + "pulpo", + "pulso", + "puma", + "punto", + "puñal", + "puño", + "pupa", + "pupila", + "puré", + "quedar", + "queja", + "quemar", + "querer", + "queso", + "quieto", + "química", + "quince", + "quitar", + "rábano", + "rabia", + "rabo", + "ración", + "radical", + "raíz", + "rama", + "rampa", + "rancho", + "rango", + "rapaz", + "rápido", + "rapto", + "rasgo", + "raspa", + "rato", + "rayo", + "raza", + "razón", + "reacción", + "realidad", + "rebaño", + "rebote", + "recaer", + "receta", + "rechazo", + "recoger", + "recreo", + "recto", + "recurso", + "red", + "redondo", + "reducir", + "reflejo", + "reforma", + "refrán", + "refugio", + "regalo", + "regir", + "regla", + "regreso", + "rehén", + "reino", + "reír", + "reja", + "relato", + "relevo", + "relieve", + "relleno", + "reloj", + "remar", + "remedio", + "remo", + "rencor", + "rendir", + "renta", + "reparto", + "repetir", + "reposo", + "reptil", + "res", + "rescate", + "resina", + "respeto", + "resto", + "resumen", + "retiro", + "retorno", + "retrato", + "reunir", + "revés", + "revista", + "rey", + "rezar", + "rico", + "riego", + "rienda", + "riesgo", + "rifa", + "rígido", + "rigor", + "rincón", + "riñón", + "río", + "riqueza", + "risa", + "ritmo", + "rito", + ) +)