X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/securityd/tests/testblobs.cpp diff --git a/securityd/tests/testblobs.cpp b/securityd/tests/testblobs.cpp new file mode 100644 index 00000000..c8c425ee --- /dev/null +++ b/securityd/tests/testblobs.cpp @@ -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"); + } +}