]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/regressions/kc-15-key-update-valueref.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / regressions / kc-15-key-update-valueref.c
diff --git a/OSX/libsecurity_keychain/regressions/kc-15-key-update-valueref.c b/OSX/libsecurity_keychain/regressions/kc-15-key-update-valueref.c
new file mode 100644 (file)
index 0000000..5d8f468
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2016 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the xLicense.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+//     Tests the SecItemUpdate function.
+//     Currently this is a simple test to determine whether the correct item
+//     is updated when specified by a kSecValueRef (see <rdar://10358577>).
+//
+
+#include "keychain_regressions.h"
+#include "kc-helpers.h"
+#include "kc-item-helpers.h"
+#include "kc-key-helpers.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <Security/Security.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/param.h>
+
+static int quiet = 0;
+static int debug = 1;
+static int verbose = 1;
+
+#define MAXNAMELEN MAXPATHLEN
+#define MAXITEMS INT32_MAX
+
+#pragma mark -- Utility Functions --
+
+
+static void PrintTestResult(char *testStr, OSStatus status, OSStatus expected)
+{
+       if (verbose) {
+               fprintf(stdout, "%s: %s (result=%d, expected=%d)\n", testStr,
+                               (status==expected) ? "OK" : "FAILED",
+                               (int)status, (int)expected);
+       }
+       if (debug) {
+               fprintf(stdout, "\n");
+       }
+       fflush(stdout);
+}
+
+static void PrintCFStringWithFormat(const char *formatStr, CFStringRef inStr)
+{
+    char *buf = (char*)malloc(MAXNAMELEN);
+    if (buf) {
+        if (CFStringGetCString(inStr, buf, (CFIndex)MAXNAMELEN, kCFStringEncodingUTF8)) {
+            fprintf(stdout, formatStr, buf);
+            fflush(stdout);
+        }
+        free(buf);
+    }
+}
+
+const CFStringRef gPrefix = CFSTR("Test Key");
+const CFStringRef gLabel = CFSTR("Test AES Encryption Key");
+const CFStringRef gUUID = CFSTR("550e8400-e29b-41d4-a716-446655441234");
+
+// CreateSymmetricKey will create a new AES-128 symmetric encryption key
+// with the provided label, application label, and application tag.
+// Each of those attributes is optional, but only the latter two
+// (application label and application tag) are considered part of the
+// key's "unique" attribute set. Previously, if you attempted to create a
+// key which differs only in the label attribute (but not in the other two)
+// then the attempt would fail and leave a "turd" key with no label in your
+// keychain: <rdar://8289559>, fixed in 11A268a.
+
+static int CreateSymmetricKey(
+       SecKeychainRef inKeychain,
+       CFStringRef keyLabel,
+       CFStringRef keyAppLabel,
+       CFStringRef keyAppTag,
+       OSStatus expected)
+{
+    OSStatus status;
+       int keySizeValue = 128;
+       CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
+
+       // get a SecKeychainRef for the keychain in which we want the key to be created
+       // (this step is optional, but if omitted, the key is NOT saved in any keychain!)
+       SecKeychainRef keychain = NULL;
+       if (inKeychain == NULL)
+               status = SecKeychainCopyDefault(&keychain);
+       else
+               keychain = (SecKeychainRef) CFRetain(inKeychain);
+
+       // create a SecAccessRef to set up the initial access control settings for this key
+       // (this step is optional; if omitted, the creating application has access to the key)
+       // note: the access descriptor should be the same string as will be used for the item's label,
+       // since it's the string that is displayed by the access confirmation dialog to describe the item.
+       SecAccessRef access = NULL;
+       status = SecAccessCreate(gLabel, NULL, &access);
+
+       // create a dictionary of parameters describing the key we want to create
+       CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL, 0,
+                                                                               &kCFTypeDictionaryKeyCallBacks,
+                                                                               &kCFTypeDictionaryValueCallBacks);
+
+       CFDictionaryAddValue( params, kSecClass, kSecClassKey );
+       CFDictionaryAddValue( params, kSecUseKeychain, keychain );
+       CFDictionaryAddValue( params, kSecAttrAccess, access );
+       CFDictionaryAddValue( params, kSecAttrKeyClass, kSecAttrKeyClassSymmetric );
+       CFDictionaryAddValue( params, kSecAttrKeyType, kSecAttrKeyTypeAES );
+       CFDictionaryAddValue( params, kSecAttrKeySizeInBits, keySize );
+       CFDictionaryAddValue( params, kSecAttrIsPermanent, kCFBooleanTrue );
+       CFDictionaryAddValue( params, kSecAttrCanEncrypt, kCFBooleanTrue );
+       CFDictionaryAddValue( params, kSecAttrCanDecrypt, kCFBooleanTrue );
+       CFDictionaryAddValue( params, kSecAttrCanWrap, kCFBooleanFalse );
+       CFDictionaryAddValue( params, kSecAttrCanUnwrap, kCFBooleanFalse );
+       if (keyLabel)
+               CFDictionaryAddValue( params, kSecAttrLabel, keyLabel );
+       if (keyAppLabel)
+               CFDictionaryAddValue( params, kSecAttrApplicationLabel, keyAppLabel );
+       if (keyAppTag)
+               CFDictionaryAddValue( params, kSecAttrApplicationTag, keyAppTag );
+
+       // generate the key
+       CFErrorRef error = NULL;
+    SecKeyRef key = SecKeyGenerateSymmetric(params, &error);
+
+       // print result and clean up
+       if (debug) {
+               if (key == NULL) {
+                       CFStringRef desc = (error) ? CFErrorCopyDescription(error) : CFRetain(CFSTR("(no result!"));
+                       PrintCFStringWithFormat("SecKeyGenerateSymmetric failed: %s\n", desc);
+                       CFRelease(desc);
+               }
+               else {
+                       CFStringRef desc = CFCopyDescription(key);
+                       PrintCFStringWithFormat("SecKeyGenerateSymmetric succeeded: %s\n", desc);
+                       CFRelease(desc);
+               }
+       }
+       status = (error) ? (OSStatus) CFErrorGetCode(error) : noErr;
+//     if (status == errSecDuplicateItem)
+//             status = noErr; // it's OK if the key already exists
+
+       if (key) CFRelease(key);
+       if (error) CFRelease(error);
+       if (params) CFRelease(params);
+       if (keychain) CFRelease(keychain);
+       if (access) CFRelease(access);
+
+       PrintTestResult("CreateSymmetricKey", status, expected);
+
+       return status;
+}
+
+static int TestUpdateItems(SecKeychainRef keychain)
+{
+       int result = 0;
+    OSStatus status = errSecSuccess;
+
+       // first, create a symmetric key
+       CFGregorianDate curGDate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), NULL);
+       CFStringRef curDateLabel = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ (%4d-%02d-%02d)"),
+               gPrefix, (int) (curGDate.year), (int) (curGDate.month), (int) (curGDate.day));
+       CFStringRef curAppTag = CFSTR("SecItemUpdate");
+
+       status = CreateSymmetricKey(keychain, curDateLabel, gUUID, curAppTag, noErr);
+       if (status && status != errSecDuplicateItem)
+               ++result;
+    
+       CFStringRef keyLabel = CFSTR("iMessage test key");
+       CFStringRef newLabel = CFSTR("iMessage test PRIVATE key");
+    
+    // create a new 1024-bit RSA key pair
+       SecKeyRef publicKey = NULL;
+       SecKeyRef privateKey = NULL;
+       CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL, 0,
+               &kCFTypeDictionaryKeyCallBacks,
+               &kCFTypeDictionaryValueCallBacks);
+       int keySizeValue = 1024;
+       CFNumberRef keySize = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
+
+       CFDictionaryAddValue( params, kSecAttrKeyType, kSecAttrKeyTypeRSA );
+       CFDictionaryAddValue( params, kSecAttrKeySizeInBits, keySize );
+       CFDictionaryAddValue( params, kSecAttrLabel, keyLabel );
+//     CFDictionaryAddValue( params, kSecAttrAccess, access );
+// %%% note that SecKeyGeneratePair will create the key pair in the default keychain
+// if a keychain is not given via the kSecUseKeychain parameter.
+       CFDictionaryAddValue( params, kSecUseKeychain, keychain );
+
+       status = SecKeyGeneratePair(params, &publicKey, &privateKey);
+    ok_status(status, "%s: SecKeyGeneratePair", testName);
+       if (status != noErr) {
+               ++result;
+       }
+       PrintTestResult("TestUpdateItems: generating key pair", status, noErr);
+    
+    // Make sure we have the key of interest
+    checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, keyLabel), 1);
+    checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, newLabel), 0);
+
+       // create a query which will match just the private key item (based on its known reference)
+       CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0,
+               &kCFTypeDictionaryKeyCallBacks,
+               &kCFTypeDictionaryValueCallBacks);
+//     CFArrayRef itemList = CFArrayCreate(NULL, (const void**) &privateKey, 1, &kCFTypeArrayCallBacks);
+// %%% note that kSecClass seems to be a required query parameter even though
+// kSecMatchItemList is provided; that looks like it could be a bug...
+       CFDictionaryAddValue( query, kSecClass, kSecClassKey );
+//     CFDictionaryAddValue( query, kSecAttrKeyClass, kSecAttrKeyClassPrivate );
+
+// %%% pass the private key ref, instead of the item list, to test <rdar://problem/10358577>
+//     CFDictionaryAddValue( query, kSecMatchItemList, itemList );
+       CFDictionaryAddValue( query, kSecValueRef, privateKey );
+
+       // create dictionary of changed attributes for the private key
+       CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(NULL, 0,
+               &kCFTypeDictionaryKeyCallBacks,
+               &kCFTypeDictionaryValueCallBacks);
+       SecAccessRef access = NULL;
+
+       status = SecAccessCreate(newLabel, NULL, &access);
+    ok_status(status, "%s: SecAccessCreate", testName);
+       if (status != noErr) {
+               ++result;
+       }
+       PrintTestResult("TestUpdateItems: creating access", status, noErr);
+
+//%%% note that changing the access for this key causes a dialog,
+// so leave this out for the moment (uncomment to test that access change works).
+// Normally the desired access should be passed into the SecKeyGeneratePair function.
+// so there is no need for a dialog later.
+//     CFDictionaryAddValue( attrs, kSecAttrAccess, access );
+       CFDictionaryAddValue( attrs, kSecAttrLabel, newLabel );
+
+       // update the private key with the new attributes
+       status = SecItemUpdate( query, attrs );
+    ok_status(status, "%s: SecItemUpdate", testName);
+    
+       if (status != noErr) {
+               ++result;
+       }
+       PrintTestResult("TestUpdateItems: updating item", status, noErr);
+    
+    // Make sure label changed
+    checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, keyLabel), 0);
+    checkN(testName, makeQueryKeyDictionaryWithLabel(keychain, kSecAttrKeyClassPrivate, newLabel), 1);
+
+       if (publicKey)
+               CFRelease(publicKey);
+       if (privateKey)
+               CFRelease(privateKey);
+       if (access)
+               CFRelease(access);
+
+       if (params)
+               CFRelease(params);
+       if (query)
+               CFRelease(query);
+       if (attrs)
+               CFRelease(attrs);
+
+       return result;
+}
+
+int kc_15_key_update_valueref(int argc, char *const *argv)
+{
+       plan_tests(20);
+    initializeKeychainTests(__FUNCTION__);
+
+    SecKeychainRef keychain = getPopulatedTestKeychain();
+
+    TestUpdateItems(keychain);
+
+    checkPrompts(0, "no prompts during test");
+
+    ok_status(SecKeychainDelete(keychain), "%s: SecKeychainDelete", testName);
+    CFRelease(keychain);
+
+    deleteTestFiles();
+       return 0;
+}