]> git.saurik.com Git - apple/security.git/blobdiff - securityd/tests/testblobs.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / securityd / tests / testblobs.cpp
diff --git a/securityd/tests/testblobs.cpp b/securityd/tests/testblobs.cpp
new file mode 100644 (file)
index 0000000..c8c425e
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2000-2001,2003-2004 Apple Computer, 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 License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// testacls - ACL-related test cases.
+// 
+#include "testclient.h"
+#include "testutils.h"
+
+
+//
+// Blob tests.
+// Encodes and decodes Db and Key blobs and all that jazz.
+//
+void blobs()
+{
+       printf("* Database blob encryption test\n");
+       ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
+       
+       DbTester db1(ss, "/tmp/one", NULL, 60, true);
+       DbTester db2(ss, "/tmp/two", NULL, 30, false);
+       
+       // encode db1, purge it, decode it again
+       CssmData dbBlob;
+       ss.encodeDb(db1, dbBlob);
+       DbHandle db1a = ss.decodeDb(db1.dbId, &nullCred, dbBlob);
+       ss.releaseDb(db1);
+       if (db1 == db1a)
+               detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
+       DBParameters savedParams;
+       ss.getDbParameters(db1a, savedParams);
+       assert(savedParams.idleTimeout == db1.params.idleTimeout);
+       assert(savedParams.lockOnSleep == db1.params.lockOnSleep);
+       detail("Database encode/decode passed");
+       
+       // make sure the old handle isn't valid anymore
+       try {
+               ss.getDbParameters(db1, savedParams);
+               printf("OLD DATABASE HANDLE NOT PURGED (possibly wrong)\n");
+       } catch (const CssmCommonError &err) {
+               detail(err, "old DB handle rejected");
+       }
+    
+    // open db1 a second time (so now there's two db handles for db1)
+    DbHandle db1b = ss.decodeDb(db1.dbId, &nullCred, dbBlob);
+    
+    // release both db1 handles and db2
+    ss.releaseDb(db1a);
+    ss.releaseDb(db1b);
+    ss.releaseDb(db2);
+}
+
+
+//
+// Database tests.
+// Database locks/unlocks etc.
+//
+void databases()
+{
+       printf("* Database manipulation test\n");
+    CssmAllocator &alloc = CssmAllocator::standard();
+       ClientSession ss(alloc, alloc);
+    
+    AutoCredentials pwCred(alloc);
+    StringData passphrase("two");
+    StringData badPassphrase("three");
+    pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
+        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+        new(alloc) ListElement(passphrase));
+    pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+        new(alloc) ListElement(badPassphrase));
+       // pwCred = (NEW: two, OLD: three)
+       
+       DbTester db1(ss, "/tmp/one", NULL, 30, true);
+       DbTester db2(ss, "/tmp/two", &pwCred, 60, false);
+       // db2.passphrase = two
+       
+       // encode db1 and re-open it
+       CssmData dbBlob;
+       ss.encodeDb(db1, dbBlob);
+       DbHandle db1b = ss.decodeDb(db1.dbId, &nullCred, dbBlob);
+       if (db1b == db1.dbRef)
+               detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
+       
+    // open db1 a third time (so now there's three db handles for db1)
+    DbHandle db1c = ss.decodeDb(db1.dbId, &nullCred, dbBlob);
+    
+    // lock them to get started
+    ss.lock(db1);
+    ss.lock(db2);
+    
+    // unlock it through user
+    prompt("unlock");
+    ss.unlock(db1);
+       prompt();
+    ss.unlock(db1b);           // 2nd unlock should not prompt
+    ss.lock(db1c);                     // lock it again
+    prompt("unlock");
+    ss.unlock(db1);            // and that should prompt again
+       prompt();
+    
+    // db2 has a passphrase lock credentials - it'll work without U/I
+       db2.unlock("wrong passphrase");         // pw=two, cred=three
+    AutoCredentials pwCred2(alloc);
+    pwCred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+        new(alloc) ListElement(passphrase));
+       // pwCred2 = (OLD: two)
+    ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, &pwCred2); // set it
+       db2.unlock();
+    ss.lock(db2);
+    
+    // now change db2's passphrase
+    ss.lock(db2);
+    pwCred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
+        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+        new(alloc) ListElement(badPassphrase));
+       // pwCred2 = (OLD: two, NEW: three)
+       db2.changePassphrase(&pwCred2);
+       // passphrase = three, cred = (OLD: two)
+       
+    // encode and re-decode to make sure new data is there
+    CssmData blob2;
+    ss.encodeDb(db2, blob2);
+    DbHandle db2a = ss.decodeDb(db2.dbId, &pwCred, blob2);
+       // db2a cred = (OLD: two, NEW: three)
+    
+    // now, the *old* cred won't work anymore
+       db2.unlock("old passphrase accepted");
+
+    // back to the old credentials, which *do* have the (old bad, now good) passphrase
+    ss.lock(db2a);
+    ss.unlock(db2a);
+    detail("New passphrase accepted");
+       
+       // clear the credentials (this will prompt; cancel it)
+       ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, NULL);
+       prompt("cancel");
+       db2.unlock("null credential accepted");
+       prompt();
+       
+       // fell-swoop from-to change password operation
+       StringData newPassphrase("hollerith");
+       AutoCredentials pwCred3(alloc);
+       pwCred3 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
+               new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+               new(alloc) ListElement(newPassphrase));
+       pwCred3 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+               new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+               new(alloc) ListElement(passphrase));
+       db2.changePassphrase(&pwCred3, "accepting original (unchanged) passphrase");
+       
+       AutoCredentials pwCred4(alloc);
+       pwCred4 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
+               new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+               new(alloc) ListElement(newPassphrase));
+       pwCred4 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+               new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+               new(alloc) ListElement(badPassphrase));
+       db2.changePassphrase(&pwCred4);
+       
+       // final status check
+       AutoCredentials pwCred5(alloc);
+       pwCred5 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+               new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
+               new(alloc) ListElement(newPassphrase)); 
+       ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, &pwCred5);
+       db2.unlock();
+       detail("Final passphrase change verified");
+}
+
+
+//
+// Key encryption tests.
+//
+void keyBlobs()
+{
+       printf("* Keyblob encryption test\n");
+    CssmAllocator &alloc = CssmAllocator::standard();
+       ClientSession ss(alloc, alloc);
+    
+    DLDbIdentifier dbId1(ssuid, "/tmp/one", NULL);
+       DBParameters initialParams1 = { 3600, false };
+    
+    // create a new database
+       DbHandle db = ss.createDb(dbId1, NULL, NULL, initialParams1);
+       detail("Database created");
+       
+       // establish an ACL for the key
+       StringData theAclPassword("Strenge Geheimsache");
+       AclEntryPrototype initialAcl;
+       initialAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD,
+               new(alloc) ListElement(theAclPassword));
+       AclEntryInput initialAclInput(initialAcl);
+    
+    AutoCredentials cred(alloc);
+    cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD,
+        new(alloc) ListElement(theAclPassword));
+    
+    // generate a key
+       const CssmCryptoData seed(StringData("Farmers' day"));
+       FakeContext genContext(CSSM_ALGCLASS_KEYGEN, CSSM_ALGID_DES,
+               &::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH, 64),
+               &::Context::Attr(CSSM_ATTRIBUTE_SEED, seed),
+               NULL);
+    KeyHandle key;
+    CssmKey::Header header;
+    ss.generateKey(db, genContext, CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
+        CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
+        /*cred*/NULL, &initialAclInput, key, header);
+       detail("Key generated");
+    
+    // encrypt with the key
+    StringData clearText("Yet another boring cleartext sample string text sequence.");
+    StringData iv("Aardvark");
+    CssmKey nullKey; memset(&nullKey, 0, sizeof(nullKey));
+       FakeContext cryptoContext(CSSM_ALGCLASS_SYMMETRIC, CSSM_ALGID_DES,
+               &::Context::Attr(CSSM_ATTRIBUTE_KEY, nullKey),
+               &::Context::Attr(CSSM_ATTRIBUTE_INIT_VECTOR, iv),
+               &::Context::Attr(CSSM_ATTRIBUTE_MODE, CSSM_ALGMODE_CBC_IV8),
+               &::Context::Attr(CSSM_ATTRIBUTE_PADDING, CSSM_PADDING_PKCS1),
+        &::Context::Attr(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, cred),
+               NULL);
+       CssmData cipherText;
+       ss.encrypt(cryptoContext, key, clearText, cipherText);
+       detail("Plaintext encrypted with original key");
+    
+    // encode the key and release it
+    CssmData blob;
+    ss.encodeKey(key, blob);
+    ss.releaseKey(key);
+    detail("Key encoded and released");
+    
+    // decode it again, re-introducing it
+    CssmKey::Header decodedHeader;
+    KeyHandle key2 = ss.decodeKey(db, blob, decodedHeader);
+    detail("Key decoded");
+    
+    // decrypt with decoded key
+    CssmData recovered;
+    ss.decrypt(cryptoContext, key2, cipherText, recovered);
+    assert(recovered == clearText);
+    detail("Decoded key correctly decrypts ciphertext");
+    
+    // check a few header fields
+    if (!memcmp(&header, &decodedHeader, sizeof(header))) {
+        detail("All header fields match");
+    } else {
+        assert(header.algorithm() == decodedHeader.algorithm());
+        assert(header.blobType() == decodedHeader.blobType());
+        assert(header.blobFormat() == decodedHeader.blobFormat());
+        assert(header.keyClass() == decodedHeader.keyClass());
+        assert(header.attributes() == decodedHeader.attributes());
+        assert(header.usage() == decodedHeader.usage());
+        printf("Some header fields differ (probably okay)\n");
+    }
+    
+    // make sure we need the credentials (destructive)
+    memset(&cred, 0, sizeof(cred));
+    try {
+        ss.decrypt(cryptoContext, key2, cipherText, recovered);
+        error("RESTORED ACL FAILS TO RESTRICT");
+    } catch (CssmError &err) {
+        detail(err, "Restored key restricts access properly");
+    }
+}