FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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