AES decryption in iOS: PKCS5 padding and CBC - ios

I am implementing for iOS some decryption code for a message originating on a server over which I have no control. A previous implementation on another platform documents the decryption requirements AES256, specifies the key and the initialization vector, and also says:
* Cipher Mode: CBC
* Padding: PKCS5Padding
The options for creation of a CCCryptor object include only kCCOptionPKCS7Padding and kCCOptionECBMode, noting that CBC is the default. From what what I understand about padding for encryption, I don't understand how one might use both; I thought they were mutually exclusive. In creating a CCCryptor for the decryption, I have tried using both a 0 for options and kCCOptionPKCS7Padding, but both give me gibberish after decryption.
I have compared the dump of this decryption with a dump of the decoded byte buffer on the other platform and confirmed that they really are different. So there is something that I am doing different in this implementation that is significantly different, I just don't know what... And don't have a clue as to how to get a handle on it. The platforms are different enough that it is difficult to infer much from the previous implementation because it is based on a very different platform. And of course, the author of the previous implementation has since departed.
Any guesses what else could be incompatible or how to troubleshoot this thing?

PKCS#5 padding and PKCS#7 padding are practically the same (adding bytes 01, or 0202, or 0303 etc up to the length of the block size of the algorithm, 16 bytes in this case). Officially PKCS#5 padding should only be used for 8 byte blocks, but in many runtimes the two can be interchanged without issue. Padding always occurs at the end of the ciphertext, so if you get just gibberish it's not the padding. ECB is a block mode of operation (that should not be used to encrypt data that can be distinguished from random numbers) : it would require padding, so the two are not mutually exclusive.
Finally, if you just perform decryption (not MAC'ing or other forms of integrity control), and you return the result of the unpadding to the server (decryption failed), your plain text data is not safe because of padding oracle attacks.

First, you can worry about the padding later. Providing 0 like you have done means AES CBC with no padding, and with that configuration you should see your message just fine. Albiet potentially with some padding bytes on the end. So that leaves:
You're not loading the key correctly.
You're not loading the IV correctly.
You're not loading the data correctly.
The server is doing something you don't expect.
To debug this, you need to isolate your system. You can do this by implementing a loopback test where you both encrypt and then decrypt the data to make sure you're loading everything correctly. But that can be misleading. Even if you do something wrong (e.g., loading the key backwards), you could still be able to decrypt what you've encrypted because you're doing it exactly the same wrong way on both sides.
So you need to test against Known Answer Tests (KATs). You can look up the official KATs on the AES wikipedia entry. But it just so happens that I have posted another answer here on SO that we can use.
Given this input:
KEY: 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
IV: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
PLAIN TEXT: encrypt me
CIPHER TEXT: 338d2a9e28208cad84c457eb9bd91c81
Verify with a third-party program that you can decrypt the cipher text and get the plain text.
$ echo -n "encrypt me" > to_encrypt
$ openssl enc -in to_encrypt -out encrypted -e -aes-256-cbc \
> -K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
> -iv 0000000000000000
$ hexdump -C encrypted
00000000 33 8d 2a 9e 28 20 8c ad 84 c4 57 eb 9b d9 1c 81 |3.*.( ....W.....|
00000010
$ openssl enc -in encrypted -out plain_text -d -aes-256-cbc \
> -K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
> -iv 0000000000000000
$ hexdump -C plain_text
00000000 65 6e 63 72 79 70 74 20 6d 65 |encrypt me|
0000000a
So now try to decrypt this known-answer test in your program. Be sure to enable PKCS7 padding, because that's what I used in this example. As an exercise, decrypt it with no padding and see that the result is the same, except you have padding bytes after the "encrypt me" text.
Implementing the KAT is a big step. It says that your implementation correct, but your assumptions on the server's behavior is wrong. And then it's time to start questioning those assumptions...
(And P.S., those options you mentioned are not mutually exclusive. ECB means no IV, and CBC means you have an IV. No relation to padding.)
OK, I know I said it's an exercise, but I want to prove that even if you encrypt with padding
and decrypt without padding, you do not get garbage. So given the KAT that used PKCS7 padding, we decrypt it with the no padding option and get a readable message followed by 06 used as a padding byte.
$ openssl enc -in encrypted -out plain_text -d -aes-256-cbc \
-K 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \
-iv 0000000000000000 -nopad
$ hexdump -C plain_text
00000000 65 6e 63 72 79 70 74 20 6d 65 06 06 06 06 06 06 |encrypt me......|
00000010
$

Paul,
The PKCS#5 padding is needed to identify padding in the decrypted data. For CBC, the input buffer must be a multiple of the cipher block size (16 for AES). For that reason, the buffer to be encrypted is extended with additional bytes. Note that after encryption, the original size of the data is lost. PKCS#5 padding allows to retrieve that size. This is done by filling the extended data buffer with repeated bytes, with value equal to the padding size. e.g if your cleartext buffer was 12 bytes, to make it multiple of 16, you will need to add 4 bytes more. (If the data was 16, you will add 16 more to make it 32). Then you fill those 4 bytes with '0x4' to conform with PKCS#5 padding. When you decrypt, simply look for the last byte in the decrypted data and subtract that number from the length of the decrypted buffer.
What you are doing is padding with '0's. Although you seem to be happy to see the results, you will get a surprise when your original data ends in one of more '0's.

It turns out that the explanation for the what I was experiencing was embarrassingly simple: I misinterpreted something I read in the previous implementation to imply that it was using a 256-bit key, but in fact it was using a 128-bit key. Make that change and all of the sudden what was obscure becomes cleartext. :-)
0 for the options argument, to invoke CBC, was in fact correct. What the reference to PKCS5 padding in the previous implementation is still mysterious, but that doesn't matter because because what I've got now works.
Thanks for the shot, indiv.

Related

Little Endian vs. Big Endian architectures

I've a question it is a kind of disagreement with a university professor, about Endianness, So I did not find any mean to solve this and find the right answer but asking and open a discussion in Stack Overflow community.
Let's say that we have this number (hex)11FF1 defined as an integer, for example in C++ it will be like : int num = 0x11FF1, and I say that the number will be presented in the memory in a little endian machine as :
addr[0] is f1 addr[1] is 1f addr[2] is 01 addr[3] is 00
in binary : 1111 0001 0001 1111 0000 0001 0000 0000
as the compiler considers 0x11ff1 as 0x0001ff1 and considers also 00 as the 1st byte and 01 as the 2nd byte and so on, and for the Big Endian I believe it will look like:
addr[0] is 00 addr[1] is 01 addr[2] is 1f addr[3] is f1
in binary : 0000 0000 0000 0001 0001 1111 1111 0001
but he has another opinion, he says :
Little Endian
Big Endian:
Actually I don't see anything logical in his representation, so I hope the developers Resolve this disagreement, Thanks in advance.
Your hex and binary numbers are correct.
Your (professor's?) French image for little-endian makes no sense at all, none of the 3 representations are consistent with either of the other 2.
73713 is 0x11ff1 in hex, so there aren't any 0xFF bytes (binary 11111111).
In 32-bit little-endian, the bytes are F1 1F 01 00 in order of increasing memory address.
You can get that by taking pairs of hex digits (bytes / octets) from the low end of the full hex value, then fill with zeros once you've consumed the value.
It looks like they maybe padded the wrong side of the hex value with 0s to zero-extend to 32 bits as 0x11ff1000, not 0x00011ff1. Note these are full hex values of the whole number, not an attempt to break it down into separate hex bytes in any order.
But the hex and binary don't match each other; their binary ends with an all-ones byte, so it has FF as the high byte, not the 3rd byte. I didn't check if that matches their hex in PDP (mixed) endian.
They broke up their hex column into 4 byte-sized groups, which would seem to indicate that it's showing bytes in memory order. But that column is the same between their big- and little-endian images, so apparently that's not what they're doing, and they really did just extend it to 32 bits by left shifting (padding with low instead of high zero).
Also, the binary field in the big vs. little endian aren't the reverse of each other. To flip from big to little endian, you reverse the order of the bytes within the integer, keeping each byte value the same. (like x86 bswap). Their 11111111 (FF) byte is 2nd in their big-endian version, but last in little-endian.
TL:DR: unfortunately, nothing about those images makes any sense that I can see.

Backend iOS iap receipt validation without /verifyReceipt request

I want to validate iOS in-app purchase receipts in my backend code.
Apple's design decision to do this using an external /verifyReceipt request is plain down stupid: it incurs latency and adds complexity of network error handling. Even more so, the data in the receipt looks like it can be public key verified.
After a bit of analysis on the signature field of a receipt, it seems to contain a PK-verified SHA1 hash:
<?php
$sig="ApxQMks+KAE0riYtKjNNwhNeuGQ6R98X223zCh60s9m8wloydP3sCceQdzrCwd/3N1L+dlefT7ZJUiquCEsDAo+Rh54eSovcKEk+2RZyoP/zRQHgTF81kYBIbkFCADhj6kzJVr1rYsRXKpOJk6qWMYPz+a90XJfGtnIDuHlRb4V5AAADVzCCA1MwggI7oAMCAQICCGUUkU3ZWAS1MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MDYxNTIyMDU1NloXDTE0MDYxNDIyMDU1NlowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrRjF2ct4IrSdiTChaI0g8pwv/cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ==";
file_put_contents('sig', substr(base64_decode($sig),1,128));
file_put_contents('cert.der', substr(base64_decode($sig),133));
# show certificate
echo `openssl x509 -in cert.der -inform der -noout -text` . "\n\n";
# convert to pem
`openssl x509 -in cert.der -inform der -out cert.pem`;
echo "signature:\n";
echo `openssl rsautl -in sig -verify -asn1parse -inkey cert.pem -certin`;
echo "\n\n";
Output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
65:14:91:4d:d9:58:04:b5
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple iTunes Store Certification Authority
Validity
Not Before: Jun 15 22:05:56 2009 GMT
Not After : Jun 14 22:05:56 2014 GMT
Subject: CN=PurchaseReceiptCertificate, OU=Apple iTunes Store, O=Apple Inc., C=US
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:ca:d1:8c:5d:9c:b7:82:2b:49:d8:93:0a:16:88:
d2:0f:29:c2:ff:dc:98:7b:3c:a7:f4:70:57:fa:ed:
ff:dd:57:29:58:4d:97:85:c8:06:29:8a:8d:04:0d:
7e:01:ec:0e:ce:b2:8e:ef:be:0e:b2:89:13:bb:8a:
b2:98:4c:75:d2:98:7c:51:39:ac:65:ec:01:d0:44:
8c:1c:11:23:17:b1:4d:eb:f1:dc:72:c4:14:60:2d:
d6:6a:0a:c7:9d:3d:76:1c:6d:88:87:43:80:9b:f6:
61:a5:6c:e3:d0:74:b8:9b:cd:77:2e:92:32:a3:4d:
2c:7b:03:2f:30:d2:f6:86:47
Exponent: 65537 (0x10001)
--- cut ---
signature:
0:d=0 hl=2 l= 33 cons: SEQUENCE
2:d=1 hl=2 l= 9 cons: SEQUENCE
4:d=2 hl=2 l= 5 prim: OBJECT :sha1
11:d=2 hl=2 l= 0 prim: NULL
13:d=1 hl=2 l= 20 prim: OCTET STRING
0000 - b7 ef f1 9e 01 2a dd 26-09 38 cd ce 63 5b b1 32 .....*.&.8..c[.2
0010 - 88 51 17 0a .Q..
The question now remains of which data this is a hash. The above contains an actual signature of this receipt:
{
"signature" = "ApxQMks+KAE0riYtKjNNwhNeuGQ6R98X223zCh60s9m8wloydP3sCceQdzrCwd/3N1L+dlefT7ZJUiquCEsDAo+Rh54eSovcKEk+2RZyoP/zRQHgTF81kYBIbkFCADhj6kzJVr1rYsRXKpOJk6qWMYPz+a90XJfGtnIDuHlRb4V5AAADVzCCA1MwggI7oAMCAQICCGUUkU3ZWAS1MA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MDYxNTIyMDU1NloXDTE0MDYxNDIyMDU1NlowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMrRjF2ct4IrSdiTChaI0g8pwv/cmHs8p/RwV/rt/91XKVhNl4XIBimKjQQNfgHsDs6yju++DrKJE7uKsphMddKYfFE5rGXsAdBEjBwRIxexTevx3HLEFGAt1moKx509dhxtiIdDgJv2YaVs49B0uJvNdy6SMqNNLHsDLzDS9oZHAgMBAAGjcjBwMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUNh3o4p2C0gEYtTJrDtdDC5FYQzowDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBSpg4PyGUjFPhJXCBTMzaN+mV8k9TAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAEaSbPjtmN4C/IB3QEpK32RxacCDXdVXAeVReS5FaZxc+t88pQP93BiAxvdW/3eTSMGY5FbeAYL3etqP5gm8wrFojX0ikyVRStQ+/AQ0KEjtqB07kLs9QUe8czR8UGfdM1EumV/UgvDd4NwNYxLQMg4WTQfgkQQVy8GXZwVHgbE/UC6Y7053pGXBk51NPM3woxhd3gSRLvXj+loHsStcTEqe9pBDpmG5+sk4tw+GK3GMeEN5/+e1QT9np/Kl1nj+aBw7C0xsy0bFnaAd1cSS6xdory/CUvM6gtKsmnOOdqTesbp0bs8sn6Wqs0C9dgcxRHuOMZ2tm8npLUm7argOSzQ==";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE0LTAxLTI3IDA0OjUwOjU2IEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInB1cmNoYXNlLWRhdGUtbXMiID0gIjEzOTA4MjcwNTYxNzciOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJiMTUxOTczZThjYzY3ZGRlMzkwNzhiZDAwMGU4N2U3MjNiYjE0M2U1IjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDA5OTcwODE1MCI7CgkiYnZycyIgPSAiMS4wLjciOwoJImFwcC1pdGVtLWlkIiA9ICI3MTE0MTEzMTciOwoJInRyYW5zYWN0aW9uLWlkIiA9ICIxMDAwMDAwMDk5NzA4MTUwIjsKCSJxdWFudGl0eSIgPSAiMSI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZS1tcyIgPSAiMTM5MDgyNzA1NjE3NyI7CgkidW5pcXVlLXZlbmRvci1pZGVudGlmaWVyIiA9ICJCNjNBRTVFQy02MjBCLTQxMkEtQjE5NC03NUI3MDU3Mjk4M0MiOwoJIml0ZW0taWQiID0gIjc3NTg5MTg5MiI7CgkidmVyc2lvbi1leHRlcm5hbC1pZGVudGlmaWVyIiA9ICIxNzU5NTMxMzgiOwoJInByb2R1Y3QtaWQiID0gImNvbS5pbnRvbXlsaWZlLmNyZWRpdHMyNSI7CgkicHVyY2hhc2UtZGF0ZSIgPSAiMjAxNC0wMS0yNyAxMjo1MDo1NiBFdGMvR01UIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDE0LTAxLTI3IDEyOjUwOjU2IEV0Yy9HTVQiOwoJImJpZCIgPSAiY29tLmludG9teWxpZmUuaW9zIjsKCSJwdXJjaGFzZS1kYXRlLXBzdCIgPSAiMjAxNC0wMS0yNyAwNDo1MDo1NiBBbWVyaWNhL0xvc19BbmdlbGVzIjsKfQ==";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}
Or, in base64:
$receipt="ewoJInNpZ25hdHVyZSIgPSAiQXB4UU1rcytLQUUwcmlZdEtqTk53aE5ldUdRNlI5OFgyMjN6Q2g2MHM5bTh3bG95ZFAzc0NjZVFkenJDd2QvM04xTCtkbGVmVDdaSlVpcXVDRXNEQW8rUmg1NGVTb3ZjS0VrKzJSWnlvUC96UlFIZ1RGODFrWUJJYmtGQ0FEaGo2a3pKVnIxcllzUlhLcE9KazZxV01ZUHorYTkwWEpmR3RuSUR1SGxSYjRWNUFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NHVVVrVTNaV0FTMU1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEE1TURZeE5USXlNRFUxTmxvWERURTBNRFl4TkRJeU1EVTFObG93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNclJqRjJjdDRJclNkaVRDaGFJMGc4cHd2L2NtSHM4cC9Sd1YvcnQvOTFYS1ZoTmw0WElCaW1LalFRTmZnSHNEczZ5anUrK0RyS0pFN3VLc3BoTWRkS1lmRkU1ckdYc0FkQkVqQndSSXhleFRldngzSExFRkdBdDFtb0t4NTA5ZGh4dGlJZERnSnYyWWFWczQ5QjB1SnZOZHk2U01xTk5MSHNETHpEUzlvWkhBZ01CQUFHamNqQndNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVOaDNvNHAyQzBnRVl0VEpyRHRkREM1RllRem93RGdZRFZSMFBBUUgvQkFRREFnZUFNQjBHQTFVZERnUVdCQlNwZzRQeUdVakZQaEpYQ0JUTXphTittVjhrOVRBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUVhU2JQanRtTjRDL0lCM1FFcEszMlJ4YWNDRFhkVlhBZVZSZVM1RmFaeGMrdDg4cFFQOTNCaUF4dmRXLzNlVFNNR1k1RmJlQVlMM2V0cVA1Z204d3JGb2pYMGlreVZSU3RRKy9BUTBLRWp0cUIwN2tMczlRVWU4Y3pSOFVHZmRNMUV1bVYvVWd2RGQ0TndOWXhMUU1nNFdUUWZna1FRVnk4R1had1ZIZ2JFL1VDNlk3MDUzcEdYQms1MU5QTTN3b3hoZDNnU1JMdlhqK2xvSHNTdGNURXFlOXBCRHBtRzUrc2s0dHcrR0szR01lRU41LytlMVFUOW5wL0tsMW5qK2FCdzdDMHhzeTBiRm5hQWQxY1NTNnhkb3J5L0NVdk02Z3RLc21uT09kcVRlc2JwMGJzOHNuNldxczBDOWRnY3hSSHVPTVoydG04bnBMVW03YXJnT1N6UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUwTFRBeExUSTNJREEwT2pVd09qVTJJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakV6T1RBNE1qY3dOVFl4TnpjaU93b0pJblZ1YVhGMVpTMXBaR1Z1ZEdsbWFXVnlJaUE5SUNKaU1UVXhPVGN6WlRoall6WTNaR1JsTXprd056aGlaREF3TUdVNE4yVTNNak5pWWpFME0yVTFJanNLQ1NKdmNtbG5hVzVoYkMxMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaU1UQXdNREF3TURBNU9UY3dPREUxTUNJN0Nna2lZblp5Y3lJZ1BTQWlNUzR3TGpjaU93b0pJbUZ3Y0MxcGRHVnRMV2xrSWlBOUlDSTNNVEUwTVRFek1UY2lPd29KSW5SeVlXNXpZV04wYVc5dUxXbGtJaUE5SUNJeE1EQXdNREF3TURrNU56QTRNVFV3SWpzS0NTSnhkV0Z1ZEdsMGVTSWdQU0FpTVNJN0Nna2liM0pwWjJsdVlXd3RjSFZ5WTJoaGMyVXRaR0YwWlMxdGN5SWdQU0FpTVRNNU1EZ3lOekExTmpFM055STdDZ2tpZFc1cGNYVmxMWFpsYm1SdmNpMXBaR1Z1ZEdsbWFXVnlJaUE5SUNKQ05qTkJSVFZGUXkwMk1qQkNMVFF4TWtFdFFqRTVOQzAzTlVJM01EVTNNams0TTBNaU93b0pJbWwwWlcwdGFXUWlJRDBnSWpjM05UZzVNVGc1TWlJN0Nna2lkbVZ5YzJsdmJpMWxlSFJsY201aGJDMXBaR1Z1ZEdsbWFXVnlJaUE5SUNJeE56VTVOVE14TXpnaU93b0pJbkJ5YjJSMVkzUXRhV1FpSUQwZ0ltTnZiUzVwYm5SdmJYbHNhV1psTG1OeVpXUnBkSE15TlNJN0Nna2ljSFZ5WTJoaGMyVXRaR0YwWlNJZ1BTQWlNakF4TkMwd01TMHlOeUF4TWpvMU1EbzFOaUJGZEdNdlIwMVVJanNLQ1NKdmNtbG5hVzVoYkMxd2RYSmphR0Z6WlMxa1lYUmxJaUE5SUNJeU1ERTBMVEF4TFRJM0lERXlPalV3T2pVMklFVjBZeTlIVFZRaU93b0pJbUpwWkNJZ1BTQWlZMjl0TG1sdWRHOXRlV3hwWm1VdWFXOXpJanNLQ1NKd2RYSmphR0Z6WlMxa1lYUmxMWEJ6ZENJZ1BTQWlNakF4TkMwd01TMHlOeUF3TkRvMU1EbzFOaUJCYldWeWFXTmhMMHh2YzE5QmJtZGxiR1Z6SWpzS2ZRPT0iOwoJImVudmlyb25tZW50IiA9ICJTYW5kYm94IjsKCSJwb2QiID0gIjEwMCI7Cgkic2lnbmluZy1zdGF0dXMiID0gIjAiOwp9";
The most simple data to verify would be the purchase-info field. Unfortunately, the sha1 sum of this (either base64-encoded or opaque) data does not match the one in the signature.
Having seen a fair share of Apple file formats, my guess would be it could be a combination in a form like "{$purchaseData}\x00\x00{$environment}\x00\x00{$pod}". With a bit of bad luck, though, they added a secret string that would make the entire exercise quite futile (but I fail to see as to why they would...)
Any insight?
update
Some more experimentation with sending different receipts to the /verifyReceipt endpoint suggests the pod/environment fields do not matter. Even more so, the order of fields within the receipt structure is of no consequence. Changing a single byte in the purchase-info data, however, directly yields an invalid receipt. This all would strengthen the hypothesis that only the purchase-info value is part of the hash -- but it's probably prefixed/suffixed with a secret. Could anyone verify (pun intended) this?
Please look at the code in https://developer.apple.com/library/ios/releasenotes/StoreKit/IAP_ReceiptValidation/index.html
The signature structure is
#pragma pack(push, 1)
struct signature_blob {
uint8_t version;
uint8_t signature[128];
uint32_t cert_len;
uint8_t certificate[];
} *signature_blob_ptr = (struct signature_blob *)signature_bytes;
#pragma pack(pop)
to_be_hashed= signature_blob_ptr->version . base64_decode(purchase_info)
code here
<?php
$plist = file_get_contents($argv[1]);
if(preg_match('/"signature" = "([^"]+)"/',$plist,$s) && preg_match('/"purchase-info" = "([^"]+)"/',$plist,$p)){
$sig= base64_decode($s[1]);
$purchase=base64_decode($p[1]);
$version=substr($sig,0,1);
$cert = substr($sig,133);
$sig = substr($sig,1,128);
$to_be_sign=$version.$purchase;
//get PEM encode
$cert="-----BEGIN CERTIFICATE-----\n".wordwrap(base64_encode($cert),64,"\n",true)."\n-----END CERTIFICATE-----";
$pubkey = openssl_get_publickey($cert);
if(openssl_verify($to_be_sign,$sig,$pubkey) ==1 )
echo "Success\n";
else
echo "Failed\n";
openssl_free_key($pubkey);
}

What kind of padding does Rails OpenSSL::Cipher use for AES-CBC-256?

What padding scheme does OpenSSL::Cipher use when padding blocks for encryption? The documentation is vague.
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-padding-3D
I will need to use the encrypted data with a different language. I'm aware there are many types of padding:
https://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Padding
Your first link advises to
See EVP_CIPHER_CTX_set_padding for further information.
This page indicates (emphasis mine) that:
If padding is enabled (the default) then EVP_EncryptFinal_ex()
encrypts the ``final'' data, that is any data that remains in a
partial block. It uses standard block padding (aka PKCS padding). The
encrypted final data is written to out which should have sufficient
space for one cipher block. The number of bytes written is placed in
outl. After this function is called the encryption operation is
finished and no further calls to EVP_EncryptUpdate() should be made.
That page also includes a link to additional information that you may find helpful.
PKCS#7/PKSC#5 are pretty common for CBC mode. PKCS#5 is identical to PKCS#7, but PKCS#5 refers only to 64 bit (8 byte) block size so for AES-256 it is PKCS#7
from en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7
01
02 02
03 03 03
04 04 04 04
05 05 05 05 05
etc.
if your msg size is a multiple of 16 (Block Size in AES) then 1 more block filled with 16 times of byte 16 is added
You can confirm what padding is being used by decrypting a message with NoPadding set in your decryption method. That will pass through any padding as if it was part of the actual message. Have a look at the last block's worth of bytes from the message. That will tell you what type of padding the sender is using. Then set your decryption function to expect that type of padding.

Convert Lockbox2 cipher text to Lockbox3 cipher text

Is there a way I can Convert my Lockbox 2 Cipher text to LockBox 3 Cipher text.
We are migrating our application built on Delphi 2007 to Delphi xe2, we used the Lockbox 2 RSA Encryption algorithm in Delphi 2007 and we intend to use lockbox 3 in Delphi xe2 to support Unicode data. since the cipher text generated by both of them differs as Xe2 supports Unicode data we face a problem . So we would like to convert the cipher text that is generated by Lockbox 2 to LockBox 3 somehow.
Since your cipher text by definition is unrecognizable, there is no easy way to tell if the underlying plaintext data was Ansi or Unicode....so you likely need to manage a new associated property.
It obviously depends on the layout of your application and where this data is stored and how the clients are going to be upgraded, but there could be a new version flag of some sort associated with the stored ciphertext. If it's in a local table say, add a new column for PlainTextVersion and set the version to some value to flag that the ciphertext was saved from Unicode plaintext. When reading the ciphertext and this new field doesn't match the Unicdoe flag, you could upgrade the ciphertext by decrypting, and encrypting using Unicode plaintext, and then re-save the ciphertext and set the new flag (or simply put-off the ciphertext version upgrade until the plaintext has changed and needs to be updated.)
Or, better yet, auto-upgrade all current ciphertext at one time if feasible.
To convert, it would be easiest to use Lockbox 2 to decrypt your cypher text and use Lockbox 3 to reencrypt it.
The reason is that from what I can tell, Lockbox 2 stuffed up the implementation of the RSA block type 2 padding which means that Lockbox 2's RSA encryption is not compatible with anybody else's RSA decryption.
Lockbox 2's RSA encryption pads out the message incorrectly as follows (found by placing a breakpoint and inspecting the memory at biBlock.Fi.IntBuf.pBuf):
message-bytes 0x00 random-padding-bytes 0x02 0x00
e.g. 'test' was padded to:
$01C883AC 74 65 73 74 00 D4 50 50 test..PP
$01C883B4 A7 BO E5 51 7A 4C C2 BC ...QzL..
$01C883BC 8C B8 69 8A 97 DF AA 1D ..I.....
$01C883C4 78 67 1E OE 8B AB 02 00 xg......
But it should be padded out to (e.g. look at this worked example):
0x00 0x02 random-padding-bytes 0x00 message-bytes
Lockbox 2 isn't just storing the bytes in reverse (otherwise the message "test" would also be reversed) or reversed 32 bit little endian (otherwise the 02 00 would be swapped too). Everything works so long as you use Lockbox 2 for both encryption and decryption.
Also I noticed another bug where Lockbox 2 calls e.RandomSimplePrime() to generate the public exponent e but it generates an even number i.e. a fairly noteworthy bug in RandomSimplePrime() eh? I only looked at Lockbox 2.07. Lockbox 3 was a complete rewrite, so it won't have these bugs.

Reading and parsing the width/height property of a bitmap

I'm trying to write a bitmap (.bmp) parser/reader by reading raw bytes from the file and simply checking their values, and I've come across something I simply cannot wrap my mind around.
The image I'm trying to read is 512x512 pixels, and when I look at the width property (at 0x12 and 4 bytes onward) it says 00 02 00 00 (when viewed in a hex editor). I assume this is the same as the binary value 00000000 00000010 00000000 00000000. This somehow represents 512, I just cannot figure out the steps to get there.
So what I really need to know is how are integers represented binarily, and how do I parse them correctly? Any help is much appreciated. :)
What you are seeing in your hex editor is actually right. Just remember that bytes are in little endian order, so the value is actually 00 00 02 00 = 0x0200 = 512.
Actually 0x200 in hex equals 512 in decimal. You may have the position of the width/height properties wrong.

Resources