@@ -15,6 +15,7 @@ public enum BIP39Language {
15
15
case french
16
16
case italian
17
17
case spanish
18
+
18
19
public var words : [ String ] {
19
20
switch self {
20
21
case . english:
@@ -70,49 +71,94 @@ public enum BIP39Language {
70
71
71
72
public class BIP39 {
72
73
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
87
118
}
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 }
88
124
let separator = language. separator
125
+ let wordList = generateMnemonicsFrom ( entropy: entropy)
89
126
return wordList. joined ( separator: separator)
90
127
}
91
128
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
+ }
102
148
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)
103
152
}
104
153
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 }
108
156
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 {
112
159
return nil
113
160
}
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 " )
116
162
bitString. append ( stringForm)
117
163
}
118
164
let stringCount = bitString. count
@@ -131,23 +177,30 @@ public class BIP39 {
131
177
return entropy
132
178
}
133
179
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 {
137
187
return nil
138
188
}
189
+ return dataFrom ( mnemonics: mnemonics, password: password)
190
+ }
191
+
192
+ private static func dataFrom( mnemonics: String , password: String ) -> Data ? {
139
193
guard let mnemData = mnemonics. decomposedStringWithCompatibilityMapping. data ( using: . utf8) else { return nil }
140
194
let salt = " mnemonic " + password
141
195
guard let saltData = salt. decomposedStringWithCompatibilityMapping. data ( using: . utf8) else { return nil }
142
196
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)
145
198
}
146
199
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 {
149
202
return nil
150
203
}
151
- return BIP39 . seedFromMmemonics ( mnemonics, password: password, language: language)
204
+ return seedFromMmemonics ( mnemonics, password: password, language: language)
152
205
}
153
206
}
0 commit comments