PPOTEncryptionHelper.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. //
  2. // PPOTEncryptionHelper.m
  3. // PayPalOneTouch
  4. //
  5. // Copyright © 2015 PayPal, Inc. All rights reserved.
  6. //
  7. #import "PPOTEncryptionHelper.h"
  8. #import <CommonCrypto/CommonCrypto.h>
  9. #import <CommonCrypto/CommonHMAC.h>
  10. #import "PPOTMacros.h"
  11. @implementation PPOTEncryptionHelper
  12. + (BOOL)compareSignatureData:(NSData *)data1 withData:(NSData *)data2 {
  13. if (data1.length != data2.length) {
  14. return NO;
  15. }
  16. NSInteger result = 0;
  17. uint8_t const *data1Ptr = [data1 bytes];
  18. uint8_t const *data2Ptr = [data2 bytes];
  19. for (unsigned int i = 0; i < data1.length; i++) {
  20. result |= data1Ptr[i] ^ data2Ptr[i];
  21. }
  22. return (result == 0);
  23. }
  24. + (NSData *)randomData:(NSUInteger)length {
  25. NSMutableData *randomKey = [NSMutableData dataWithLength:length];
  26. int error = SecRandomCopyBytes(kSecRandomDefault, length, [randomKey mutableBytes]);
  27. return (error == 0) ? randomKey : nil;
  28. }
  29. + (NSData *)generate256BitKey {
  30. return [PPOTEncryptionHelper randomData:kCCKeySizeAES256];
  31. }
  32. // HMACSHA256
  33. + (NSData *)dataDigest:(NSData *)data encryptionKey:(NSData *)encryptionKey {
  34. NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
  35. CCHmac(kCCHmacAlgSHA256, encryptionKey.bytes, encryptionKey.length, data.bytes, data.length, hash.mutableBytes);
  36. return hash;
  37. }
  38. #pragma mark - AES CTR
  39. + (NSData *)encryptAESCTRData:(NSData *)plainData encryptionKey:(NSData *)key {
  40. if (key.length != kCCKeySizeAES256) {
  41. // wrong key
  42. PPSDKLog(@"encryptAESCTRData: Supplied key is not %@ bytes", @(kCCKeySizeAES256));
  43. return nil;
  44. }
  45. NSData *nonce = [PPOTEncryptionHelper randomData:kCCKeySizeAES128];
  46. NSData *encryptionKey = [key subdataWithRange:NSMakeRange(0, kCCKeySizeAES128)];
  47. NSData *digestKey = [key subdataWithRange:NSMakeRange(16, kCCKeySizeAES128)];
  48. // Init cryptor
  49. CCCryptorRef cryptor = NULL;
  50. CCCryptorStatus status = CCCryptorCreateWithMode(kCCEncrypt,
  51. kCCModeCTR,
  52. kCCAlgorithmAES,
  53. ccNoPadding,
  54. [nonce bytes],
  55. [encryptionKey bytes],
  56. encryptionKey.length,
  57. 0,
  58. 0,
  59. 0,
  60. kCCModeOptionCTR_BE,// deprecated and no longer used
  61. &cryptor);
  62. if (status != kCCSuccess) {
  63. PPSDKLog(@"encryptAESCTRData: createWithMode error: %@", @(status));
  64. return nil;
  65. }
  66. size_t cipherDataSize = plainData.length + kCCKeySizeAES128 +1; // null terminator
  67. uint8_t *cipherData = malloc(cipherDataSize);
  68. memset(cipherData, 0, cipherDataSize);
  69. size_t dataMoved;
  70. // now re-use buffer to do decryption
  71. status = CCCryptorUpdate(cryptor, [plainData bytes], [plainData length], cipherData, cipherDataSize, &dataMoved);
  72. // note: there is no need to call CCCryptorFinal when no padding is used.
  73. CCCryptorRelease(cryptor);
  74. // add logging
  75. if (status != kCCSuccess) {
  76. PPSDKLog(@"encryptAESCTRData: encryption error: %@", @(status));
  77. free(cipherData);
  78. cipherData = NULL;
  79. return nil;
  80. }
  81. // concat nonce and cipher for signing
  82. NSData *encryptedBlob = [NSData dataWithBytes:cipherData length:dataMoved];
  83. free(cipherData);
  84. cipherData = NULL;
  85. NSMutableData *signData = [NSMutableData dataWithCapacity:encryptedBlob.length + nonce.length];
  86. [signData appendData:nonce];
  87. [signData appendData:encryptedBlob];
  88. // sign
  89. NSData *digest = [PPOTEncryptionHelper dataDigest:signData encryptionKey:digestKey];
  90. // construct encrypted payload = signature + nonce + encrypted blob
  91. NSMutableData *payload = [NSMutableData dataWithCapacity:signData.length + digest.length];
  92. [payload appendData:digest];
  93. [payload appendData:signData];
  94. return payload;
  95. }
  96. + (NSData *)decryptAESCTRData:(NSData *)cipherData encryptionKey:(NSData *)key {
  97. if (key.length != kCCKeySizeAES256) {
  98. // wrong key
  99. PPSDKLog(@"decryptAESCTRData: Supplied key is not %@ bytes", @(kCCKeySizeAES256));
  100. return nil;
  101. }
  102. if (cipherData.length < CC_SHA256_DIGEST_LENGTH + kCCKeySizeAES128) {
  103. // we won't be able to decrypt, data sample too small
  104. PPSDKLog(@"decryptAESCTRData: data is too small to decrypt");
  105. return nil;
  106. }
  107. NSData *resultData = nil;
  108. NSData *encryptionKey = [key subdataWithRange:NSMakeRange(0, kCCKeySizeAES128)];
  109. NSData *digestKey = [key subdataWithRange:NSMakeRange(16, kCCKeySizeAES128)];
  110. NSData *signature = [cipherData subdataWithRange:NSMakeRange(0, CC_SHA256_DIGEST_LENGTH)];
  111. NSData *digest = [PPOTEncryptionHelper dataDigest:[cipherData subdataWithRange:NSMakeRange(CC_SHA256_DIGEST_LENGTH, cipherData.length-CC_SHA256_DIGEST_LENGTH)]
  112. encryptionKey:digestKey];
  113. if (![self compareSignatureData:signature withData:digest]) {
  114. PPSDKLog(@"decryptAESCTRData: signature doesn't match");
  115. return nil;
  116. }
  117. NSData *nonce = [cipherData subdataWithRange:NSMakeRange(CC_SHA256_DIGEST_LENGTH, kCCKeySizeAES128)];
  118. // Init cryptor
  119. CCCryptorRef cryptor = NULL;
  120. CCCryptorStatus status = CCCryptorCreateWithMode(kCCDecrypt,
  121. kCCModeCTR,
  122. kCCAlgorithmAES,
  123. ccNoPadding,
  124. [nonce bytes],
  125. [encryptionKey bytes],
  126. encryptionKey.length,
  127. 0,
  128. 0,
  129. 0,
  130. kCCModeOptionCTR_BE,
  131. &cryptor);
  132. if (status != kCCSuccess) {
  133. PPSDKLog(@"decryptAESCTRData: createWithMode error: %@", @(status));
  134. return nil;
  135. }
  136. uint8_t *plainText = malloc(cipherData.length); // that's big enough
  137. memset(plainText, 0, cipherData.length);
  138. const uint8_t *encryptionBlockPtr = [cipherData bytes] + CC_SHA256_DIGEST_LENGTH + kCCKeySizeAES128; // adjust pointer
  139. size_t encryptionBlockSize = cipherData.length - CC_SHA256_DIGEST_LENGTH - kCCKeySizeAES128; // minus signature, minus nonce
  140. // now re-use buffer to do decryption
  141. size_t dataMoved;
  142. status = CCCryptorUpdate(cryptor, encryptionBlockPtr, encryptionBlockSize, plainText, cipherData.length, &dataMoved);
  143. // note: there is no need to call CCCryptorFinal when no padding is used.
  144. CCCryptorRelease(cryptor);
  145. if (status != kCCSuccess) {
  146. PPSDKLog(@"decryptAESCTRData: encryption error: %@", @(status));
  147. free(plainText);
  148. plainText = NULL;
  149. return nil;
  150. }
  151. resultData = [NSData dataWithBytes:plainText length:dataMoved];
  152. free(plainText);
  153. plainText = NULL;
  154. return resultData;
  155. }
  156. #pragma mark - AES Public Key
  157. + (SecKeyRef)createPublicKeyUsingCertificate:(NSData *)certificateData {
  158. SecCertificateRef certificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certificateData);
  159. if (certificate == NULL) {
  160. PPSDKLog(@"createPublicKeyUsingData: failed to create public key");
  161. return NULL;
  162. }
  163. SecPolicyRef policy = SecPolicyCreateBasicX509();
  164. SecTrustRef trustRef = NULL;
  165. SecKeyRef keyRef = NULL;
  166. OSStatus status = SecTrustCreateWithCertificates(certificate, policy, &trustRef);
  167. if (status == errSecSuccess) {
  168. keyRef = SecTrustCopyPublicKey(trustRef);
  169. } else {
  170. PPSDKLog(@"createPublicKeyUsingData: create certificate failed with error %@", @(status));
  171. }
  172. CFRelease(trustRef);
  173. CFRelease(policy);
  174. CFRelease(certificate);
  175. return keyRef;
  176. }
  177. + (NSData *)encryptRSAData:(NSData *)plainData certificate:(NSData *)certificate {
  178. if (![certificate length]) {
  179. PPSDKLog(@"encryptRSAData: no certificate provided");
  180. return nil;
  181. }
  182. SecKeyRef publicKeyRef = [self createPublicKeyUsingCertificate:certificate];
  183. if (!publicKeyRef) {
  184. // logging is done above
  185. return nil;
  186. }
  187. size_t keyBlockSize = SecKeyGetBlockSize(publicKeyRef);
  188. if (plainData.length > keyBlockSize) {
  189. PPSDKLog(@"encryptRSAData: data too big to encrypt");
  190. CFRelease(publicKeyRef);
  191. return nil;
  192. }
  193. size_t cipherTextLen = keyBlockSize;
  194. uint8_t *cipherText = malloc(keyBlockSize);
  195. memset(cipherText, 0, keyBlockSize);
  196. OSStatus status = SecKeyEncrypt(publicKeyRef, kSecPaddingOAEP, [plainData bytes], [plainData length], cipherText, &cipherTextLen);
  197. CFRelease(publicKeyRef);
  198. if (status != errSecSuccess) {
  199. PPSDKLog(@"encryptRSAData: encryption failed with error %@", @(status));
  200. free(cipherText);
  201. return nil;
  202. }
  203. NSData *resultData = [NSData dataWithBytes:cipherText length:cipherTextLen];
  204. free(cipherText);
  205. cipherText = NULL;
  206. return resultData;
  207. }
  208. @end