From c90def520d99c5fa8d610decca1d6d271831ed0a Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu Date: Mon, 1 Apr 2024 01:00:11 +0300 Subject: [PATCH 1/2] fix: BIP39 use given language to generate mnemonics; --- Sources/Web3Core/KeystoreManager/BIP39.swift | 4 ++-- Tests/web3swiftTests/localTests/BIP39Tests.swift | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/BIP39.swift b/Sources/Web3Core/KeystoreManager/BIP39.swift index 7e7314316..e655d1589 100755 --- a/Sources/Web3Core/KeystoreManager/BIP39.swift +++ b/Sources/Web3Core/KeystoreManager/BIP39.swift @@ -6,7 +6,7 @@ import Foundation import CryptoSwift -public enum BIP39Language { +public enum BIP39Language: CaseIterable { case english case chinese_simplified case chinese_traditional @@ -124,7 +124,7 @@ public class BIP39 { public static func generateMnemonicsFromEntropy(entropy: Data, language: BIP39Language = .english) -> String? { guard entropy.count >= 16, entropy.count & 4 == 0 else { return nil } let separator = language.separator - let wordList = generateMnemonicsFrom(entropy: entropy) + let wordList = generateMnemonicsFrom(entropy: entropy, language: language) return wordList.joined(separator: separator) } diff --git a/Tests/web3swiftTests/localTests/BIP39Tests.swift b/Tests/web3swiftTests/localTests/BIP39Tests.swift index e9ccf6410..443286808 100644 --- a/Tests/web3swiftTests/localTests/BIP39Tests.swift +++ b/Tests/web3swiftTests/localTests/BIP39Tests.swift @@ -11,6 +11,22 @@ import XCTest final class BIP39Tests: XCTestCase { + func testAllLanguageMnemonics() throws { + for language in BIP39Language.allCases { + guard let newMnemonics = try BIP39.generateMnemonics(bitsOfEntropy: 128, language: language) else { + XCTFail("Failed to generate BIP39 mnemonics phrase") + return + } + let wordsOfNewMnemonic = newMnemonics.split(separator: language.separator).map { String($0) } + for word in wordsOfNewMnemonic { + guard language.words.contains(word) else { + XCTFail("Given word is not contained in the list of words of selected language available for mnemonics generation: \(word); \(language)") + return + } + } + } + } + func testBIP39() throws { var entropy = Data.fromHex("00000000000000000000000000000000")! var phrase = BIP39.generateMnemonicsFromEntropy(entropy: entropy) From d99c0f67fe6002b8a165d8e24515bfb661fdef83 Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu Date: Mon, 1 Apr 2024 22:07:18 +0300 Subject: [PATCH 2/2] fix: CI failure + new tests for BIP39 --- Sources/Web3Core/KeystoreManager/BIP39.swift | 5 ++ .../localTests/BIP39Tests.swift | 46 ++++++++++++------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/BIP39.swift b/Sources/Web3Core/KeystoreManager/BIP39.swift index e655d1589..7ea57d27f 100755 --- a/Sources/Web3Core/KeystoreManager/BIP39.swift +++ b/Sources/Web3Core/KeystoreManager/BIP39.swift @@ -36,7 +36,12 @@ public enum BIP39Language: CaseIterable { return spanishWords } } + public var separator: String { + return String(separatorCharacter) + } + + public var separatorCharacter: Character { switch self { case .japanese: return "\u{3000}" diff --git a/Tests/web3swiftTests/localTests/BIP39Tests.swift b/Tests/web3swiftTests/localTests/BIP39Tests.swift index 443286808..55b053c9d 100644 --- a/Tests/web3swiftTests/localTests/BIP39Tests.swift +++ b/Tests/web3swiftTests/localTests/BIP39Tests.swift @@ -11,22 +11,6 @@ import XCTest final class BIP39Tests: XCTestCase { - func testAllLanguageMnemonics() throws { - for language in BIP39Language.allCases { - guard let newMnemonics = try BIP39.generateMnemonics(bitsOfEntropy: 128, language: language) else { - XCTFail("Failed to generate BIP39 mnemonics phrase") - return - } - let wordsOfNewMnemonic = newMnemonics.split(separator: language.separator).map { String($0) } - for word in wordsOfNewMnemonic { - guard language.words.contains(word) else { - XCTFail("Given word is not contained in the list of words of selected language available for mnemonics generation: \(word); \(language)") - return - } - } - } - } - func testBIP39() throws { var entropy = Data.fromHex("00000000000000000000000000000000")! var phrase = BIP39.generateMnemonicsFromEntropy(entropy: entropy) @@ -167,6 +151,7 @@ final class BIP39Tests: XCTestCase { XCTAssert(keystore1?.addresses?.first == keystore2?.addresses?.first) } + /// It's expected for the entropy bits count to be [128, 256] and (bits mod 32) must return 0. func testWrongBitsOfEntropyMustThrow() throws { XCTAssertThrowsError(try BIP39.generateMnemonics(entropy: 127)) XCTAssertThrowsError(try BIP39.generateMnemonics(entropy: 255)) @@ -182,4 +167,33 @@ final class BIP39Tests: XCTestCase { XCTAssertFalse(try BIP39.generateMnemonics(entropy: 256).isEmpty) } + func testBip39CorrectWordsCount() throws { + XCTAssertEqual(try BIP39.generateMnemonics(entropy: 128).count, 12) + XCTAssertEqual(try BIP39.generateMnemonics(entropy: 160).count, 15) + XCTAssertEqual(try BIP39.generateMnemonics(entropy: 192).count, 18) + XCTAssertEqual(try BIP39.generateMnemonics(entropy: 224).count, 21) + XCTAssertEqual(try BIP39.generateMnemonics(entropy: 256).count, 24) + } + + func testAllLanguageMnemonics() throws { + for language in BIP39Language.allCases { + let mnemonicPhrase = try BIP39.generateMnemonics(entropy: 128, language: language) + for word in mnemonicPhrase { + guard language.words.contains(word) else { + XCTFail("Given word is not contained in the list of words of selected language available for mnemonics generation: \(word); \(language)") + return + } + } + } + } + + func testBip39MnemonicSeparatorUse() throws { + for language in BIP39Language.allCases { + guard let mnemonicPhrase = try BIP39.generateMnemonics(bitsOfEntropy: 128, language: language) else { + XCTFail("Failed to generate BIP39 mnemonics phrase with 128 bits of entropy using language: \(language)") + return + } + XCTAssertEqual(mnemonicPhrase.split(whereSeparator: { $0 == language.separatorCharacter }).count, 12) + } + } }