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