Home | History | Annotate | Download | only in server
      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 
     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:
     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 
     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 
     79   CK_SESSION_HANDLE handle() const {
     80     return handle_;
     81   }
     82 
     83   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 
     93 Pkcs11KeyStore::Pkcs11KeyStore(chaps::TokenManagerClient* token_manager)
     94     : token_manager_(token_manager) {}
     95 
     96 Pkcs11KeyStore::~Pkcs11KeyStore() {}
     97 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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