MnemonicSwift/Tests/MnemonicSwiftTests.swift

315 lines
14 KiB
Swift
Raw Permalink Normal View History

2018-11-11 19:48:48 -08:00
// Copyright Keefer Taylor, 2018
2020-06-08 14:17:51 -07:00
// Copyright Electric Coin Company, 2020
2021-12-13 06:08:01 -08:00
@testable import MnemonicSwift
2019-01-27 10:54:32 -08:00
import XCTest
2018-10-28 11:48:16 -07:00
2020-06-08 14:17:51 -07:00
class MnemonicSwiftTests: XCTestCase {
/// Indices in the input file.
private let hexRepresentationIndex = 0
private let mnenomicStringIndex = 1
private let deterministicSeedStringIndex = 2
/// Named arrays in the test file
private let englishTestCases = "english"
/// Passphrase
private let passphrase = "TREZOR"
/// Test that MnemonicSwift can generate mnemonic strings from hex representations.
func testGenerateMnemonicFromHex() throws {
guard let vectors = MnemonicSwiftTests.dictionaryFromTestInputFile(),
2021-12-13 06:08:01 -08:00
let testCases = vectors[englishTestCases] as? [[String]] else {
XCTFail("Failed to parse input file.")
return
}
for testCase in testCases {
let expectedMnemonicString = testCase[mnenomicStringIndex]
let hexRepresentation = testCase[hexRepresentationIndex]
let mnemonicString = try Mnemonic.mnemonicString(from: hexRepresentation)
XCTAssertEqual(mnemonicString, expectedMnemonicString)
}
}
/// Test that MnemonicSwift can generate deterministic seed strings strings without a passphrase.
func testGenerateDeterministicSeedStringWithPassphrase() throws {
guard let vectors = MnemonicSwiftTests.dictionaryFromTestInputFile(),
2021-12-13 06:08:01 -08:00
let testCases = vectors[englishTestCases] as? [[String]] else {
XCTFail("Failed to parse input file.")
return
}
for testCase in testCases {
let mnemonicString = testCase[mnenomicStringIndex]
let expectedDeterministicSeedString = testCase[deterministicSeedStringIndex]
XCTAssertNoThrow({
let deterministicSeedString: String = try Mnemonic.deterministicSeedString(from: mnemonicString, passphrase: self.passphrase)
XCTAssertEqual(deterministicSeedString, expectedDeterministicSeedString)
})
}
}
static func dictionaryFromTestInputFile() -> [String: Any]? {
#if SWIFT_PACKAGE
2021-12-13 06:08:01 -08:00
guard let url = Bundle.module.url(forResource: "vectors", withExtension: "json") else {
return nil
}
#else
guard let url = Bundle(for: MnemonicSwiftTests.self).url(forResource: "vectors", withExtension: "json") else {
return nil
}
#endif
do {
let data = try Data(contentsOf: url)
2021-12-13 06:08:01 -08:00
let options: JSONSerialization.ReadingOptions = [.allowFragments, .mutableContainers, .mutableLeaves]
guard let parsedDictionary =
2021-12-13 06:08:01 -08:00
try JSONSerialization.jsonObject(with: data, options: options) as? [String: Any] else {
return nil
}
return parsedDictionary
} catch {
return nil
}
}
/// Test mnemonic generation in english.
func testGenerateMnemonic() {
XCTAssertNoThrow(try Mnemonic.generateMnemonic(strength: 32))
}
/// Prove that functions work in chinese as well.
func testGenerateMnemonicChinese() {
XCTAssertNoThrow(try Mnemonic.generateMnemonic(strength: 32, language: .chinese))
}
/// Test input strengths for mnemonic generation.
func testMnemonicGenerationStrength() {
XCTAssertNoThrow(try {
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 32).components(separatedBy: " ").count, 3)
2021-12-13 06:08:01 -08:00
}())
XCTAssertNoThrow(try {
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 64).components(separatedBy: " ").count, 6)
}())
XCTAssertNoThrow(try {
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 128).components(separatedBy: " ").count, 12)
}())
XCTAssertNoThrow(try {
2021-12-13 06:08:01 -08:00
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 160).components(separatedBy: " ").count, 15)
}())
XCTAssertNoThrow(try {
2021-12-13 06:08:01 -08:00
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 192).components(separatedBy: " ").count, 18)
}())
XCTAssertNoThrow(try {
2021-12-13 06:08:01 -08:00
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 224).components(separatedBy: " ").count, 21)
}())
XCTAssertNoThrow(try {
2021-12-13 06:08:01 -08:00
XCTAssertEqual(try Mnemonic.generateMnemonic(strength: 256).components(separatedBy: " ").count, 24)
}())
}
/// Test valid chinese and english mnemonics are determined to be Invalid.
func testInValidEnglishAndChineseMnemonics() {
let englishMnemonic =
2018-12-09 09:47:34 -08:00
"pear peasant pelican pen pear peasant pelican pen pear peasant pelican pen pear peasant pelican pen"
let chineseMnemonic = "式 扬 它 锦 亦 桥 晋 尼 登 串 焦 五 溶 寿 沿 能 妹 少 旅 冬 乳 承"
2018-11-04 15:17:19 -08:00
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: englishMnemonic))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: chineseMnemonic))
}
2018-11-04 15:17:19 -08:00
/// Test invalid chinese and english mnemonics are determined to be invalid.
func testInvalidEnglishAndChineseMnemonics() {
let englishMnemonic = "slacktivist snacktivity snuggie"
let chineseMnemonic = "亂 語"
2018-11-04 15:17:19 -08:00
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: englishMnemonic))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: chineseMnemonic))
}
2018-11-04 15:17:19 -08:00
/// Test the empty string is determined to be an invalid mnemonic.
func testEmptyStringValidation() {
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: ""))
}
2018-11-04 15:17:19 -08:00
/// Test that strings in an unknown language are determined to be invalid.
func testUnknownLanguageValidation() {
let spanishMnemonic =
2018-12-09 09:47:34 -08:00
"pera campesina pelican pen pera campesina pelican pen pera campesina pelican pen pera campesina pelican pen"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: spanishMnemonic))
}
/// Test that strings of mixed case are determined to be invalid.
func testMixedCaseValidation() {
let mixedCaseMnemonic = "pear PEASANT PeLiCaN pen"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: mixedCaseMnemonic))
}
/// Test mixed language mnemonics.
func testMixedLanguageMnemonicValidation() {
let mixedLanguageMnemonic = "pear peasant pelican pen 路 级 少 图"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: mixedLanguageMnemonic))
}
/// Test that strings padded with whitespace are determined to be valid.
func testWhitespacePaddedValidation() {
let whitespacePaddedMnemonic = " flash tobacco obey genius army stove desk anchor quarter reflect chalk caution\t\t\n"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: whitespacePaddedMnemonic))
XCTAssertNoThrow(try Mnemonic.deterministicSeedString(from: whitespacePaddedMnemonic))
}
/// Test an valid mnemonic generates a seed string.
func testDeterministicSeedStringVisuallyValidButYetInvalidMnemonic() {
let invalidMnemonic =
"pear peasant pelican pen pear peasant pelican pen pear peasant pelican pen pear peasant pelican pen"
XCTAssertThrowsError(try Mnemonic.deterministicSeedString(from: invalidMnemonic))
}
/// Test an invalid mnemonic does not generate a seed string.
func testDeterministicSeedStringInvalidMnemonic() {
let invalidMnemonic =
"MnemonicSwift MnemonicSwift MnemonicSwift MnemonicSwift MnemonicSwift MnemonicSwift MnemonicSwift MnemonicSwift MnemonicSwift"
XCTAssertThrowsError(try Mnemonic.deterministicSeedString(from: invalidMnemonic))
}
func testValidWordCountChinese() {
let mnemonic12 = "针 环 焦 译 脏 密 嘴 土 殿 钠 燕 仰"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: mnemonic12))
let mnemonic15 = "李 凉 暗 均 粒 卖 再 送 绳 勃 窗 丙 洁 危 位"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: mnemonic15))
let mnemonic18 = "蚀 司 档 截 硫 空 激 狱 型 影 湖 钙 柱 控 拜 元 条 扶"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: mnemonic18))
let mnemonic21 = "遭 霉 费 龙 依 固 征 乔 束 锋 盐 芳 走 他 声 废 带 如 套 迹 代"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: mnemonic21))
let mnemonic24 = "式 扬 技 书 它 锦 亦 桥 晋 尼 登 串 焦 五 溶 寿 沿 能 妹 少 旅 冬 乳 承"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: mnemonic24))
}
func testInvalidSmallWordCount() {
let twoWordSeed = "flash tobacco"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: twoWordSeed))
let singleWordSeed = "flash"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: singleWordSeed))
}
func testValidWordCount() {
let twelveWordSeed = "flash tobacco obey genius army stove desk anchor quarter reflect chalk caution"
let fifteenWordSeed = "figure manual hunt oil unusual outer flee yellow cable bottom uncle okay deputy witness fire"
let eighteenWordSeed = "cost raccoon apple hill success sight bag harvest lawsuit exact snow police camp faith weather squirrel defy dry"
let twentyOneWordSeed = "beach allow aim neglect phone boring horror venture door crouch ecology tent bulb oval culture hat half easily crucial horse heart"
let twentyFourWordSeed = "vanish dream art asset response click orphan patch property owner lawsuit sweet smoke bicycle grunt sentence dish tribe review soap chief soft bone race"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: twelveWordSeed), "error validating 12 word seed")
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: fifteenWordSeed), "error validating 15 word seed")
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: eighteenWordSeed), "error validating 18 word seed")
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: twentyOneWordSeed), "error validating 21 word seed")
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: twentyFourWordSeed), "error validating 24 word seed")
}
func testInvalidWordCount() {
let elevenWordSeed = "flash tobacco obey genius army stove desk anchor quarter reflect chalk"
let fourteenWordSeed = "figure manual hunt oil unusual outer flee yellow cable bottom uncle okay deputy witness"
let seventeenWordSeed = "cost raccoon apple hill success sight bag harvest lawsuit exact snow police camp faith weather squirrel defy"
let twentyWordSeed = "beach aim neglect phone boring horror venture door crouch ecology tent bulb oval culture hat half easily crucial horse heart"
let twentythreeWordSeed = "vanish dream art response click orphan patch property owner lawsuit sweet smoke bicycle grunt sentence dish tribe review soap chief soft bone race"
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: elevenWordSeed))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: fourteenWordSeed))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: seventeenWordSeed))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: twentyWordSeed))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: twentythreeWordSeed))
}
func testApparentlyValidSeedPhraseWithUppercaseCharacter() throws {
let x = "human pulse approve subway climb stairs mind gentle raccoon warfare fog roast sponsor under absorb spirit hurdle animal original honey owner upper empower describe"
let y = "Human pulse approve subway climb stairs mind gentle raccoon warfare fog roast sponsor under absorb spirit hurdle animal original honey owner upper empower describe"
XCTAssertNoThrow(try Mnemonic.validate(mnemonic: x))
XCTAssertThrowsError(try Mnemonic.validate(mnemonic: y))
XCTAssertNoThrow(try Mnemonic.deterministicSeedBytes(from: x))
XCTAssertThrowsError(try Mnemonic.deterministicSeedBytes(from: y))
2021-12-13 06:08:01 -08:00
XCTAssertEqual(
try Mnemonic.deterministicSeedBytes(from: x),
try Mnemonic.deterministicSeedBytes(from: " " + x)
)
XCTAssertEqual(
try Mnemonic.deterministicSeedBytes(from: x),
try Mnemonic.deterministicSeedBytes(from: x + "\n")
)
}
func testSwapTwoWords() {
let phrase = "human pulse approve subway climb stairs mind gentle raccoon warfare fog roast sponsor under absorb spirit hurdle animal original honey owner upper empower describe"
var x = phrase.components(separatedBy: " ")
let i = Int.random(in: 1 ..< x.count)
let j = Int.random(in: 0 ..< i)
x.swapAt(i, j)
let swappedPhrase = x.joined(separator: " ")
XCTAssertNoThrow(try Mnemonic.deterministicSeedBytes(from: phrase))
XCTAssertThrowsError(try Mnemonic.deterministicSeedBytes(from: swappedPhrase))
}
func testBitStringArrayToData() {
2021-12-13 06:08:01 -08:00
let validBitString = "10000000" + "00010000" + "00001111" + "11110000"
let uints: [UInt8] = [128, 16, 15, 240]
let expectedDataArray = Data(uints)
let result = validBitString.bitStringToBytes()
XCTAssertNotNil(result)
XCTAssertEqual(result, expectedDataArray)
}
func testBitStringArrayToDataFailsOnIncorrectString() {
2021-12-13 06:08:01 -08:00
let bendersDream = "10000000" + "00010000" + "00020111" + "11110000"
let result = bendersDream.bitStringToBytes()
XCTAssertNil(result)
}
func testInvalidMnemonicData() {
let validMnemonicData = "f1f1f1"
let invalidMnemonicData = "f1f1f1f"
let veryInvalidMnemonicData = "f1f1g1"
let expectedData = Data([0xf1, 0xf1, 0xf1])
XCTAssertEqual(validMnemonicData.mnemonicData(), expectedData)
XCTAssertEqual(invalidMnemonicData.mnemonicData(), nil)
XCTAssertEqual(veryInvalidMnemonicData.mnemonicData(), nil)
}
func testPad() {
// it should pad to givesize when size is less that target size
XCTAssertEqual("0".pad(toSize: 8), "00000000")
XCTAssertEqual("1".pad(toSize: 8), "00000001")
XCTAssertEqual("10".pad(toSize: 8), "00000010")
XCTAssertEqual("10".pad(toSize: 8), "00000010")
// it should not pad when size of the given string is same as target size
XCTAssertEqual("00000010".pad(toSize: 8), "00000010")
// it should n ot pad whe size of the given string is greater that target size
XCTAssertEqual("100000010".pad(toSize: 8), "100000010")
}
2018-10-28 11:48:16 -07:00
}