FreeRDP
Encryptor.m
1 /*
2  Password Encryptor
3 
4  Copyright 2013 Thincast Technologies GmbH, Author: Dorian Johnson
5 
6  This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7  If a copy of the MPL was not distributed with this file, You can obtain one at
8  http://mozilla.org/MPL/2.0/.
9  */
10 
11 /* We try to use CommonCrypto as much as possible. PBKDF2 was added to CommonCrypto in iOS 5, so use
12  * OpenSSL only as a fallback to do PBKDF2 on pre iOS 5 systems. */
13 
14 #import "Encryptor.h"
15 #import <CommonCrypto/CommonKeyDerivation.h>
16 #import <CommonCrypto/CommonCryptor.h>
17 #import <CommonCrypto/CommonDigest.h>
18 #import <openssl/evp.h> // For PBKDF2 on < 5.0
19 #include <fcntl.h>
20 
21 #pragma mark -
22 
23 @interface Encryptor (Private)
24 - (NSData *)randomInitializationVector;
25 @end
26 
27 @implementation Encryptor
28 @synthesize plaintextPassword = _plaintext_password;
29 
30 - (id)initWithPassword:(NSString *)plaintext_password
31 {
32  if (plaintext_password == nil)
33  return nil;
34 
35  if (!(self = [super init]))
36  return nil;
37 
38  _plaintext_password = [plaintext_password retain];
39  const char *plaintext_password_data =
40  [plaintext_password length] ? [plaintext_password UTF8String] : " ";
41 
42  if (!plaintext_password_data || !strlen(plaintext_password_data))
43  [NSException raise:NSInternalInconsistencyException
44  format:@"%s: plaintext password data is zero length!", __func__];
45 
46  uint8_t *derived_key = calloc(1, TSXEncryptorPBKDF2KeySize);
47 
48  if (CCKeyDerivationPBKDF != NULL)
49  {
50  int ret = CCKeyDerivationPBKDF(
51  kCCPBKDF2, plaintext_password_data, strlen(plaintext_password_data) - 1,
52  (const uint8_t *)TSXEncryptorPBKDF2Salt, TSXEncryptorPBKDF2SaltLen, kCCPRFHmacAlgSHA1,
53  TSXEncryptorPBKDF2Rounds, derived_key, TSXEncryptorPBKDF2KeySize);
54  // NSLog(@"CCKeyDerivationPBKDF ret = %d; key: %@", ret, [NSData
55  // dataWithBytesNoCopy:derived_key length:TWEncryptorPBKDF2KeySize freeWhenDone:NO]);
56 
57  if (ret)
58  {
59  NSLog(@"%s: CCKeyDerivationPBKDF ret == %d, indicating some sort of failure.", __func__,
60  ret);
61  free(derived_key);
62  [self autorelease];
63  return nil;
64  }
65  }
66  else
67  {
68  // iOS 4.x or earlier -- use OpenSSL
69  unsigned long ret = PKCS5_PBKDF2_HMAC_SHA1(
70  plaintext_password_data, (int)strlen(plaintext_password_data) - 1,
71  (const unsigned char *)TSXEncryptorPBKDF2Salt, TSXEncryptorPBKDF2SaltLen,
72  TSXEncryptorPBKDF2Rounds, TSXEncryptorPBKDF2KeySize, derived_key);
73  // NSLog(@"PKCS5_PBKDF2_HMAC_SHA1 ret = %lu; key: %@", ret, [NSData
74  // dataWithBytesNoCopy:derived_key length:TWEncryptorPBKDF2KeySize freeWhenDone:NO]);
75 
76  if (ret != 1)
77  {
78  NSLog(@"%s: PKCS5_PBKDF2_HMAC_SHA1 ret == %lu, indicating some sort of failure.",
79  __func__, ret);
80  free(derived_key);
81  [self release];
82  return nil;
83  }
84  }
85 
86  _encryption_key = [[NSData alloc] initWithBytesNoCopy:derived_key
87  length:TSXEncryptorPBKDF2KeySize
88  freeWhenDone:YES];
89  return self;
90 }
91 
92 #pragma mark -
93 #pragma mark Encrypting/Decrypting data
94 
95 - (NSData *)encryptData:(NSData *)plaintext_data
96 {
97  if (![plaintext_data length])
98  return nil;
99 
100  NSData *iv = [self randomInitializationVector];
101  NSMutableData *encrypted_data = [NSMutableData
102  dataWithLength:[iv length] + [plaintext_data length] + TSXEncryptorBlockCipherBlockSize];
103  [encrypted_data replaceBytesInRange:NSMakeRange(0, [iv length]) withBytes:[iv bytes]];
104 
105  size_t data_out_moved = 0;
106  int ret = CCCrypt(kCCEncrypt, TSXEncryptorBlockCipherAlgo, TSXEncryptorBlockCipherOptions,
107  [_encryption_key bytes], TSXEncryptorBlockCipherKeySize, [iv bytes],
108  [plaintext_data bytes], [plaintext_data length],
109  [encrypted_data mutableBytes] + [iv length],
110  [encrypted_data length] - [iv length], &data_out_moved);
111 
112  switch (ret)
113  {
114  case kCCSuccess:
115  [encrypted_data setLength:[iv length] + data_out_moved];
116  return encrypted_data;
117 
118  default:
119  NSLog(
120  @"%s: uncaught error, ret CCCryptorStatus = %d (plaintext len = %lu; buffer size = "
121  @"%lu)",
122  __func__, ret, (unsigned long)[plaintext_data length],
123  (unsigned long)([encrypted_data length] - [iv length]));
124  return nil;
125  }
126 
127  return nil;
128 }
129 
130 - (NSData *)decryptData:(NSData *)encrypted_data
131 {
132  if ([encrypted_data length] <= TSXEncryptorBlockCipherBlockSize)
133  return nil;
134 
135  NSMutableData *plaintext_data =
136  [NSMutableData dataWithLength:[encrypted_data length] + TSXEncryptorBlockCipherBlockSize];
137  size_t data_out_moved = 0;
138 
139  int ret =
140  CCCrypt(kCCDecrypt, TSXEncryptorBlockCipherAlgo, TSXEncryptorBlockCipherOptions,
141  [_encryption_key bytes], TSXEncryptorBlockCipherKeySize, [encrypted_data bytes],
142  [encrypted_data bytes] + TSXEncryptorBlockCipherBlockSize,
143  [encrypted_data length] - TSXEncryptorBlockCipherBlockSize,
144  [plaintext_data mutableBytes], [plaintext_data length], &data_out_moved);
145 
146  switch (ret)
147  {
148  case kCCSuccess:
149  [plaintext_data setLength:data_out_moved];
150  return plaintext_data;
151 
152  case kCCBufferTooSmall: // Our output buffer is big enough to decrypt valid data. This
153  // return code indicates malformed data.
154  case kCCAlignmentError: // Shouldn't get this, since we're using padding.
155  case kCCDecodeError: // Wrong key.
156  return nil;
157 
158  default:
159  NSLog(@"%s: uncaught error, ret CCCryptorStatus = %d (encrypted data len = %lu; buffer "
160  @"size = %lu; dom = %lu)",
161  __func__, ret, (unsigned long)[encrypted_data length],
162  (unsigned long)[plaintext_data length], data_out_moved);
163  return nil;
164  }
165 
166  return nil;
167 }
168 
169 - (NSData *)encryptString:(NSString *)plaintext_string
170 {
171  return [self encryptData:[plaintext_string dataUsingEncoding:NSUTF8StringEncoding]];
172 }
173 
174 - (NSString *)decryptString:(NSData *)encrypted_string
175 {
176  return [[[NSString alloc] initWithData:[self decryptData:encrypted_string]
177  encoding:NSUTF8StringEncoding] autorelease];
178 }
179 
180 - (NSData *)randomInitializationVector
181 {
182  NSMutableData *iv = [NSMutableData dataWithLength:TSXEncryptorBlockCipherBlockSize];
183  int fd;
184 
185  if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
186  return nil;
187 
188  NSInteger bytes_needed = [iv length];
189  char *p = [iv mutableBytes];
190 
191  while (bytes_needed)
192  {
193  long bytes_read = read(fd, p, bytes_needed);
194 
195  if (bytes_read < 0)
196  continue;
197 
198  p += bytes_read;
199  bytes_needed -= bytes_read;
200  }
201 
202  close(fd);
203  return iv;
204 }
205 
206 @end