1/*
2 *  si-40-seckey.c
3 *  Security
4 *
5 *  Created by Michael Brouwer on 1/29/07.
6 *  Copyright (c) 2007-2009 Apple Inc. All Rights Reserved.
7 *
8 */
9
10#include <CoreFoundation/CoreFoundation.h>
11#include <Security/SecCertificate.h>
12#include <Security/SecCertificateInternal.h>
13#include <Security/SecKey.h>
14#include <Security/SecKeyPriv.h>
15#include <Security/SecItem.h>
16#include <Security/SecAsn1Types.h>
17#include <Security/oidsalg.h>
18#include <Security/SecureTransport.h>
19#include <Security/SecRandom.h>
20#include <utilities/array_size.h>
21#include <CommonCrypto/CommonDigest.h>
22#include <libDER/libDER.h>
23#include <stdlib.h>
24#include <unistd.h>
25
26#include "Security_regressions.h"
27
28#include "utilities/SecCFRelease.h"
29
30static void testdigestandsignalg(SecKeyRef privKey, SecKeyRef pubKey, const SecAsn1AlgId *algId) {
31    uint8_t dataToDigest[256] = {0,};
32    size_t dataToDigestLen = sizeof(dataToDigest);
33    size_t sigLen = SecKeyGetSize(privKey, kSecKeySignatureSize);
34    uint8_t sig[sigLen];
35
36    DERItem oid;
37    oid.length = algId->algorithm.Length;
38    oid.data = algId->algorithm.Data;
39
40    /* Get the oid in decimal for display purposes. */
41    CFStringRef oidStr = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, &oid);
42	char oidBuf[40];
43    CFStringGetCString(oidStr, oidBuf, sizeof(oidBuf), kCFStringEncodingUTF8);
44    CFRelease(oidStr);
45
46	SKIP: {
47        OSStatus status;
48
49        /* Time to sign. */
50        ok_status(status = SecKeyDigestAndSign(privKey, algId, dataToDigest, dataToDigestLen,
51            sig, &sigLen), "digest and sign %s with %ld bit RSA key", oidBuf, sigLen * 8);
52
53        skip("SecKeyDigestAndSign failed", 3, status == errSecSuccess);
54
55        /* Verify the signature we just made. */
56        ok_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen,
57            sig, sigLen), "digest and verify");
58        /* Invalidate the signature. */
59        sig[0] ^= 0xff;
60        is_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen,
61            sig, sigLen), errSSLCrypto, "digest and verify bad sig");
62        sig[0] ^= 0xff;
63        dataToDigest[0] ^= 0xff;
64        is_status(SecKeyDigestAndVerify(pubKey, algId, dataToDigest, dataToDigestLen,
65            sig, sigLen), errSSLCrypto, "digest and verify bad digest");
66    }
67}
68
69static void testdigestandsign(SecKeyRef privKey, SecKeyRef pubKey) {
70    static const SecAsn1Oid *oids[] = {
71        &CSSMOID_SHA1WithRSA,
72        &CSSMOID_SHA224WithRSA,
73        &CSSMOID_SHA256WithRSA,
74        &CSSMOID_SHA384WithRSA,
75        &CSSMOID_SHA512WithRSA,
76#if 0
77        &CSSMOID_SHA1WithRSA_OIW,
78        &CSSMOID_SHA1WithDSA,		// BSAFE
79        &CSSMOID_SHA1WithDSA_CMS,	// X509/CMS
80        &CSSMOID_SHA1WithDSA_JDK,	// JDK 1.1
81#endif
82    };
83
84
85    uint32_t ix;
86    SecAsn1AlgId algId = {};
87    for (ix = 0; ix < array_size(oids); ++ix) {
88        if (oids[ix]) {
89            algId.algorithm = *oids[ix];
90        } else {
91            algId.algorithm.Length = 0;
92            algId.algorithm.Data = NULL;
93        }
94
95        testdigestandsignalg(privKey, pubKey, &algId);
96    }
97}
98
99#if 0
100static void dump_bytes(uint8_t* bytes, size_t amount)
101{
102    while (amount > 0) {
103        printf("0x%02x ", *bytes);
104        ++bytes;
105        --amount;
106    }
107}
108#endif
109
110#define kEncryptDecryptTestCount 5
111static void test_encrypt_decrypt(SecKeyRef pubKey, SecKeyRef privKey, uint32_t padding, size_t keySizeInBytes)
112{
113    SKIP: {
114        size_t max_len = keySizeInBytes;
115        switch (padding) {
116            case kSecPaddingNone: max_len = keySizeInBytes; break;
117            case kSecPaddingOAEP: max_len = keySizeInBytes - 2 - 2 * CC_SHA1_DIGEST_LENGTH; break;
118            case kSecPaddingPKCS1: max_len = keySizeInBytes - 11; break;
119            default: skip("what is the max_len for this padding?", 5, false);
120        }
121
122        uint8_t secret[max_len + 1], encrypted_secret[keySizeInBytes], decrypted_secret[keySizeInBytes];
123        uint8_t *secret_ptr = secret;
124        size_t secret_len = max_len;
125        size_t encrypted_secret_len = sizeof(encrypted_secret);
126        size_t decrypted_secret_len = sizeof(decrypted_secret);
127        memset(decrypted_secret, 0xff, decrypted_secret_len);
128        SecRandomCopyBytes(kSecRandomDefault, sizeof(secret), secret);
129
130        // zero pad, no accidental second zero byte
131        if (padding == kSecPaddingNone) {
132            secret[0] = 0;
133            secret[1] = 128;
134        }
135
136        is_status(SecKeyEncrypt(pubKey, padding,
137                                secret, sizeof(secret),
138                                encrypted_secret, &encrypted_secret_len), errSecParam, "encrypt secret (overflow)");
139        ok_status(SecKeyEncrypt(pubKey, padding,
140                                secret, secret_len,
141                                encrypted_secret, &encrypted_secret_len), "encrypt secret");
142
143        ok_status(SecKeyDecrypt(privKey, padding,
144                                encrypted_secret, encrypted_secret_len,
145                                decrypted_secret, &decrypted_secret_len), "decrypt secret");
146
147        // zero padding is removed on decode
148        if (padding == kSecPaddingNone) {
149            secret_len--;
150            secret_ptr++;
151        }
152
153        ok(decrypted_secret_len == secret_len, "correct length");
154        ok_status(memcmp(secret_ptr, decrypted_secret, secret_len), "verify secret");
155    }
156}
157
158#define kKeyGenTestCount (49 + (3*kEncryptDecryptTestCount))
159static void testkeygen(size_t keySizeInBits) {
160	SecKeyRef pubKey = NULL, privKey = NULL;
161	size_t keySizeInBytes = (keySizeInBits + 7) / 8;
162	CFNumberRef kzib;
163
164	kzib = CFNumberCreate(NULL, kCFNumberSInt32Type, &keySizeInBits);
165	CFMutableDictionaryRef kgp = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
166	CFDictionaryAddValue(kgp, kSecAttrKeyType, kSecAttrKeyTypeRSA);
167	CFDictionaryAddValue(kgp, kSecAttrKeySizeInBits, kzib);
168
169	OSStatus status;
170	ok_status(status = SecKeyGeneratePair(kgp, &pubKey, &privKey),
171              "Generate %ld bit (%ld byte) RSA keypair", keySizeInBits,
172              keySizeInBytes);
173	CFRelease(kzib);
174	CFRelease(kgp);
175
176	SKIP: {
177        skip("keygen failed", 8, status == errSecSuccess);
178		ok(pubKey, "pubkey returned");
179		ok(privKey, "privKey returned");
180		is(SecKeyGetSize(pubKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "public key size is ok");
181		is(SecKeyGetSize(privKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "private key size is ok");
182
183		/* Sign something. */
184		uint8_t something[keySizeInBytes];
185        size_t something_len = keySizeInBytes - 11;
186        SecRandomCopyBytes(kSecRandomDefault, sizeof(something), something);
187		uint8_t sig[keySizeInBytes];
188		size_t sigLen = sizeof(sig);
189		is_status(SecKeyRawSign(privKey, kSecPaddingPKCS1,
190                                something, something_len + 1, sig, &sigLen),
191                                errSecParam, "sign overflow");
192		ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1,
193			something, something_len, sig, &sigLen), "sign something");
194		ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1,
195			something, something_len, sig, sigLen), "verify sig on something");
196
197        // Torture test ASN.1 encoder by setting high bit to 1.
198        uint8_t digest[CC_SHA512_DIGEST_LENGTH] = {
199            0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
200            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
201            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
202            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
203            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
204            0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
205            0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
206            0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
207        };
208        //CC_MD2(something, sizeof(something), digest);
209		ok_status(!SecKeyRawSign(privKey, kSecPaddingPKCS1MD2,
210			digest, CC_MD2_DIGEST_LENGTH, sig, &sigLen),
211            "don't sign md2 digest");
212		ok_status(!SecKeyRawVerify(pubKey, kSecPaddingPKCS1MD2,
213			digest, CC_MD2_DIGEST_LENGTH, sig, sigLen),
214            "verify sig on md2 digest fails");
215
216        //CC_MD5(something, sizeof(something), digest);
217		sigLen = sizeof(sig);
218		ok_status(!SecKeyRawSign(privKey, kSecPaddingPKCS1MD5,
219			digest, CC_MD5_DIGEST_LENGTH, sig, &sigLen),
220            "don't sign md5 digest");
221		ok_status(!SecKeyRawVerify(pubKey, kSecPaddingPKCS1MD5,
222			digest, CC_MD5_DIGEST_LENGTH, sig, sigLen),
223            "verify sig on md5 digest fails");
224
225        //CCDigest(kCCDigestSHA1, something, sizeof(something), digest);
226		sigLen = sizeof(sig);
227		ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA1,
228			digest, CC_SHA1_DIGEST_LENGTH, sig, &sigLen),
229            "sign sha1 digest");
230		ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA1,
231			digest, CC_SHA1_DIGEST_LENGTH, sig, sigLen),
232            "verify sig on sha1 digest");
233
234		uint8_t signature[keySizeInBytes], *ptr = signature;
235		size_t signature_len = sizeof(signature);
236		ok_status(SecKeyDecrypt(pubKey, kSecPaddingNone, sig, sigLen, signature, &signature_len), "inspect signature");
237		is(signature_len, keySizeInBytes - 1, "got signature");
238		while(*ptr && ((size_t)(ptr - signature) < signature_len)) ptr++;
239		is(signature + signature_len - ptr, 16 /* length(\0 || OID_SHA1) */ + CC_SHA1_DIGEST_LENGTH, "successful decode");
240
241        /* PKCS1 padding is 00 01 PAD * 8 or more 00 data.
242           data is SEQ { SEQ { OID NULL } BIT STRING 00 DIGEST }
243           So min data + pad overhead is 11 + 9 + oidlen
244           oidlen = 11 for the sha2 family of oids, so we have 29 bytes; or
245           232 bits of minimum overhead.  */
246        const size_t pkcs1Overhead = 232;
247        if (keySizeInBits > 224 + pkcs1Overhead) {
248            //CC_SHA224(something, sizeof(something), digest);
249            sigLen = sizeof(sig);
250            ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA224,
251                                    digest, CC_SHA224_DIGEST_LENGTH, sig, &sigLen),
252                      "sign sha224 digest");
253            ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA224,
254                                      digest, CC_SHA224_DIGEST_LENGTH, sig, sigLen),
255                      "verify sig on sha224 digest");
256        }
257
258        if (keySizeInBits > 256 + pkcs1Overhead) {
259            //CC_SHA256(something, sizeof(something), digest);
260            sigLen = sizeof(sig);
261            ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA256,
262                                    digest, CC_SHA256_DIGEST_LENGTH, sig, &sigLen),
263                      "sign sha256 digest");
264            ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA256,
265                                      digest, CC_SHA256_DIGEST_LENGTH, sig, sigLen),
266                      "verify sig on sha256 digest");
267        }
268
269        if (keySizeInBits > 384 + pkcs1Overhead) {
270            //CC_SHA384(something, sizeof(something), digest);
271            sigLen = sizeof(sig);
272            ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA384,
273                                    digest, CC_SHA384_DIGEST_LENGTH, sig, &sigLen),
274                      "sign sha384 digest");
275            ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA384,
276                                      digest, CC_SHA384_DIGEST_LENGTH, sig, sigLen),
277                      "verify sig on sha384 digest");
278        }
279
280        if (keySizeInBits > 512 + pkcs1Overhead) {
281            //CC_SHA512(something, sizeof(something), digest);
282            sigLen = sizeof(sig);
283            ok_status(SecKeyRawSign(privKey, kSecPaddingPKCS1SHA512,
284                                    digest, CC_SHA512_DIGEST_LENGTH, sig, &sigLen),
285                      "sign sha512 digest");
286            ok_status(SecKeyRawVerify(pubKey, kSecPaddingPKCS1SHA512,
287                                      digest, CC_SHA512_DIGEST_LENGTH, sig, sigLen),
288                      "verify sig on sha512 digest");
289        }
290
291        test_encrypt_decrypt(pubKey, privKey, kSecPaddingNone, keySizeInBytes);
292        test_encrypt_decrypt(pubKey, privKey, kSecPaddingPKCS1, keySizeInBytes);
293        test_encrypt_decrypt(pubKey, privKey, kSecPaddingOAEP, keySizeInBytes);
294
295        testdigestandsign(privKey, pubKey);
296
297		const void *privkeys[] = {
298			kSecValueRef
299		};
300		const void *privvalues[] = {
301			privKey
302		};
303		CFDictionaryRef privitem = CFDictionaryCreate(NULL, privkeys, privvalues,
304		array_size(privkeys), NULL, NULL);
305		ok_status(SecItemAdd(privitem, NULL), "add private key");
306        ok_status(SecItemDelete(privitem), "delete private key");
307		CFReleaseNull(privitem);
308
309		const void *pubkeys[] = {
310			kSecValueRef
311		};
312		const void *pubvalues[] = {
313			pubKey
314		};
315		CFDictionaryRef pubitem = CFDictionaryCreate(NULL, pubkeys, pubvalues,
316		array_size(pubkeys), NULL, NULL);
317		ok_status(SecItemAdd(pubitem, NULL), "add public key");
318        ok_status(SecItemDelete(pubitem), "delete public key");
319		CFReleaseNull(pubitem);
320
321		/* Cleanup. */
322		CFReleaseNull(pubKey);
323		CFReleaseNull(privKey);
324	}
325}
326
327#define kKeyGen2TestCount 12
328static void testkeygen2(size_t keySizeInBits) {
329	SecKeyRef pubKey = NULL, privKey = NULL;
330	size_t keySizeInBytes = (keySizeInBits + 7) / 8;
331	CFNumberRef kzib;
332
333    CFUUIDRef ourUUID = CFUUIDCreate(kCFAllocatorDefault);
334    CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, ourUUID);
335    CFMutableStringRef publicName = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, uuidString);
336    CFMutableStringRef privateName = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, uuidString);
337
338    CFReleaseNull(ourUUID);
339    CFReleaseNull(uuidString);
340
341    CFStringAppend(publicName, CFSTR("-Public-40"));
342    CFStringAppend(privateName, CFSTR("-Private-40"));
343	CFMutableDictionaryRef pubd = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
344	CFMutableDictionaryRef privd = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
345
346	CFDictionaryAddValue(pubd, kSecAttrLabel, publicName);
347	CFDictionaryAddValue(privd, kSecAttrLabel, privateName);
348
349	kzib = CFNumberCreate(NULL, kCFNumberSInt32Type, &keySizeInBits);
350	CFMutableDictionaryRef kgp = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
351	CFDictionaryAddValue(kgp, kSecAttrKeyType, kSecAttrKeyTypeRSA);
352	CFDictionaryAddValue(kgp, kSecAttrKeySizeInBits, kzib);
353	CFDictionaryAddValue(kgp, kSecAttrIsPermanent, kCFBooleanTrue);
354	CFDictionaryAddValue(kgp, kSecPublicKeyAttrs, pubd);
355	CFDictionaryAddValue(kgp, kSecPrivateKeyAttrs, privd);
356
357	OSStatus status;
358	ok_status(status = SecKeyGeneratePair(kgp, &pubKey, &privKey),
359              "Generate %ld bit (%ld byte) persistent RSA keypair",
360              keySizeInBits, keySizeInBytes);
361	CFRelease(kzib);
362	CFRelease(kgp);
363
364	SKIP: {
365        skip("keygen failed", 8, status == errSecSuccess);
366		ok(pubKey, "pubkey returned");
367		ok(privKey, "privKey returned");
368		is(SecKeyGetSize(pubKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "public key size is ok");
369		is(SecKeyGetSize(privKey, kSecKeyKeySizeInBits), (size_t) keySizeInBits, "private key size is ok");
370
371		SecKeyRef pubKey2, privKey2;
372		CFDictionaryAddValue(pubd, kSecClass, kSecClassKey);
373		CFDictionaryAddValue(pubd, kSecReturnRef, kCFBooleanTrue);
374		CFDictionaryAddValue(privd, kSecClass, kSecClassKey);
375		CFDictionaryAddValue(privd, kSecReturnRef, kCFBooleanTrue);
376        CFDictionaryAddValue(privd, kSecAttrCanSign, kCFBooleanTrue);
377		ok_status(SecItemCopyMatching(pubd, (CFTypeRef *)&pubKey2),
378			"retrieve pub key by label");
379		ok_status(SecItemCopyMatching(privd, (CFTypeRef *)&privKey2),
380			"retrieve priv key by label and kSecAttrCanSign");
381
382		/* Sign something. */
383		uint8_t something[50] = {0x80, 0xbe, 0xef, 0xba, 0xd0, };
384		uint8_t sig[keySizeInBytes];
385		size_t sigLen = keySizeInBytes;
386		ok_status(SecKeyRawSign(privKey2, kSecPaddingPKCS1,
387			something, sizeof(something), sig, &sigLen), "sign something");
388		ok_status(SecKeyRawVerify(pubKey2, kSecPaddingPKCS1,
389			something, sizeof(something), sig, sigLen), "verify sig on something");
390
391        sigLen = keySizeInBytes;
392		is_status(SecKeyEncrypt(pubKey2, kSecPaddingPKCS1SHA1,
393			something, sizeof(something), sig, &sigLen), errSecParam,
394            "encrypt something with invalid padding");
395
396		/* Cleanup. */
397		CFReleaseNull(pubKey2);
398		CFReleaseNull(privKey2);
399
400        /* delete from keychain - note: do it before releasing publicName and privateName
401         because pubd and privd have no retain/release callbacks */
402        ok_status(SecItemDelete(pubd), "delete generated pub key");
403        ok_status(SecItemDelete(privd), "delete generated priv key");
404	}
405
406	/* Cleanup. */
407	CFReleaseNull(pubKey);
408	CFReleaseNull(privKey);
409
410    CFReleaseNull(publicName);
411    CFReleaseNull(privateName);
412
413	CFRelease(pubd);
414	CFRelease(privd);
415}
416
417/* Test basic add delete update copy matching stuff. */
418#define kTestCount ((3 * kKeyGenTestCount) + kKeyGen2TestCount)
419static void tests(void)
420{
421	/* Comment out lines below for testing generating all common key sizes,
422	   disabled now for speed reasons. */
423	//testkeygen(512);
424	testkeygen(768);
425	testkeygen(1024);
426	testkeygen(2056); // Stranged sized for edge cases in padding.
427	//testkeygen(2048);
428	//testkeygen(4096);
429
430    testkeygen2(768);
431}
432
433int si_40_seckey(int argc, char *const *argv)
434{
435	plan_tests(kTestCount);
436
437	tests();
438
439	return 0;
440}
441