I'm building a mobile app that accepts payments. The user enters their CC details, and the payment info is submitted to a retailer's POS system over HTTPS. The POS processes the payment directly and needs the actual credit card info to work, therefore we cannot use services like Stripe that will store the card, and give us back a token to process payments.
Because of the nature of the app, users will be making regular payments and so I want to store their CC info for convenience. However this is not recurring billing, the user will be initiating the transaction at will. Therefore I don't need to keep CC centrally on a server and I'm considering storing individual cards on each user's device using this method:
- Collect CC number & expiration date
- Encrypt it using AES256, using the CVC as key (CVC not stored)
- Then store the encrypted data in iOS keychain (or equivalent for Android)
- To make a payment, (a) data is taken out of keychain, and
- (b) the user must enter the CVC to decrypt the CC info
The idea being, that if the user knows the CVC they likely possess the card anyway, hence they don't need to try hacking the device.
For encryption, I'm considering using the RNCryptor lib. One of its primary features is the automatic conversion of common passwords to cryptographically 'random' byte sequences of two 256-bit keys, for encryption and authentication. The key stretching is implemented through 10k rounds of PBKDF2. The implementation details are in the link, but in short:
- AES-256 encryption
- CBC mode
- Password stretching with PBKDF2
- Password salting
- Random IV
- Encrypt-then-hash HMAC
Questions:
I don't understand the math well-enough to judge if seeding RNCryptor's key-stretching implementation with just the 3 CVC digits, would produce a statistically random enough key. I haven't been able to find any docs on what password specifications are required for RNCryptor to stay secure. Any thoughts on that would be appreciated. Using this lib is as simple as this:
// Encryption
NSData *data = ...
NSString *password = @"Secret password";
NSData *ciphertext = [RNCryptor encryptData:data password:password];
// Decryption
NSError *error = nil;
NSData *plaintext = [RNCryptor decryptData:ciphertext password:password error:&error];
if (error != nil) {
NSLog(@"ERROR:%@", error);
return
}
// ...
When storying the AES256 encrypted CC info on iOS keychain (or droid equivalent), does it matter if there is no device lock passcode enabled? My thinking is, the info is already AES256 encrypted, it could be stored on the device without keychain's encryption anyway?
What sections of PCI compliance are relevant in this case, given that there is no central server storing lots of CC numbers? I tried reading the PCI specs but the docs are a maze to navigate :(