2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 // testacls - ACL-related test cases.
22 #include "testclient.h"
23 #include "testutils.h"
28 // Encodes and decodes Db and Key blobs and all that jazz.
32 printf("* Database blob encryption test\n");
33 ClientSession
ss(CssmAllocator::standard(), CssmAllocator::standard());
35 DbTester
db1(ss
, "/tmp/one", NULL
, 60, true);
36 DbTester
db2(ss
, "/tmp/two", NULL
, 30, false);
38 // encode db1, purge it, decode it again
40 ss
.encodeDb(db1
, dbBlob
);
41 DbHandle db1a
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
44 detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
45 DBParameters savedParams
;
46 ss
.getDbParameters(db1a
, savedParams
);
47 assert(savedParams
.idleTimeout
== db1
.params
.idleTimeout
);
48 assert(savedParams
.lockOnSleep
== db1
.params
.lockOnSleep
);
49 detail("Database encode/decode passed");
51 // make sure the old handle isn't valid anymore
53 ss
.getDbParameters(db1
, savedParams
);
54 printf("OLD DATABASE HANDLE NOT PURGED (possibly wrong)\n");
55 } catch (const CssmCommonError
&err
) {
56 detail(err
, "old DB handle rejected");
59 // open db1 a second time (so now there's two db handles for db1)
60 DbHandle db1b
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
62 // release both db1 handles and db2
71 // Database locks/unlocks etc.
75 printf("* Database manipulation test\n");
76 CssmAllocator
&alloc
= CssmAllocator::standard();
77 ClientSession
ss(alloc
, alloc
);
79 AutoCredentials
pwCred(alloc
);
80 StringData
passphrase("two");
81 StringData
badPassphrase("three");
82 pwCred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
83 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
84 new(alloc
) ListElement(passphrase
));
85 pwCred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
86 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
87 new(alloc
) ListElement(badPassphrase
));
88 // pwCred = (NEW: two, OLD: three)
90 DbTester
db1(ss
, "/tmp/one", NULL
, 30, true);
91 DbTester
db2(ss
, "/tmp/two", &pwCred
, 60, false);
92 // db2.passphrase = two
94 // encode db1 and re-open it
96 ss
.encodeDb(db1
, dbBlob
);
97 DbHandle db1b
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
98 if (db1b
== db1
.dbRef
)
99 detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
101 // open db1 a third time (so now there's three db handles for db1)
102 DbHandle db1c
= ss
.decodeDb(db1
.dbId
, &nullCred
, dbBlob
);
104 // lock them to get started
108 // unlock it through user
112 ss
.unlock(db1b
); // 2nd unlock should not prompt
113 ss
.lock(db1c
); // lock it again
115 ss
.unlock(db1
); // and that should prompt again
118 // db2 has a passphrase lock credentials - it'll work without U/I
119 db2
.unlock("wrong passphrase"); // pw=two, cred=three
120 AutoCredentials
pwCred2(alloc
);
121 pwCred2
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
122 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
123 new(alloc
) ListElement(passphrase
));
124 // pwCred2 = (OLD: two)
125 ss
.authenticateDb(db2
, CSSM_DB_ACCESS_WRITE
, &pwCred2
); // set it
129 // now change db2's passphrase
131 pwCred2
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
132 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
133 new(alloc
) ListElement(badPassphrase
));
134 // pwCred2 = (OLD: two, NEW: three)
135 db2
.changePassphrase(&pwCred2
);
136 // passphrase = three, cred = (OLD: two)
138 // encode and re-decode to make sure new data is there
140 ss
.encodeDb(db2
, blob2
);
141 DbHandle db2a
= ss
.decodeDb(db2
.dbId
, &pwCred
, blob2
);
142 // db2a cred = (OLD: two, NEW: three)
144 // now, the *old* cred won't work anymore
145 db2
.unlock("old passphrase accepted");
147 // back to the old credentials, which *do* have the (old bad, now good) passphrase
150 detail("New passphrase accepted");
152 // clear the credentials (this will prompt; cancel it)
153 ss
.authenticateDb(db2
, CSSM_DB_ACCESS_WRITE
, NULL
);
155 db2
.unlock("null credential accepted");
158 // fell-swoop from-to change password operation
159 StringData
newPassphrase("hollerith");
160 AutoCredentials
pwCred3(alloc
);
161 pwCred3
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
162 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
163 new(alloc
) ListElement(newPassphrase
));
164 pwCred3
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
165 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
166 new(alloc
) ListElement(passphrase
));
167 db2
.changePassphrase(&pwCred3
, "accepting original (unchanged) passphrase");
169 AutoCredentials
pwCred4(alloc
);
170 pwCred4
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
,
171 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
172 new(alloc
) ListElement(newPassphrase
));
173 pwCred4
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
174 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
175 new(alloc
) ListElement(badPassphrase
));
176 db2
.changePassphrase(&pwCred4
);
178 // final status check
179 AutoCredentials
pwCred5(alloc
);
180 pwCred5
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
181 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
),
182 new(alloc
) ListElement(newPassphrase
));
183 ss
.authenticateDb(db2
, CSSM_DB_ACCESS_WRITE
, &pwCred5
);
185 detail("Final passphrase change verified");
190 // Key encryption tests.
194 printf("* Keyblob encryption test\n");
195 CssmAllocator
&alloc
= CssmAllocator::standard();
196 ClientSession
ss(alloc
, alloc
);
198 DLDbIdentifier
dbId1(ssuid
, "/tmp/one", NULL
);
199 DBParameters initialParams1
= { 3600, false };
201 // create a new database
202 DbHandle db
= ss
.createDb(dbId1
, NULL
, NULL
, initialParams1
);
203 detail("Database created");
205 // establish an ACL for the key
206 StringData
theAclPassword("Strenge Geheimsache");
207 AclEntryPrototype initialAcl
;
208 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
209 new(alloc
) ListElement(theAclPassword
));
210 AclEntryInput
initialAclInput(initialAcl
);
212 AutoCredentials
cred(alloc
);
213 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
214 new(alloc
) ListElement(theAclPassword
));
217 const CssmCryptoData
seed(StringData("Farmers' day"));
218 FakeContext
genContext(CSSM_ALGCLASS_KEYGEN
, CSSM_ALGID_DES
,
219 &::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH
, 64),
220 &::Context::Attr(CSSM_ATTRIBUTE_SEED
, seed
),
223 CssmKey::Header header
;
224 ss
.generateKey(db
, genContext
, CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
,
225 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_PERMANENT
,
226 /*cred*/NULL
, &initialAclInput
, key
, header
);
227 detail("Key generated");
229 // encrypt with the key
230 StringData
clearText("Yet another boring cleartext sample string text sequence.");
231 StringData
iv("Aardvark");
232 CssmKey nullKey
; memset(&nullKey
, 0, sizeof(nullKey
));
233 FakeContext
cryptoContext(CSSM_ALGCLASS_SYMMETRIC
, CSSM_ALGID_DES
,
234 &::Context::Attr(CSSM_ATTRIBUTE_KEY
, nullKey
),
235 &::Context::Attr(CSSM_ATTRIBUTE_INIT_VECTOR
, iv
),
236 &::Context::Attr(CSSM_ATTRIBUTE_MODE
, CSSM_ALGMODE_CBC_IV8
),
237 &::Context::Attr(CSSM_ATTRIBUTE_PADDING
, CSSM_PADDING_PKCS1
),
238 &::Context::Attr(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS
, cred
),
241 ss
.encrypt(cryptoContext
, key
, clearText
, cipherText
);
242 detail("Plaintext encrypted with original key");
244 // encode the key and release it
246 ss
.encodeKey(key
, blob
);
248 detail("Key encoded and released");
250 // decode it again, re-introducing it
251 CssmKey::Header decodedHeader
;
252 KeyHandle key2
= ss
.decodeKey(db
, blob
, decodedHeader
);
253 detail("Key decoded");
255 // decrypt with decoded key
257 ss
.decrypt(cryptoContext
, key2
, cipherText
, recovered
);
258 assert(recovered
== clearText
);
259 detail("Decoded key correctly decrypts ciphertext");
261 // check a few header fields
262 if (!memcmp(&header
, &decodedHeader
, sizeof(header
))) {
263 detail("All header fields match");
265 assert(header
.algorithm() == decodedHeader
.algorithm());
266 assert(header
.blobType() == decodedHeader
.blobType());
267 assert(header
.blobFormat() == decodedHeader
.blobFormat());
268 assert(header
.keyClass() == decodedHeader
.keyClass());
269 assert(header
.attributes() == decodedHeader
.attributes());
270 assert(header
.usage() == decodedHeader
.usage());
271 printf("Some header fields differ (probably okay)\n");
274 // make sure we need the credentials (destructive)
275 memset(&cred
, 0, sizeof(cred
));
277 ss
.decrypt(cryptoContext
, key2
, cipherText
, recovered
);
278 error("RESTORED ACL FAILS TO RESTRICT");
279 } catch (CssmError
&err
) {
280 detail(err
, "Restored key restricts access properly");