@@ -15,6 +15,7 @@ public enum BIP39Language {
1515 case french
1616 case italian
1717 case spanish
18+
1819 public var words : [ String ] {
1920 switch self {
2021 case . english:
@@ -70,49 +71,94 @@ public enum BIP39Language {
7071
7172public class BIP39 {
7273
73- static public func generateMnemonicsFromEntropy( entropy: Data , language: BIP39Language = BIP39Language . english) -> String ? {
74- guard entropy. count >= 16 , entropy. count & 4 == 0 else { return nil }
75- let checksum = entropy. sha256 ( )
76- let checksumBits = entropy. count*8/ 32
77- var fullEntropy = Data ( )
78- fullEntropy. append ( entropy)
79- fullEntropy. append ( checksum [ 0 ..< ( checksumBits+ 7 ) / 8 ] )
80- var wordList = [ String] ( )
81- for i in 0 ..< fullEntropy. count*8/ 11 {
82- guard let bits = fullEntropy. bitsInRange ( i*11, 11 ) else { return nil }
83- let index = Int ( bits)
84- guard language. words. count > index else { return nil }
85- let word = language. words [ index]
86- wordList. append ( word)
74+ /// Generates a mnemonic phrase length of which depends on the provided `bitsOfEntropy`.
75+ /// Returned value is a single string where words are joined by ``BIP39Language/separator``.
76+ /// Keep in mind that different languages may have different separators.
77+ /// - Parameters:
78+ /// - bitsOfEntropy: 128 - 12 words, 160 - 15 words, and up to 256 - 24 words as output. The value must be a multiple of 32.
79+ /// - language: words language, default is set to english.
80+ /// - Returns: mnemonic phrase as a single string containing 12, 15, 18, 21 or 24 words.
81+ public static func generateMnemonics( bitsOfEntropy: Int , language: BIP39Language = . english) throws -> String ? {
82+ let entropy = try entropyOf ( size: bitsOfEntropy)
83+ return generateMnemonicsFromEntropy ( entropy: entropy, language: language)
84+ }
85+
86+ /// Generates a mnemonic phrase length of which depends on the provided `entropy`.
87+ /// - Parameters:
88+ /// - entropy: 128 - 12 words, 192 - 18 words, 256 - 24 words in output.
89+ /// - language: words language, default is set to english.
90+ /// - Returns: mnemonic phrase as an array containing 12, 15, 18, 21 or 24 words.
91+ /// `nil` is returned in cases like wrong `entropy` value (e.g. `entropy` is not a multiple of 32).
92+ public static func generateMnemonics( entropy: Int , language: BIP39Language = . english) throws -> [ String ] {
93+ let entropy = try entropyOf ( size: entropy)
94+ return generateMnemonicsFrom ( entropy: entropy, language: language)
95+ }
96+
97+ private static func entropyOf( size: Int ) throws -> Data {
98+ guard
99+ size >= 128 && size <= 256 && size. isMultiple ( of: 32 ) ,
100+ let entropy = Data . randomBytes ( length: size/ 8 )
101+ else {
102+ throw AbstractKeystoreError . noEntropyError
103+ }
104+ return entropy
105+ }
106+
107+ static func bitarray( from data: Data ) -> String {
108+ data. map {
109+ let binary = String ( $0, radix: 2 )
110+ let padding = String ( repeating: " 0 " , count: 8 - binary. count)
111+ return padding + binary
112+ } . joined ( )
113+ }
114+
115+ static func generateChecksum( entropyBytes inputData: Data , checksumLength: Int ) -> String ? {
116+ guard let checksumData = inputData. sha256 ( ) . bitsInRange ( 0 , checksumLength) else {
117+ return nil
87118 }
119+ return String ( checksumData, radix: 2 ) . leftPadding ( toLength: checksumLength, withPad: " 0 " )
120+ }
121+
122+ public static func generateMnemonicsFromEntropy( entropy: Data , language: BIP39Language = . english) -> String ? {
123+ guard entropy. count >= 16 , entropy. count & 4 == 0 else { return nil }
88124 let separator = language. separator
125+ let wordList = generateMnemonicsFrom ( entropy: entropy)
89126 return wordList. joined ( separator: separator)
90127 }
91128
92- /// Initializes a new mnemonics set with the provided bitsOfEntropy.
93- /// - Parameters:
94- /// - bitsOfEntropy: 128 - 12 words, 192 - 18 words , 256 - 24 words in output.
95- /// - language: words language, default english
96- /// - Returns: random 12-24 words, that represent new Mnemonic phrase.
97- static public func generateMnemonics( bitsOfEntropy: Int , language: BIP39Language = BIP39Language . english) throws -> String ? {
98- guard bitsOfEntropy >= 128 && bitsOfEntropy <= 256 && bitsOfEntropy. isMultiple ( of: 32 ) else { return nil }
99- guard let entropy = Data . randomBytes ( length: bitsOfEntropy/ 8 ) else { throw AbstractKeystoreError . noEntropyError}
100- return BIP39 . generateMnemonicsFromEntropy ( entropy: entropy, language:
101- language)
129+ public static func generateMnemonicsFrom( entropy: Data , language: BIP39Language = . english) -> [ String ] {
130+ let entropyBitSize = entropy. count * 8
131+ let checksum_length = entropyBitSize / 32
132+
133+ var entropy_bits = bitarray ( from: entropy)
134+
135+ guard let checksumTest = generateChecksum ( entropyBytes: entropy, checksumLength: checksum_length) else {
136+ return [ ]
137+ }
138+ entropy_bits += checksumTest
139+ return entropy_bits
140+ . split ( intoChunksOf: 11 )
141+ . compactMap { binary in
142+ Int ( binary, radix: 2 )
143+ }
144+ . map { index in
145+ language. words [ index]
146+ }
147+ }
102148
149+ public static func mnemonicsToEntropy( _ mnemonics: String , language: BIP39Language = . english) -> Data ? {
150+ let wordList = mnemonics. components ( separatedBy: language. separator)
151+ return mnemonicsToEntropy ( wordList, language: language)
103152 }
104153
105- static public func mnemonicsToEntropy( _ mnemonics: String , language: BIP39Language = BIP39Language . english) -> Data ? {
106- let wordList = mnemonics. components ( separatedBy: " " )
107- guard wordList. count >= 12 && wordList. count. isMultiple ( of: 3 ) && wordList. count <= 24 else { return nil }
154+ public static func mnemonicsToEntropy( _ mnemonics: [ String ] , language: BIP39Language = . english) -> Data ? {
155+ guard 12 ... 24 ~= mnemonics. count && mnemonics. count. isMultiple ( of: 3 ) else { return nil }
108156 var bitString = " "
109- for word in wordList {
110- let idx = language. words. firstIndex ( of: word)
111- if idx == nil {
157+ for word in mnemonics {
158+ guard let idx = language. words. firstIndex ( of: word) else {
112159 return nil
113160 }
114- let idxAsInt = language. words. startIndex. distance ( to: idx!)
115- let stringForm = String ( UInt16 ( idxAsInt) , radix: 2 ) . leftPadding ( toLength: 11 , withPad: " 0 " )
161+ let stringForm = String ( UInt16 ( idx) , radix: 2 ) . leftPadding ( toLength: 11 , withPad: " 0 " )
116162 bitString. append ( stringForm)
117163 }
118164 let stringCount = bitString. count
@@ -131,23 +177,30 @@ public class BIP39 {
131177 return entropy
132178 }
133179
134- static public func seedFromMmemonics( _ mnemonics: String , password: String = " " , language: BIP39Language = BIP39Language . english) -> Data ? {
135- let valid = BIP39 . mnemonicsToEntropy ( mnemonics, language: language) != nil
136- if !valid {
180+ public static func seedFromMmemonics( _ mnemonics: [ String ] , password: String = " " , language: BIP39Language = . english) -> Data ? {
181+ let wordList = mnemonics. joined ( separator: language. separator)
182+ return seedFromMmemonics ( wordList, password: password, language: language)
183+ }
184+
185+ public static func seedFromMmemonics( _ mnemonics: String , password: String = " " , language: BIP39Language = . english) -> Data ? {
186+ guard mnemonicsToEntropy ( mnemonics, language: language) != nil else {
137187 return nil
138188 }
189+ return dataFrom ( mnemonics: mnemonics, password: password)
190+ }
191+
192+ private static func dataFrom( mnemonics: String , password: String ) -> Data ? {
139193 guard let mnemData = mnemonics. decomposedStringWithCompatibilityMapping. data ( using: . utf8) else { return nil }
140194 let salt = " mnemonic " + password
141195 guard let saltData = salt. decomposedStringWithCompatibilityMapping. data ( using: . utf8) else { return nil }
142196 guard let seedArray = try ? PKCS5 . PBKDF2 ( password: mnemData. bytes, salt: saltData. bytes, iterations: 2048 , keyLength: 64 , variant: HMAC . Variant. sha2 ( . sha512) ) . calculate ( ) else { return nil }
143- let seed = Data ( seedArray)
144- return seed
197+ return Data ( seedArray)
145198 }
146199
147- static public func seedFromEntropy( _ entropy: Data , password: String = " " , language: BIP39Language = BIP39Language . english) -> Data ? {
148- guard let mnemonics = BIP39 . generateMnemonicsFromEntropy ( entropy: entropy, language: language) else {
200+ public static func seedFromEntropy( _ entropy: Data , password: String = " " , language: BIP39Language = . english) -> Data ? {
201+ guard let mnemonics = generateMnemonicsFromEntropy ( entropy: entropy, language: language) else {
149202 return nil
150203 }
151- return BIP39 . seedFromMmemonics ( mnemonics, password: password, language: language)
204+ return seedFromMmemonics ( mnemonics, password: password, language: language)
152205 }
153206}
0 commit comments