2 * Copyright (c) 2000-2001,2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 // testacls - ACL-related test cases.
30 #include "testclient.h"
31 #include "testutils.h"
36 // Encodes and decodes Db and Key blobs and all that jazz.
40 printf("* Database blob encryption test\n");
41 ClientSession
ss(CssmAllocator::standard(), CssmAllocator::standard());
43 DbTester
db1(ss
, "/tmp/one", NULL
, 60, true);
44 DbTester
db2(ss
, "/tmp/two", NULL
, 30, false);
46 // encode db1, purge it, decode it again
48 ss
.encodeDb(db1
, dbBlob
);
49 DbHandle db1a
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
52 detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
53 DBParameters savedParams
;
54 ss
.getDbParameters(db1a
, savedParams
);
55 assert(savedParams
.idleTimeout
== db1
.params
.idleTimeout
);
56 assert(savedParams
.lockOnSleep
== db1
.params
.lockOnSleep
);
57 detail("Database encode/decode passed");
59 // make sure the old handle isn't valid anymore
61 ss
.getDbParameters(db1
, savedParams
);
62 printf("OLD DATABASE HANDLE NOT PURGED (possibly wrong)\n");
63 } catch (const CssmCommonError
&err
) {
64 detail(err
, "old DB handle rejected");
67 // open db1 a second time (so now there's two db handles for db1)
68 DbHandle db1b
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
70 // release both db1 handles and db2
79 // Database locks/unlocks etc.
83 printf("* Database manipulation test\n");
84 CssmAllocator
&alloc
= CssmAllocator::standard();
85 ClientSession
ss(alloc
, alloc
);
87 AutoCredentials
pwCred(alloc
);
88 StringData
passphrase("two");
89 StringData
badPassphrase("three");
90 pwCred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
91 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
92 new(alloc
) ListElement(passphrase
));
93 pwCred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
94 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
95 new(alloc
) ListElement(badPassphrase
));
96 // pwCred = (NEW: two, OLD: three)
98 DbTester
db1(ss
, "/tmp/one", NULL
, 30, true);
99 DbTester
db2(ss
, "/tmp/two", &pwCred
, 60, false);
100 // db2.passphrase = two
102 // encode db1 and re-open it
104 ss
.encodeDb(db1
, dbBlob
);
105 DbHandle db1b
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
106 if (db1b
== db1
.dbRef
)
107 detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
109 // open db1 a third time (so now there's three db handles for db1)
110 DbHandle db1c
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
112 // lock them to get started
116 // unlock it through user
120 ss
.unlock(db1b
); // 2nd unlock should not prompt
121 ss
.lock(db1c
); // lock it again
123 ss
.unlock(db1
); // and that should prompt again
126 // db2 has a passphrase lock credentials - it'll work without U/I
127 db2
.unlock("wrong passphrase"); // pw=two, cred=three
128 AutoCredentials
pwCred2(alloc
);
129 pwCred2
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
130 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
131 new(alloc
) ListElement(passphrase
));
132 // pwCred2 = (OLD: two)
133 ss
.authenticateDb(db2
, CSSM_DB_ACCESS_WRITE
, &pwCred2
); // set it
137 // now change db2's passphrase
139 pwCred2
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
140 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
141 new(alloc
) ListElement(badPassphrase
));
142 // pwCred2 = (OLD: two, NEW: three)
143 db2
.changePassphrase(&pwCred2
);
144 // passphrase = three, cred = (OLD: two)
146 // encode and re-decode to make sure new data is there
148 ss
.encodeDb(db2
, blob2
);
149 DbHandle db2a
= ss
.decodeDb(db2
.dbId
, &pwCred
, blob2
);
150 // db2a cred = (OLD: two, NEW: three)
152 // now, the *old* cred won't work anymore
153 db2
.unlock("old passphrase accepted");
155 // back to the old credentials, which *do* have the (old bad, now good) passphrase
158 detail("New passphrase accepted");
160 // clear the credentials (this will prompt; cancel it)
161 ss
.authenticateDb(db2
, CSSM_DB_ACCESS_WRITE
, NULL
);
163 db2
.unlock("null credential accepted");
166 // fell-swoop from-to change password operation
167 StringData
newPassphrase("hollerith");
168 AutoCredentials
pwCred3(alloc
);
169 pwCred3
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
170 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
171 new(alloc
) ListElement(newPassphrase
));
172 pwCred3
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
173 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
174 new(alloc
) ListElement(passphrase
));
175 db2
.changePassphrase(&pwCred3
, "accepting original (unchanged) passphrase");
177 AutoCredentials
pwCred4(alloc
);
178 pwCred4
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
179 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
180 new(alloc
) ListElement(newPassphrase
));
181 pwCred4
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
182 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
183 new(alloc
) ListElement(badPassphrase
));
184 db2
.changePassphrase(&pwCred4
);
186 // final status check
187 AutoCredentials
pwCred5(alloc
);
188 pwCred5
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
189 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
190 new(alloc
) ListElement(newPassphrase
));
191 ss
.authenticateDb(db2
, CSSM_DB_ACCESS_WRITE
, &pwCred5
);
193 detail("Final passphrase change verified");
198 // Key encryption tests.
202 printf("* Keyblob encryption test\n");
203 CssmAllocator
&alloc
= CssmAllocator::standard();
204 ClientSession
ss(alloc
, alloc
);
206 DLDbIdentifier
dbId1(ssuid
, "/tmp/one", NULL
);
207 DBParameters initialParams1
= { 3600, false };
209 // create a new database
210 DbHandle db
= ss
.createDb(dbId1
, NULL
, NULL
, initialParams1
);
211 detail("Database created");
213 // establish an ACL for the key
214 StringData
theAclPassword("Strenge Geheimsache");
215 AclEntryPrototype initialAcl
;
216 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
217 new(alloc
) ListElement(theAclPassword
));
218 AclEntryInput
initialAclInput(initialAcl
);
220 AutoCredentials
cred(alloc
);
221 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
222 new(alloc
) ListElement(theAclPassword
));
225 const CssmCryptoData
seed(StringData("Farmers' day"));
226 FakeContext
genContext(CSSM_ALGCLASS_KEYGEN
, CSSM_ALGID_DES
,
227 &::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH
, 64),
228 &::Context::Attr(CSSM_ATTRIBUTE_SEED
, seed
),
231 CssmKey::Header header
;
232 ss
.generateKey(db
, genContext
, CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
233 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_PERMANENT
,
234 /*cred*/NULL
, &initialAclInput
, key
, header
);
235 detail("Key generated");
237 // encrypt with the key
238 StringData
clearText("Yet another boring cleartext sample string text sequence.");
239 StringData
iv("Aardvark");
240 CssmKey nullKey
; memset(&nullKey
, 0, sizeof(nullKey
));
241 FakeContext
cryptoContext(CSSM_ALGCLASS_SYMMETRIC
, CSSM_ALGID_DES
,
242 &::Context::Attr(CSSM_ATTRIBUTE_KEY
, nullKey
),
243 &::Context::Attr(CSSM_ATTRIBUTE_INIT_VECTOR
, iv
),
244 &::Context::Attr(CSSM_ATTRIBUTE_MODE
, CSSM_ALGMODE_CBC_IV8
),
245 &::Context::Attr(CSSM_ATTRIBUTE_PADDING
, CSSM_PADDING_PKCS1
),
246 &::Context::Attr(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS
, cred
),
249 ss
.encrypt(cryptoContext
, key
, clearText
, cipherText
);
250 detail("Plaintext encrypted with original key");
252 // encode the key and release it
254 ss
.encodeKey(key
, blob
);
256 detail("Key encoded and released");
258 // decode it again, re-introducing it
259 CssmKey::Header decodedHeader
;
260 KeyHandle key2
= ss
.decodeKey(db
, blob
, decodedHeader
);
261 detail("Key decoded");
263 // decrypt with decoded key
265 ss
.decrypt(cryptoContext
, key2
, cipherText
, recovered
);
266 assert(recovered
== clearText
);
267 detail("Decoded key correctly decrypts ciphertext");
269 // check a few header fields
270 if (!memcmp(&header
, &decodedHeader
, sizeof(header
))) {
271 detail("All header fields match");
273 assert(header
.algorithm() == decodedHeader
.algorithm());
274 assert(header
.blobType() == decodedHeader
.blobType());
275 assert(header
.blobFormat() == decodedHeader
.blobFormat());
276 assert(header
.keyClass() == decodedHeader
.keyClass());
277 assert(header
.attributes() == decodedHeader
.attributes());
278 assert(header
.usage() == decodedHeader
.usage());
279 printf("Some header fields differ (probably okay)\n");
282 // make sure we need the credentials (destructive)
283 memset(&cred
, 0, sizeof(cred
));
285 ss
.decrypt(cryptoContext
, key2
, cipherText
, recovered
);
286 error("RESTORED ACL FAILS TO RESTRICT");
287 } catch (CssmError
&err
) {
288 detail(err
, "Restored key restricts access properly");