1 //
2 // Copyright (C) 2015 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "attestation/server/pkcs11_key_store.h"
18
19 #include <memory>
20 #include <string>
21
22 #include <base/bind.h>
23 #include <base/callback.h>
24 #include <base/files/file_path.h>
25 #include <base/logging.h>
26 #include <base/stl_util.h>
27 #include <base/strings/string_util.h>
28 #include <chaps/isolate.h>
29 #include <chaps/pkcs11/cryptoki.h>
30 #include <chaps/token_manager_client.h>
31 #include <brillo/cryptohome.h>
32 #include <crypto/scoped_openssl_types.h>
33 #include <openssl/rsa.h>
34 #include <openssl/sha.h>
35 #include <openssl/x509.h>
36
37 namespace {
38
Sha1(const std::string & input)39 std::string Sha1(const std::string& input) {
40 unsigned char output[SHA_DIGEST_LENGTH];
41 SHA1(reinterpret_cast<const unsigned char*>(input.data()), input.size(),
42 output);
43 return std::string(reinterpret_cast<char*>(output), SHA_DIGEST_LENGTH);
44 }
45
46 } // namespace
47
48 namespace attestation {
49
50 typedef crypto::ScopedOpenSSL<X509, X509_free> ScopedX509;
51
52 // An arbitrary application ID to identify PKCS #11 objects.
53 const char kApplicationID[] = "CrOS_d5bbc079d2497110feadfc97c40d718ae46f4658";
54
55 // A helper class to scope a PKCS #11 session.
56 class ScopedSession {
57 public:
ScopedSession(CK_SLOT_ID slot)58 explicit ScopedSession(CK_SLOT_ID slot) : handle_(CK_INVALID_HANDLE) {
59 CK_RV rv = C_Initialize(nullptr);
60 if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
61 // This may be normal in a test environment.
62 LOG(INFO) << "PKCS #11 is not available.";
63 return;
64 }
65 CK_FLAGS flags = CKF_RW_SESSION | CKF_SERIAL_SESSION;
66 if (C_OpenSession(slot, flags, nullptr, nullptr, &handle_) != CKR_OK) {
67 LOG(ERROR) << "Failed to open PKCS #11 session.";
68 return;
69 }
70 }
71
~ScopedSession()72 ~ScopedSession() {
73 if (IsValid() && (C_CloseSession(handle_) != CKR_OK)) {
74 LOG(WARNING) << "Failed to close PKCS #11 session.";
75 handle_ = CK_INVALID_HANDLE;
76 }
77 }
78
handle() const79 CK_SESSION_HANDLE handle() const {
80 return handle_;
81 }
82
IsValid() const83 bool IsValid() const {
84 return (handle_ != CK_INVALID_HANDLE);
85 }
86
87 private:
88 CK_SESSION_HANDLE handle_;
89
90 DISALLOW_COPY_AND_ASSIGN(ScopedSession);
91 };
92
Pkcs11KeyStore(chaps::TokenManagerClient * token_manager)93 Pkcs11KeyStore::Pkcs11KeyStore(chaps::TokenManagerClient* token_manager)
94 : token_manager_(token_manager) {}
95
~Pkcs11KeyStore()96 Pkcs11KeyStore::~Pkcs11KeyStore() {}
97
Read(const std::string & username,const std::string & key_name,std::string * key_data)98 bool Pkcs11KeyStore::Read(const std::string& username,
99 const std::string& key_name,
100 std::string* key_data) {
101 CK_SLOT_ID slot;
102 if (!GetUserSlot(username, &slot)) {
103 LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
104 return false;
105 }
106 ScopedSession session(slot);
107 if (!session.IsValid()) {
108 LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
109 return false;
110 }
111 CK_OBJECT_HANDLE key_handle = FindObject(session.handle(), key_name);
112 if (key_handle == CK_INVALID_HANDLE) {
113 LOG(WARNING) << "Pkcs11KeyStore: Key does not exist: " << key_name;
114 return false;
115 }
116 // First get the attribute with a NULL buffer which will give us the length.
117 CK_ATTRIBUTE attribute = {CKA_VALUE, nullptr, 0};
118 if (C_GetAttributeValue(session.handle(),
119 key_handle,
120 &attribute, 1) != CKR_OK) {
121 LOG(ERROR) << "Pkcs11KeyStore: Failed to read key data: " << key_name;
122 return false;
123 }
124 key_data->resize(attribute.ulValueLen);
125 attribute.pValue = string_as_array(key_data);
126 if (C_GetAttributeValue(session.handle(),
127 key_handle,
128 &attribute, 1) != CKR_OK) {
129 LOG(ERROR) << "Pkcs11KeyStore: Failed to read key data: " << key_name;
130 return false;
131 }
132 key_data->resize(attribute.ulValueLen);
133 return true;
134 }
135
Write(const std::string & username,const std::string & key_name,const std::string & key_data)136 bool Pkcs11KeyStore::Write(const std::string& username,
137 const std::string& key_name,
138 const std::string& key_data) {
139 // Delete any existing key with the same name.
140 if (!Delete(username, key_name)) {
141 return false;
142 }
143 CK_SLOT_ID slot;
144 if (!GetUserSlot(username, &slot)) {
145 LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
146 return false;
147 }
148 ScopedSession session(slot);
149 if (!session.IsValid()) {
150 LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
151 return false;
152 }
153 std::string mutable_key_name(key_name);
154 std::string mutable_key_data(key_data);
155 std::string mutable_application_id(kApplicationID);
156 // Create a new data object for the key.
157 CK_OBJECT_CLASS object_class = CKO_DATA;
158 CK_BBOOL true_value = CK_TRUE;
159 CK_BBOOL false_value = CK_FALSE;
160 CK_ATTRIBUTE attributes[] = {
161 {CKA_CLASS, &object_class, sizeof(object_class)},
162 {
163 CKA_LABEL,
164 string_as_array(&mutable_key_name),
165 mutable_key_name.size()
166 },
167 {
168 CKA_VALUE,
169 string_as_array(&mutable_key_data),
170 mutable_key_data.size()
171 },
172 {
173 CKA_APPLICATION,
174 string_as_array(&mutable_application_id),
175 mutable_application_id.size()
176 },
177 {CKA_TOKEN, &true_value, sizeof(true_value)},
178 {CKA_PRIVATE, &true_value, sizeof(true_value)},
179 {CKA_MODIFIABLE, &false_value, sizeof(false_value)}
180 };
181 CK_OBJECT_HANDLE key_handle = CK_INVALID_HANDLE;
182 if (C_CreateObject(session.handle(),
183 attributes,
184 arraysize(attributes),
185 &key_handle) != CKR_OK) {
186 LOG(ERROR) << "Pkcs11KeyStore: Failed to write key data: " << key_name;
187 return false;
188 }
189 return true;
190 }
191
Delete(const std::string & username,const std::string & key_name)192 bool Pkcs11KeyStore::Delete(const std::string& username,
193 const std::string& key_name) {
194 CK_SLOT_ID slot;
195 if (!GetUserSlot(username, &slot)) {
196 LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
197 return false;
198 }
199 ScopedSession session(slot);
200 if (!session.IsValid()) {
201 LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
202 return false;
203 }
204 CK_OBJECT_HANDLE key_handle = FindObject(session.handle(), key_name);
205 if (key_handle != CK_INVALID_HANDLE) {
206 if (C_DestroyObject(session.handle(), key_handle) != CKR_OK) {
207 LOG(ERROR) << "Pkcs11KeyStore: Failed to delete key data.";
208 return false;
209 }
210 }
211 return true;
212 }
213
DeleteByPrefix(const std::string & username,const std::string & key_prefix)214 bool Pkcs11KeyStore::DeleteByPrefix(const std::string& username,
215 const std::string& key_prefix) {
216 CK_SLOT_ID slot;
217 if (!GetUserSlot(username, &slot)) {
218 LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
219 return false;
220 }
221 ScopedSession session(slot);
222 if (!session.IsValid()) {
223 LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
224 return false;
225 }
226 EnumObjectsCallback callback = base::Bind(
227 &Pkcs11KeyStore::DeleteIfMatchesPrefix,
228 base::Unretained(this),
229 session.handle(),
230 key_prefix);
231 if (!EnumObjects(session.handle(), callback)) {
232 LOG(ERROR) << "Pkcs11KeyStore: Failed to delete key data.";
233 return false;
234 }
235 return true;
236 }
237
Register(const std::string & username,const std::string & label,KeyType key_type,KeyUsage key_usage,const std::string & private_key_blob,const std::string & public_key_der,const std::string & certificate)238 bool Pkcs11KeyStore::Register(const std::string& username,
239 const std::string& label,
240 KeyType key_type,
241 KeyUsage key_usage,
242 const std::string& private_key_blob,
243 const std::string& public_key_der,
244 const std::string& certificate) {
245 const CK_ATTRIBUTE_TYPE kKeyBlobAttribute = CKA_VENDOR_DEFINED + 1;
246
247 if (key_type != KEY_TYPE_RSA) {
248 LOG(ERROR) << "Pkcs11KeyStore: Only RSA supported.";
249 return false;
250 }
251 CK_SLOT_ID slot;
252 if (!GetUserSlot(username, &slot)) {
253 LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
254 return false;
255 }
256 ScopedSession session(slot);
257 if (!session.IsValid()) {
258 LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
259 return false;
260 }
261
262 // Extract the modulus from the public key.
263 const unsigned char* asn1_ptr = reinterpret_cast<const unsigned char*>(
264 public_key_der.data());
265 crypto::ScopedRSA public_key(d2i_RSAPublicKey(nullptr,
266 &asn1_ptr,
267 public_key_der.size()));
268 if (!public_key.get()) {
269 LOG(ERROR) << "Pkcs11KeyStore: Failed to decode public key.";
270 return false;
271 }
272 std::string modulus(BN_num_bytes(public_key.get()->n), 0);
273 int length = BN_bn2bin(public_key.get()->n, reinterpret_cast<unsigned char*>(
274 string_as_array(&modulus)));
275 if (length <= 0) {
276 LOG(ERROR) << "Pkcs11KeyStore: Failed to extract public key modulus.";
277 return false;
278 }
279 modulus.resize(length);
280
281 // Construct a PKCS #11 template for the public key object.
282 CK_BBOOL true_value = CK_TRUE;
283 CK_BBOOL false_value = CK_FALSE;
284 CK_KEY_TYPE p11_key_type = CKK_RSA;
285 CK_OBJECT_CLASS public_key_class = CKO_PUBLIC_KEY;
286 std::string id = Sha1(modulus);
287 std::string mutable_label(label);
288 CK_ULONG modulus_bits = modulus.size() * 8;
289 CK_BBOOL sign_usage = (key_usage == KEY_USAGE_SIGN);
290 CK_BBOOL decrypt_usage = (key_usage == KEY_USAGE_DECRYPT);
291 unsigned char public_exponent[] = {1, 0, 1};
292 CK_ATTRIBUTE public_key_attributes[] = {
293 {CKA_CLASS, &public_key_class, sizeof(public_key_class)},
294 {CKA_TOKEN, &true_value, sizeof(true_value)},
295 {CKA_DERIVE, &false_value, sizeof(false_value)},
296 {CKA_WRAP, &false_value, sizeof(false_value)},
297 {CKA_VERIFY, &sign_usage, sizeof(sign_usage)},
298 {CKA_VERIFY_RECOVER, &false_value, sizeof(false_value)},
299 {CKA_ENCRYPT, &decrypt_usage, sizeof(decrypt_usage)},
300 {CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
301 {CKA_ID, string_as_array(&id), id.size()},
302 {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
303 {CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)},
304 {CKA_PUBLIC_EXPONENT, public_exponent, arraysize(public_exponent)},
305 {CKA_MODULUS, string_as_array(&modulus), modulus.size()}
306 };
307
308 CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
309 if (C_CreateObject(session.handle(),
310 public_key_attributes,
311 arraysize(public_key_attributes),
312 &object_handle) != CKR_OK) {
313 LOG(ERROR) << "Pkcs11KeyStore: Failed to create public key object.";
314 return false;
315 }
316
317 // Construct a PKCS #11 template for the private key object.
318 std::string mutable_private_key_blob(private_key_blob);
319 CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
320 CK_ATTRIBUTE private_key_attributes[] = {
321 {CKA_CLASS, &private_key_class, sizeof(private_key_class)},
322 {CKA_TOKEN, &true_value, sizeof(true_value)},
323 {CKA_PRIVATE, &true_value, sizeof(true_value)},
324 {CKA_SENSITIVE, &true_value, sizeof(true_value)},
325 {CKA_EXTRACTABLE, &false_value, sizeof(false_value)},
326 {CKA_DERIVE, &false_value, sizeof(false_value)},
327 {CKA_UNWRAP, &false_value, sizeof(false_value)},
328 {CKA_SIGN, &sign_usage, sizeof(sign_usage)},
329 {CKA_SIGN_RECOVER, &false_value, sizeof(false_value)},
330 {CKA_DECRYPT, &decrypt_usage, sizeof(decrypt_usage)},
331 {CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
332 {CKA_ID, string_as_array(&id), id.size()},
333 {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
334 {CKA_PUBLIC_EXPONENT, public_exponent, arraysize(public_exponent)},
335 {CKA_MODULUS, string_as_array(&modulus), modulus.size()},
336 {
337 kKeyBlobAttribute,
338 string_as_array(&mutable_private_key_blob),
339 mutable_private_key_blob.size()
340 }
341 };
342
343 if (C_CreateObject(session.handle(),
344 private_key_attributes,
345 arraysize(private_key_attributes),
346 &object_handle) != CKR_OK) {
347 LOG(ERROR) << "Pkcs11KeyStore: Failed to create private key object.";
348 return false;
349 }
350
351 if (!certificate.empty()) {
352 std::string subject;
353 std::string issuer;
354 std::string serial_number;
355 if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
356 LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
357 }
358 // Construct a PKCS #11 template for a certificate object.
359 std::string mutable_certificate = certificate;
360 CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
361 CK_CERTIFICATE_TYPE certificate_type = CKC_X_509;
362 CK_ATTRIBUTE certificate_attributes[] = {
363 {CKA_CLASS, &certificate_class, sizeof(certificate_class)},
364 {CKA_TOKEN, &true_value, sizeof(true_value)},
365 {CKA_PRIVATE, &false_value, sizeof(false_value)},
366 {CKA_ID, string_as_array(&id), id.size()},
367 {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
368 {CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
369 {CKA_SUBJECT, string_as_array(&subject), subject.size()},
370 {CKA_ISSUER, string_as_array(&issuer), issuer.size()},
371 {
372 CKA_SERIAL_NUMBER,
373 string_as_array(&serial_number),
374 serial_number.size()
375 },
376 {
377 CKA_VALUE,
378 string_as_array(&mutable_certificate),
379 mutable_certificate.size()
380 }
381 };
382
383 if (C_CreateObject(session.handle(),
384 certificate_attributes,
385 arraysize(certificate_attributes),
386 &object_handle) != CKR_OK) {
387 LOG(ERROR) << "Pkcs11KeyStore: Failed to create certificate object.";
388 return false;
389 }
390 }
391
392 // Close all sessions in an attempt to trigger other modules to find the new
393 // objects.
394 C_CloseAllSessions(slot);
395
396 return true;
397 }
398
RegisterCertificate(const std::string & username,const std::string & certificate)399 bool Pkcs11KeyStore::RegisterCertificate(const std::string& username,
400 const std::string& certificate) {
401 CK_SLOT_ID slot;
402 if (!GetUserSlot(username, &slot)) {
403 LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
404 return false;
405 }
406 ScopedSession session(slot);
407 if (!session.IsValid()) {
408 LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
409 return false;
410 }
411
412 if (DoesCertificateExist(session.handle(), certificate)) {
413 LOG(INFO) << "Pkcs11KeyStore: Certificate already exists.";
414 return true;
415 }
416 std::string subject;
417 std::string issuer;
418 std::string serial_number;
419 if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
420 LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
421 }
422 // Construct a PKCS #11 template for a certificate object.
423 std::string mutable_certificate = certificate;
424 CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
425 CK_CERTIFICATE_TYPE certificate_type = CKC_X_509;
426 CK_BBOOL true_value = CK_TRUE;
427 CK_BBOOL false_value = CK_FALSE;
428 CK_ATTRIBUTE certificate_attributes[] = {
429 {CKA_CLASS, &certificate_class, sizeof(certificate_class)},
430 {CKA_TOKEN, &true_value, sizeof(true_value)},
431 {CKA_PRIVATE, &false_value, sizeof(false_value)},
432 {CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
433 {CKA_SUBJECT, string_as_array(&subject), subject.size()},
434 {CKA_ISSUER, string_as_array(&issuer), issuer.size()},
435 {CKA_SERIAL_NUMBER, string_as_array(&serial_number), serial_number.size()},
436 {
437 CKA_VALUE,
438 string_as_array(&mutable_certificate),
439 mutable_certificate.size()
440 }
441 };
442 CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
443 if (C_CreateObject(session.handle(),
444 certificate_attributes,
445 arraysize(certificate_attributes),
446 &object_handle) != CKR_OK) {
447 LOG(ERROR) << "Pkcs11KeyStore: Failed to create certificate object.";
448 return false;
449 }
450 return true;
451 }
452
FindObject(CK_SESSION_HANDLE session_handle,const std::string & key_name)453 CK_OBJECT_HANDLE Pkcs11KeyStore::FindObject(CK_SESSION_HANDLE session_handle,
454 const std::string& key_name) {
455 // Assemble a search template.
456 std::string mutable_key_name(key_name);
457 std::string mutable_application_id(kApplicationID);
458 CK_OBJECT_CLASS object_class = CKO_DATA;
459 CK_BBOOL true_value = CK_TRUE;
460 CK_BBOOL false_value = CK_FALSE;
461 CK_ATTRIBUTE attributes[] = {
462 {CKA_CLASS, &object_class, sizeof(object_class)},
463 {
464 CKA_LABEL,
465 string_as_array(&mutable_key_name),
466 mutable_key_name.size()
467 },
468 {
469 CKA_APPLICATION,
470 string_as_array(&mutable_application_id),
471 mutable_application_id.size()
472 },
473 {CKA_TOKEN, &true_value, sizeof(true_value)},
474 {CKA_PRIVATE, &true_value, sizeof(true_value)},
475 {CKA_MODIFIABLE, &false_value, sizeof(false_value)}
476 };
477 CK_OBJECT_HANDLE key_handle = CK_INVALID_HANDLE;
478 CK_ULONG count = 0;
479 if ((C_FindObjectsInit(session_handle,
480 attributes,
481 arraysize(attributes)) != CKR_OK) ||
482 (C_FindObjects(session_handle, &key_handle, 1, &count) != CKR_OK) ||
483 (C_FindObjectsFinal(session_handle) != CKR_OK)) {
484 LOG(ERROR) << "Key search failed: " << key_name;
485 return CK_INVALID_HANDLE;
486 }
487 if (count == 1)
488 return key_handle;
489 return CK_INVALID_HANDLE;
490 }
491
GetUserSlot(const std::string & username,CK_SLOT_ID_PTR slot)492 bool Pkcs11KeyStore::GetUserSlot(const std::string& username,
493 CK_SLOT_ID_PTR slot) {
494 const char kChapsDaemonName[] = "chaps";
495 const char kChapsSystemToken[] = "/var/lib/chaps";
496 base::FilePath token_path = username.empty() ?
497 base::FilePath(kChapsSystemToken) :
498 brillo::cryptohome::home::GetDaemonPath(username, kChapsDaemonName);
499 CK_RV rv;
500 rv = C_Initialize(nullptr);
501 if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
502 LOG(WARNING) << __func__ << ": C_Initialize failed.";
503 return false;
504 }
505 CK_ULONG num_slots = 0;
506 rv = C_GetSlotList(CK_TRUE, nullptr, &num_slots);
507 if (rv != CKR_OK) {
508 LOG(WARNING) << __func__ << ": C_GetSlotList(nullptr) failed.";
509 return false;
510 }
511 std::unique_ptr<CK_SLOT_ID[]> slot_list(new CK_SLOT_ID[num_slots]);
512 rv = C_GetSlotList(CK_TRUE, slot_list.get(), &num_slots);
513 if (rv != CKR_OK) {
514 LOG(WARNING) << __func__ << ": C_GetSlotList failed.";
515 return false;
516 }
517 // Look through all slots for |token_path|.
518 for (CK_ULONG i = 0; i < num_slots; ++i) {
519 base::FilePath slot_path;
520 if (token_manager_->GetTokenPath(
521 chaps::IsolateCredentialManager::GetDefaultIsolateCredential(),
522 slot_list[i],
523 &slot_path) && (token_path == slot_path)) {
524 *slot = slot_list[i];
525 return true;
526 }
527 }
528 LOG(WARNING) << __func__ << ": Path not found.";
529 return false;
530 }
531
EnumObjects(CK_SESSION_HANDLE session_handle,const Pkcs11KeyStore::EnumObjectsCallback & callback)532 bool Pkcs11KeyStore::EnumObjects(
533 CK_SESSION_HANDLE session_handle,
534 const Pkcs11KeyStore::EnumObjectsCallback& callback) {
535 std::string mutable_application_id(kApplicationID);
536 // Assemble a search template.
537 CK_OBJECT_CLASS object_class = CKO_DATA;
538 CK_BBOOL true_value = CK_TRUE;
539 CK_BBOOL false_value = CK_FALSE;
540 CK_ATTRIBUTE attributes[] = {
541 {CKA_CLASS, &object_class, sizeof(object_class)},
542 {
543 CKA_APPLICATION,
544 string_as_array(&mutable_application_id),
545 mutable_application_id.size()
546 },
547 {CKA_TOKEN, &true_value, sizeof(true_value)},
548 {CKA_PRIVATE, &true_value, sizeof(true_value)},
549 {CKA_MODIFIABLE, &false_value, sizeof(false_value)}
550 };
551 const CK_ULONG kMaxHandles = 100; // Arbitrary.
552 CK_OBJECT_HANDLE handles[kMaxHandles];
553 CK_ULONG count = 0;
554 if ((C_FindObjectsInit(session_handle,
555 attributes,
556 arraysize(attributes)) != CKR_OK) ||
557 (C_FindObjects(session_handle, handles, kMaxHandles, &count) != CKR_OK)) {
558 LOG(ERROR) << "Key search failed.";
559 return false;
560 }
561 while (count > 0) {
562 for (CK_ULONG i = 0; i < count; ++i) {
563 std::string key_name;
564 if (!GetKeyName(session_handle, handles[i], &key_name)) {
565 LOG(WARNING) << "Found key object but failed to get name.";
566 continue;
567 }
568 if (!callback.Run(key_name, handles[i]))
569 return false;
570 }
571 if (C_FindObjects(session_handle, handles, kMaxHandles, &count) != CKR_OK) {
572 LOG(ERROR) << "Key search continuation failed.";
573 return false;
574 }
575 }
576 if (C_FindObjectsFinal(session_handle) != CKR_OK) {
577 LOG(WARNING) << "Failed to finalize key search.";
578 }
579 return true;
580 }
581
GetKeyName(CK_SESSION_HANDLE session_handle,CK_OBJECT_HANDLE object_handle,std::string * key_name)582 bool Pkcs11KeyStore::GetKeyName(CK_SESSION_HANDLE session_handle,
583 CK_OBJECT_HANDLE object_handle,
584 std::string* key_name) {
585 CK_ATTRIBUTE attribute = {CKA_LABEL, nullptr, 0};
586 if (C_GetAttributeValue(session_handle, object_handle, &attribute, 1) !=
587 CKR_OK) {
588 LOG(ERROR) << "C_GetAttributeValue(CKA_LABEL) [length] failed.";
589 return false;
590 }
591 key_name->resize(attribute.ulValueLen);
592 attribute.pValue = string_as_array(key_name);
593 if (C_GetAttributeValue(session_handle, object_handle, &attribute, 1) !=
594 CKR_OK) {
595 LOG(ERROR) << "C_GetAttributeValue(CKA_LABEL) failed.";
596 return false;
597 }
598 return true;
599 }
600
DeleteIfMatchesPrefix(CK_SESSION_HANDLE session_handle,const std::string & key_prefix,const std::string & key_name,CK_OBJECT_HANDLE object_handle)601 bool Pkcs11KeyStore::DeleteIfMatchesPrefix(CK_SESSION_HANDLE session_handle,
602 const std::string& key_prefix,
603 const std::string& key_name,
604 CK_OBJECT_HANDLE object_handle) {
605 if (base::StartsWith(key_name, key_prefix, base::CompareCase::SENSITIVE)) {
606 if (C_DestroyObject(session_handle, object_handle) != CKR_OK) {
607 LOG(ERROR) << "C_DestroyObject failed.";
608 return false;
609 }
610 }
611 return true;
612 }
613
GetCertificateFields(const std::string & certificate,std::string * subject,std::string * issuer,std::string * serial_number)614 bool Pkcs11KeyStore::GetCertificateFields(const std::string& certificate,
615 std::string* subject,
616 std::string* issuer,
617 std::string* serial_number) {
618 const unsigned char* asn1_ptr = reinterpret_cast<const unsigned char*>(
619 certificate.data());
620 ScopedX509 x509(d2i_X509(nullptr, &asn1_ptr, certificate.size()));
621 if (!x509.get() || !x509->cert_info || !x509->cert_info->subject) {
622 LOG(WARNING) << "Pkcs11KeyStore: Failed to decode certificate.";
623 return false;
624 }
625 unsigned char* subject_buffer = nullptr;
626 int length = i2d_X509_NAME(x509->cert_info->subject, &subject_buffer);
627 crypto::ScopedOpenSSLBytes scoped_subject_buffer(subject_buffer);
628 if (length <= 0) {
629 LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate subject.";
630 return false;
631 }
632 subject->assign(reinterpret_cast<char*>(subject_buffer), length);
633
634 unsigned char* issuer_buffer = nullptr;
635 length = i2d_X509_NAME(x509->cert_info->issuer, &issuer_buffer);
636 crypto::ScopedOpenSSLBytes scoped_issuer_buffer(issuer_buffer);
637 if (length <= 0) {
638 LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate issuer.";
639 return false;
640 }
641 issuer->assign(reinterpret_cast<char*>(issuer_buffer), length);
642
643 unsigned char* serial_number_buffer = nullptr;
644 length = i2d_ASN1_INTEGER(x509->cert_info->serialNumber,
645 &serial_number_buffer);
646 crypto::ScopedOpenSSLBytes scoped_serial_number_buffer(serial_number_buffer);
647 if (length <= 0) {
648 LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate serial "
649 "number.";
650 return false;
651 }
652 serial_number->assign(reinterpret_cast<char*>(serial_number_buffer), length);
653 return true;
654 }
655
DoesCertificateExist(CK_SESSION_HANDLE session_handle,const std::string & certificate)656 bool Pkcs11KeyStore::DoesCertificateExist(
657 CK_SESSION_HANDLE session_handle,
658 const std::string& certificate) {
659 CK_OBJECT_CLASS object_class = CKO_CERTIFICATE;
660 CK_BBOOL true_value = CK_TRUE;
661 CK_BBOOL false_value = CK_FALSE;
662 std::string mutable_certificate = certificate;
663 CK_ATTRIBUTE attributes[] = {
664 {CKA_CLASS, &object_class, sizeof(object_class)},
665 {CKA_TOKEN, &true_value, sizeof(true_value)},
666 {CKA_PRIVATE, &false_value, sizeof(false_value)},
667 {
668 CKA_VALUE,
669 string_as_array(&mutable_certificate),
670 mutable_certificate.size()
671 }
672 };
673 CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
674 CK_ULONG count = 0;
675 if ((C_FindObjectsInit(session_handle,
676 attributes,
677 arraysize(attributes)) != CKR_OK) ||
678 (C_FindObjects(session_handle, &object_handle, 1, &count) != CKR_OK) ||
679 (C_FindObjectsFinal(session_handle) != CKR_OK)) {
680 return false;
681 }
682 return (count > 0);
683 }
684
685 } // namespace attestation
686