• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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