123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- // NYMnemonic.m
- //
- // Copyright (c) 2014 Nybex, Inc. (https://nybex.com)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "NYMnemonic.h"
- @implementation NYMnemonic
- + (NSString *)mnemonicStringFromRandomHexString:(NSString *)seed language:(NSString *)language {
- // Convert our hex string to NSData
- NSData *seedData = [seed ny_dataFromHexString];
- // Calculate the sha256 hash to use with a checksum
- NSMutableData *hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
- CC_SHA256(seedData.bytes, (int)seedData.length, hash.mutableBytes);
- NSMutableArray *checksumBits = [NSMutableArray
- arrayWithArray:[[NSData dataWithData:hash] ny_hexToBitArray]];
- NSMutableArray *seedBits =
- [NSMutableArray arrayWithArray:[seedData ny_hexToBitArray]];
- // Append the appropriate checksum bits to the seed
- for (int i = 0; i < (int)seedBits.count / 32; i++) {
- [seedBits addObject: checksumBits[i]];
- }
- NSString *path = [NSString stringWithFormat:@"%@/%@.txt", [[NSBundle mainBundle] bundlePath], language];
- NSString *fileText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
- NSArray *lines = [fileText componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
- // Split into groups of 11, and change to numbers
- NSMutableArray *words = [NSMutableArray arrayWithCapacity:(int)seedBits.count / 11];
- for (int i = 0; i < (int)seedBits.count / 11; i++) {
- NSUInteger wordNumber =
- strtol(
- [[[seedBits subarrayWithRange: NSMakeRange(i * 11, 11)] componentsJoinedByString: @""] UTF8String],
- NULL,
- 2);
- [words addObject: lines[wordNumber]];
- }
- return [words componentsJoinedByString:@" "];
- }
- + (NSString *)deterministicSeedStringFromMnemonicString:(NSString *)mnemonic
- passphrase:(NSString *)passphrase
- language:(NSString *)language {
- NSData *data = [mnemonic dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
- NSString *dataString = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding];
- NSData *normalized = [dataString dataUsingEncoding: NSASCIIStringEncoding allowLossyConversion: NO];
- NSData *saltData =
- [[@"mnemonic" stringByAppendingString: [[NSString alloc] initWithData:[passphrase dataUsingEncoding: NSASCIIStringEncoding
- allowLossyConversion:YES]
- encoding:NSASCIIStringEncoding]]
- dataUsingEncoding: NSASCIIStringEncoding
- allowLossyConversion: NO];
- NSMutableData *hashKeyData =
- [NSMutableData dataWithLength:CC_SHA512_DIGEST_LENGTH];
- CCKeyDerivationPBKDF(kCCPBKDF2, normalized.bytes, normalized.length,
- saltData.bytes, saltData.length, kCCPRFHmacAlgSHA512,
- 2048, hashKeyData.mutableBytes, hashKeyData.length);
- return [[NSData dataWithData:hashKeyData] ny_hexString];
- }
- + (NSString *)generateMnemonicString:(NSNumber *)strength
- language:(NSString *)language {
- // Check that the strength is divisible by 32
- if ([strength intValue] % 32 != 0) {
- [NSException raise:@"Strength must be divisible by 32"
- format:@"Strength Was: %@", strength];
- }
- // Create an array of bytes
- NSMutableData *bytes = [NSMutableData dataWithLength: ([strength integerValue]/8)];
- // Generate the random data
- int status = SecRandomCopyBytes(kSecRandomDefault, bytes.length, bytes.mutableBytes);
- // Make sure we were successful
- if (status != -1) {
- return [self mnemonicStringFromRandomHexString:[bytes ny_hexString] language:language];
- } else {
- [NSException raise:@"Unable to get random data!"
- format:@"Unable to get random data!"];
- }
- return nil;
- }
- @end
- @implementation NSData (NYMnemonic)
- - (NSString *)ny_hexString {
- const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
- if (!dataBuffer) {
- return [NSString string];
- }
- NSUInteger dataLength = [self length];
- NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
- for (int i = 0; i < dataLength; ++i) {
- [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
- }
- return [NSString stringWithString:hexString];
- }
- - (NSArray *)ny_hexToBitArray {
- NSMutableArray *bitArray = [NSMutableArray arrayWithCapacity:(int)self.length * 8];
- NSString *hexStr = [self ny_hexString];
- // Loop over the string and convert each char
- for (NSUInteger i = 0; i < [hexStr length]; i++) {
- NSString *bin = [self _hexToBinary:[hexStr characterAtIndex:i]];
- // Each character will return a string representation of the binary
- // Create NSNumbers from each and append to the array.
- for (NSInteger j = 0; j < bin.length; j++) {
- [bitArray addObject:
- @([[NSString stringWithFormat: @"%C", [bin characterAtIndex: j]] intValue])];
- }
- }
- return [NSArray arrayWithArray:bitArray];
- }
- - (NSString *)_hexToBinary:(unichar)value {
- switch (value) {
- case '0': return @"0000";
- case '1': return @"0001";
- case '2': return @"0010";
- case '3': return @"0011";
- case '4': return @"0100";
- case '5': return @"0101";
- case '6': return @"0110";
- case '7': return @"0111";
- case '8': return @"1000";
- case '9': return @"1001";
- case 'a':
- case 'A': return @"1010";
- case 'b':
- case 'B': return @"1011";
- case 'c':
- case 'C': return @"1100";
- case 'd':
- case 'D': return @"1101";
- case 'e':
- case 'E': return @"1110";
- case 'f':
- case 'F': return @"1111";
- }
- return @"-1";
- }
- @end
- @implementation NSString (NYMnemonic)
- - (NSData *)ny_dataFromHexString {
- const char *chars = [self UTF8String];
- int i = 0, len = (int)self.length;
- NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
- char byteChars[3] = { '\0', '\0', '\0' };
- unsigned long wholeByte;
- while (i < len) {
- byteChars[0] = chars[i++];
- byteChars[1] = chars[i++];
- wholeByte = strtoul(byteChars, NULL, 16);
- [data appendBytes:&wholeByte length:1];
- }
- return data;
- }
- @end
|