NYMnemonic.m 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // NYMnemonic.m
  2. //
  3. // Copyright (c) 2014 Nybex, Inc. (https://nybex.com)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. #import "NYMnemonic.h"
  23. @implementation NYMnemonic
  24. + (NSString *)mnemonicStringFromRandomHexString:(NSString *)seed language:(NSString *)language {
  25. // Convert our hex string to NSData
  26. NSData *seedData = [seed ny_dataFromHexString];
  27. // Calculate the sha256 hash to use with a checksum
  28. NSMutableData *hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
  29. CC_SHA256(seedData.bytes, (int)seedData.length, hash.mutableBytes);
  30. NSMutableArray *checksumBits = [NSMutableArray
  31. arrayWithArray:[[NSData dataWithData:hash] ny_hexToBitArray]];
  32. NSMutableArray *seedBits =
  33. [NSMutableArray arrayWithArray:[seedData ny_hexToBitArray]];
  34. // Append the appropriate checksum bits to the seed
  35. for (int i = 0; i < (int)seedBits.count / 32; i++) {
  36. [seedBits addObject: checksumBits[i]];
  37. }
  38. NSString *path = [NSString stringWithFormat:@"%@/%@.txt", [[NSBundle mainBundle] bundlePath], language];
  39. NSString *fileText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
  40. NSArray *lines = [fileText componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
  41. // Split into groups of 11, and change to numbers
  42. NSMutableArray *words = [NSMutableArray arrayWithCapacity:(int)seedBits.count / 11];
  43. for (int i = 0; i < (int)seedBits.count / 11; i++) {
  44. NSUInteger wordNumber =
  45. strtol(
  46. [[[seedBits subarrayWithRange: NSMakeRange(i * 11, 11)] componentsJoinedByString: @""] UTF8String],
  47. NULL,
  48. 2);
  49. [words addObject: lines[wordNumber]];
  50. }
  51. return [words componentsJoinedByString:@" "];
  52. }
  53. + (NSString *)deterministicSeedStringFromMnemonicString:(NSString *)mnemonic
  54. passphrase:(NSString *)passphrase
  55. language:(NSString *)language {
  56. NSData *data = [mnemonic dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
  57. NSString *dataString = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding];
  58. NSData *normalized = [dataString dataUsingEncoding: NSASCIIStringEncoding allowLossyConversion: NO];
  59. NSData *saltData =
  60. [[@"mnemonic" stringByAppendingString: [[NSString alloc] initWithData:[passphrase dataUsingEncoding: NSASCIIStringEncoding
  61. allowLossyConversion:YES]
  62. encoding:NSASCIIStringEncoding]]
  63. dataUsingEncoding: NSASCIIStringEncoding
  64. allowLossyConversion: NO];
  65. NSMutableData *hashKeyData =
  66. [NSMutableData dataWithLength:CC_SHA512_DIGEST_LENGTH];
  67. CCKeyDerivationPBKDF(kCCPBKDF2, normalized.bytes, normalized.length,
  68. saltData.bytes, saltData.length, kCCPRFHmacAlgSHA512,
  69. 2048, hashKeyData.mutableBytes, hashKeyData.length);
  70. return [[NSData dataWithData:hashKeyData] ny_hexString];
  71. }
  72. + (NSString *)generateMnemonicString:(NSNumber *)strength
  73. language:(NSString *)language {
  74. // Check that the strength is divisible by 32
  75. if ([strength intValue] % 32 != 0) {
  76. [NSException raise:@"Strength must be divisible by 32"
  77. format:@"Strength Was: %@", strength];
  78. }
  79. // Create an array of bytes
  80. NSMutableData *bytes = [NSMutableData dataWithLength: ([strength integerValue]/8)];
  81. // Generate the random data
  82. int status = SecRandomCopyBytes(kSecRandomDefault, bytes.length, bytes.mutableBytes);
  83. // Make sure we were successful
  84. if (status != -1) {
  85. return [self mnemonicStringFromRandomHexString:[bytes ny_hexString] language:language];
  86. } else {
  87. [NSException raise:@"Unable to get random data!"
  88. format:@"Unable to get random data!"];
  89. }
  90. return nil;
  91. }
  92. @end
  93. @implementation NSData (NYMnemonic)
  94. - (NSString *)ny_hexString {
  95. const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
  96. if (!dataBuffer) {
  97. return [NSString string];
  98. }
  99. NSUInteger dataLength = [self length];
  100. NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
  101. for (int i = 0; i < dataLength; ++i) {
  102. [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
  103. }
  104. return [NSString stringWithString:hexString];
  105. }
  106. - (NSArray *)ny_hexToBitArray {
  107. NSMutableArray *bitArray = [NSMutableArray arrayWithCapacity:(int)self.length * 8];
  108. NSString *hexStr = [self ny_hexString];
  109. // Loop over the string and convert each char
  110. for (NSUInteger i = 0; i < [hexStr length]; i++) {
  111. NSString *bin = [self _hexToBinary:[hexStr characterAtIndex:i]];
  112. // Each character will return a string representation of the binary
  113. // Create NSNumbers from each and append to the array.
  114. for (NSInteger j = 0; j < bin.length; j++) {
  115. [bitArray addObject:
  116. @([[NSString stringWithFormat: @"%C", [bin characterAtIndex: j]] intValue])];
  117. }
  118. }
  119. return [NSArray arrayWithArray:bitArray];
  120. }
  121. - (NSString *)_hexToBinary:(unichar)value {
  122. switch (value) {
  123. case '0': return @"0000";
  124. case '1': return @"0001";
  125. case '2': return @"0010";
  126. case '3': return @"0011";
  127. case '4': return @"0100";
  128. case '5': return @"0101";
  129. case '6': return @"0110";
  130. case '7': return @"0111";
  131. case '8': return @"1000";
  132. case '9': return @"1001";
  133. case 'a':
  134. case 'A': return @"1010";
  135. case 'b':
  136. case 'B': return @"1011";
  137. case 'c':
  138. case 'C': return @"1100";
  139. case 'd':
  140. case 'D': return @"1101";
  141. case 'e':
  142. case 'E': return @"1110";
  143. case 'f':
  144. case 'F': return @"1111";
  145. }
  146. return @"-1";
  147. }
  148. @end
  149. @implementation NSString (NYMnemonic)
  150. - (NSData *)ny_dataFromHexString {
  151. const char *chars = [self UTF8String];
  152. int i = 0, len = (int)self.length;
  153. NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
  154. char byteChars[3] = { '\0', '\0', '\0' };
  155. unsigned long wholeByte;
  156. while (i < len) {
  157. byteChars[0] = chars[i++];
  158. byteChars[1] = chars[i++];
  159. wholeByte = strtoul(byteChars, NULL, 16);
  160. [data appendBytes:&wholeByte length:1];
  161. }
  162. return data;
  163. }
  164. @end