Home | History | Annotate | Download | only in keystore
      1 // Copyright 2015 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #define LOG_TAG "keystore_client"
     16 
     17 #include "keystore/keystore_client_impl.h"
     18 
     19 #include <string>
     20 #include <vector>
     21 
     22 #include <binder/IBinder.h>
     23 #include <binder/IInterface.h>
     24 #include <binder/IServiceManager.h>
     25 #include <keystore/IKeystoreService.h>
     26 #include <keystore/keystore.h>
     27 #include <log/log.h>
     28 #include <utils/String16.h>
     29 #include <utils/String8.h>
     30 
     31 #include "keystore_client.pb.h"
     32 #include <keystore/authorization_set.h>
     33 #include <keystore/keystore_hidl_support.h>
     34 
     35 using android::ExportResult;
     36 using keystore::KeyCharacteristics;
     37 using android::OperationResult;
     38 using android::String16;
     39 using keystore::AuthorizationSet;
     40 using keystore::AuthorizationSetBuilder;
     41 
     42 namespace {
     43 
     44 // Use the UID of the current process.
     45 const int kDefaultUID = -1;
     46 const char kEncryptSuffix[] = "_ENC";
     47 const char kAuthenticateSuffix[] = "_AUTH";
     48 constexpr uint32_t kAESKeySize = 256;      // bits
     49 constexpr uint32_t kHMACKeySize = 256;     // bits
     50 constexpr uint32_t kHMACOutputSize = 256;  // bits
     51 
     52 }  // namespace
     53 
     54 namespace keystore {
     55 
     56 KeystoreClientImpl::KeystoreClientImpl() {
     57     service_manager_ = android::defaultServiceManager();
     58     keystore_binder_ = service_manager_->getService(String16("android.security.keystore"));
     59     keystore_ = android::interface_cast<android::IKeystoreService>(keystore_binder_);
     60 }
     61 
     62 bool KeystoreClientImpl::encryptWithAuthentication(const std::string& key_name,
     63                                                    const std::string& data,
     64                                                    std::string* encrypted_data) {
     65     // The encryption algorithm is AES-256-CBC with PKCS #7 padding and a random
     66     // IV. The authentication algorithm is HMAC-SHA256 and is computed over the
     67     // cipher-text (i.e. Encrypt-then-MAC approach). This was chosen over AES-GCM
     68     // because hardware support for GCM is not mandatory for all Brillo devices.
     69     std::string encryption_key_name = key_name + kEncryptSuffix;
     70     if (!createOrVerifyEncryptionKey(encryption_key_name)) {
     71         return false;
     72     }
     73     std::string authentication_key_name = key_name + kAuthenticateSuffix;
     74     if (!createOrVerifyAuthenticationKey(authentication_key_name)) {
     75         return false;
     76     }
     77     AuthorizationSetBuilder encrypt_params;
     78     encrypt_params.Padding(PaddingMode::PKCS7);
     79     encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
     80     AuthorizationSet output_params;
     81     std::string raw_encrypted_data;
     82     if (!oneShotOperation(KeyPurpose::ENCRYPT, encryption_key_name, encrypt_params, data,
     83                           std::string(), /* signature_to_verify */
     84                           &output_params, &raw_encrypted_data)) {
     85         ALOGE("Encrypt: AES operation failed.");
     86         return false;
     87     }
     88     auto init_vector_blob = output_params.GetTagValue(TAG_NONCE);
     89     if (!init_vector_blob.isOk()){
     90         ALOGE("Encrypt: Missing initialization vector.");
     91         return false;
     92     }
     93     std::string init_vector = hidlVec2String(init_vector_blob.value());
     94 
     95     AuthorizationSetBuilder authenticate_params;
     96     authenticate_params.Digest(Digest::SHA_2_256);
     97     authenticate_params.Authorization(TAG_MAC_LENGTH, kHMACOutputSize);
     98     std::string raw_authentication_data;
     99     if (!oneShotOperation(KeyPurpose::SIGN, authentication_key_name, authenticate_params,
    100                           init_vector + raw_encrypted_data, std::string(), /* signature_to_verify */
    101                           &output_params, &raw_authentication_data)) {
    102         ALOGE("Encrypt: HMAC operation failed.");
    103         return false;
    104     }
    105     EncryptedData protobuf;
    106     protobuf.set_init_vector(init_vector);
    107     protobuf.set_authentication_data(raw_authentication_data);
    108     protobuf.set_encrypted_data(raw_encrypted_data);
    109     if (!protobuf.SerializeToString(encrypted_data)) {
    110         ALOGE("Encrypt: Failed to serialize EncryptedData protobuf.");
    111         return false;
    112     }
    113     return true;
    114 }
    115 
    116 bool KeystoreClientImpl::decryptWithAuthentication(const std::string& key_name,
    117                                                    const std::string& encrypted_data,
    118                                                    std::string* data) {
    119     EncryptedData protobuf;
    120     if (!protobuf.ParseFromString(encrypted_data)) {
    121         ALOGE("Decrypt: Failed to parse EncryptedData protobuf.");
    122     }
    123     // Verify authentication before attempting decryption.
    124     std::string authentication_key_name = key_name + kAuthenticateSuffix;
    125     AuthorizationSetBuilder authenticate_params;
    126     authenticate_params.Digest(Digest::SHA_2_256);
    127     AuthorizationSet output_params;
    128     std::string output_data;
    129     if (!oneShotOperation(KeyPurpose::VERIFY, authentication_key_name, authenticate_params,
    130                           protobuf.init_vector() + protobuf.encrypted_data(),
    131                           protobuf.authentication_data(), &output_params, &output_data)) {
    132         ALOGE("Decrypt: HMAC operation failed.");
    133         return false;
    134     }
    135     std::string encryption_key_name = key_name + kEncryptSuffix;
    136     AuthorizationSetBuilder encrypt_params;
    137     encrypt_params.Padding(PaddingMode::PKCS7);
    138     encrypt_params.Authorization(TAG_BLOCK_MODE, BlockMode::CBC);
    139     encrypt_params.Authorization(TAG_NONCE, protobuf.init_vector().data(),
    140                                  protobuf.init_vector().size());
    141     if (!oneShotOperation(KeyPurpose::DECRYPT, encryption_key_name, encrypt_params,
    142                           protobuf.encrypted_data(), std::string(), /* signature_to_verify */
    143                           &output_params, data)) {
    144         ALOGE("Decrypt: AES operation failed.");
    145         return false;
    146     }
    147     return true;
    148 }
    149 
    150 bool KeystoreClientImpl::oneShotOperation(KeyPurpose purpose, const std::string& key_name,
    151                                           const AuthorizationSet& input_parameters,
    152                                           const std::string& input_data,
    153                                           const std::string& signature_to_verify,
    154                                           AuthorizationSet* output_parameters,
    155                                           std::string* output_data) {
    156     uint64_t handle;
    157     auto result =
    158         beginOperation(purpose, key_name, input_parameters, output_parameters, &handle);
    159     if (!result.isOk()) {
    160         ALOGE("BeginOperation failed: %d", int32_t(result));
    161         return false;
    162     }
    163     AuthorizationSet empty_params;
    164     size_t num_input_bytes_consumed;
    165     AuthorizationSet ignored_params;
    166     result = updateOperation(handle, empty_params, input_data, &num_input_bytes_consumed,
    167                              &ignored_params, output_data);
    168     if (!result.isOk()) {
    169         ALOGE("UpdateOperation failed: %d", int32_t(result));
    170         return false;
    171     }
    172     result =
    173         finishOperation(handle, empty_params, signature_to_verify, &ignored_params, output_data);
    174     if (!result.isOk()) {
    175         ALOGE("FinishOperation failed: %d", int32_t(result));
    176         return false;
    177     }
    178     return true;
    179 }
    180 
    181 KeyStoreNativeReturnCode KeystoreClientImpl::addRandomNumberGeneratorEntropy(const std::string& entropy) {
    182     return keystore_->addRngEntropy(blob2hidlVec(entropy));
    183 }
    184 
    185 KeyStoreNativeReturnCode KeystoreClientImpl::generateKey(const std::string& key_name,
    186                                         const AuthorizationSet& key_parameters,
    187                                         AuthorizationSet* hardware_enforced_characteristics,
    188                                         AuthorizationSet* software_enforced_characteristics) {
    189     String16 key_name16(key_name.data(), key_name.size());
    190     KeyCharacteristics characteristics;
    191     auto result =
    192         keystore_->generateKey(key_name16, key_parameters.hidl_data(), hidl_vec<uint8_t>(),
    193                                kDefaultUID, KEYSTORE_FLAG_NONE, &characteristics);
    194 
    195     /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
    196      * There are no references to Parcel memory after that, and ownership of the newly acquired
    197      * memory is with the AuthorizationSet objects. */
    198     *hardware_enforced_characteristics = characteristics.teeEnforced;
    199     *software_enforced_characteristics = characteristics.softwareEnforced;
    200     return result;
    201 }
    202 
    203 KeyStoreNativeReturnCode
    204 KeystoreClientImpl::getKeyCharacteristics(const std::string& key_name,
    205                                           AuthorizationSet* hardware_enforced_characteristics,
    206                                           AuthorizationSet* software_enforced_characteristics) {
    207     String16 key_name16(key_name.data(), key_name.size());
    208     KeyCharacteristics characteristics;
    209     auto result = keystore_->getKeyCharacteristics(key_name16, hidl_vec<uint8_t>(), hidl_vec<uint8_t>(),
    210                                                       kDefaultUID, &characteristics);
    211 
    212     /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
    213      * There are no references to Parcel memory after that, and ownership of the newly acquired
    214      * memory is with the AuthorizationSet objects. */
    215     *hardware_enforced_characteristics = characteristics.teeEnforced;
    216     *software_enforced_characteristics = characteristics.softwareEnforced;
    217     return result;
    218 }
    219 
    220 KeyStoreNativeReturnCode KeystoreClientImpl::importKey(const std::string& key_name,
    221                                       const AuthorizationSet& key_parameters,
    222                                       KeyFormat key_format,
    223                                       const std::string& key_data,
    224                                       AuthorizationSet* hardware_enforced_characteristics,
    225                                       AuthorizationSet* software_enforced_characteristics) {
    226     String16 key_name16(key_name.data(), key_name.size());
    227     auto hidlKeyData = blob2hidlVec(key_data);
    228     KeyCharacteristics characteristics;
    229     auto result = keystore_->importKey(key_name16, key_parameters.hidl_data(), key_format,
    230             hidlKeyData, kDefaultUID, KEYSTORE_FLAG_NONE, &characteristics);
    231 
    232     /* assignment (hidl_vec<KeyParameter> -> AuthorizationSet) makes a deep copy.
    233      * There are no references to Parcel memory after that, and ownership of the newly acquired
    234      * memory is with the AuthorizationSet objects. */
    235     *hardware_enforced_characteristics = characteristics.teeEnforced;
    236     *software_enforced_characteristics = characteristics.softwareEnforced;
    237     return result;
    238 }
    239 
    240 KeyStoreNativeReturnCode KeystoreClientImpl::exportKey(KeyFormat export_format,
    241                                       const std::string& key_name, std::string* export_data) {
    242     String16 key_name16(key_name.data(), key_name.size());
    243     ExportResult export_result;
    244     keystore_->exportKey(key_name16, export_format, hidl_vec<uint8_t>(), hidl_vec<uint8_t>(),
    245                          kDefaultUID, &export_result);
    246     *export_data = hidlVec2String(export_result.exportData);
    247     return export_result.resultCode;
    248 }
    249 
    250 KeyStoreNativeReturnCode KeystoreClientImpl::deleteKey(const std::string& key_name) {
    251     String16 key_name16(key_name.data(), key_name.size());
    252     return keystore_->del(key_name16, kDefaultUID);
    253 }
    254 
    255 KeyStoreNativeReturnCode KeystoreClientImpl::deleteAllKeys() {
    256     return keystore_->clear_uid(kDefaultUID);
    257 }
    258 
    259 KeyStoreNativeReturnCode KeystoreClientImpl::beginOperation(KeyPurpose purpose, const std::string& key_name,
    260                                            const AuthorizationSet& input_parameters,
    261                                            AuthorizationSet* output_parameters,
    262                                            uint64_t* handle) {
    263     android::sp<android::IBinder> token(new android::BBinder);
    264     String16 key_name16(key_name.data(), key_name.size());
    265     OperationResult result;
    266     keystore_->begin(token, key_name16, purpose, true /*pruneable*/, input_parameters.hidl_data(),
    267                      hidl_vec<uint8_t>(), kDefaultUID, &result);
    268     if (result.resultCode.isOk()) {
    269         *handle = getNextVirtualHandle();
    270         active_operations_[*handle] = result.token;
    271         if (result.outParams.size()) {
    272             *output_parameters = result.outParams;
    273         }
    274     }
    275     return result.resultCode;
    276 }
    277 
    278 KeyStoreNativeReturnCode KeystoreClientImpl::updateOperation(uint64_t handle,
    279                                             const AuthorizationSet& input_parameters,
    280                                             const std::string& input_data,
    281                                             size_t* num_input_bytes_consumed,
    282                                             AuthorizationSet* output_parameters,
    283                                             std::string* output_data) {
    284     if (active_operations_.count(handle) == 0) {
    285         return ErrorCode::INVALID_OPERATION_HANDLE;
    286     }
    287     OperationResult result;
    288     auto hidlInputData = blob2hidlVec(input_data);
    289     keystore_->update(active_operations_[handle], input_parameters.hidl_data(), hidlInputData,
    290             &result);
    291 
    292     if (result.resultCode.isOk()) {
    293         *num_input_bytes_consumed = result.inputConsumed;
    294         if (result.outParams.size()) {
    295             *output_parameters = result.outParams;
    296         }
    297         // TODO verify that append should not be assign
    298         output_data->append(hidlVec2String(result.data));
    299     }
    300     return result.resultCode;
    301 }
    302 
    303 KeyStoreNativeReturnCode KeystoreClientImpl::finishOperation(uint64_t handle,
    304                                             const AuthorizationSet& input_parameters,
    305                                             const std::string& signature_to_verify,
    306                                             AuthorizationSet* output_parameters,
    307                                             std::string* output_data) {
    308     if (active_operations_.count(handle) == 0) {
    309         return ErrorCode::INVALID_OPERATION_HANDLE;
    310     }
    311     OperationResult result;
    312     auto hidlSignature = blob2hidlVec(signature_to_verify);
    313     keystore_->finish(active_operations_[handle], input_parameters.hidl_data(),
    314                       hidlSignature,
    315                       hidl_vec<uint8_t>(), &result);
    316 
    317     if (result.resultCode.isOk()) {
    318         if (result.outParams.size()) {
    319             *output_parameters = result.outParams;
    320         }
    321         // TODO verify that append should not be assign
    322         output_data->append(hidlVec2String(result.data));
    323         active_operations_.erase(handle);
    324     }
    325     return result.resultCode;
    326 }
    327 
    328 KeyStoreNativeReturnCode KeystoreClientImpl::abortOperation(uint64_t handle) {
    329     if (active_operations_.count(handle) == 0) {
    330         return ErrorCode::INVALID_OPERATION_HANDLE;
    331     }
    332     auto error_code = keystore_->abort(active_operations_[handle]);
    333     if (error_code.isOk()) {
    334         active_operations_.erase(handle);
    335     }
    336     return error_code;
    337 }
    338 
    339 bool KeystoreClientImpl::doesKeyExist(const std::string& key_name) {
    340     String16 key_name16(key_name.data(), key_name.size());
    341     auto error_code = keystore_->exist(key_name16, kDefaultUID);
    342     return error_code.isOk();
    343 }
    344 
    345 bool KeystoreClientImpl::listKeys(const std::string& prefix,
    346                                   std::vector<std::string>* key_name_list) {
    347     String16 prefix16(prefix.data(), prefix.size());
    348     android::Vector<String16> matches;
    349     auto error_code = keystore_->list(prefix16, kDefaultUID, &matches);
    350     if (error_code.isOk()) {
    351         for (const auto& match : matches) {
    352             android::String8 key_name(match);
    353             key_name_list->push_back(prefix + std::string(key_name.string(), key_name.size()));
    354         }
    355         return true;
    356     }
    357     return false;
    358 }
    359 
    360 uint64_t KeystoreClientImpl::getNextVirtualHandle() {
    361     return next_virtual_handle_++;
    362 }
    363 
    364 bool KeystoreClientImpl::createOrVerifyEncryptionKey(const std::string& key_name) {
    365     bool key_exists = doesKeyExist(key_name);
    366     if (key_exists) {
    367         bool verified = false;
    368         if (!verifyEncryptionKeyAttributes(key_name, &verified)) {
    369             return false;
    370         }
    371         if (!verified) {
    372             auto result = deleteKey(key_name);
    373             if (!result.isOk()) {
    374                 ALOGE("Failed to delete invalid encryption key: %d", int32_t(result));
    375                 return false;
    376             }
    377             key_exists = false;
    378         }
    379     }
    380     if (!key_exists) {
    381         AuthorizationSetBuilder key_parameters;
    382         key_parameters.AesEncryptionKey(kAESKeySize)
    383             .Padding(PaddingMode::PKCS7)
    384             .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
    385             .Authorization(TAG_NO_AUTH_REQUIRED);
    386         AuthorizationSet hardware_enforced_characteristics;
    387         AuthorizationSet software_enforced_characteristics;
    388         auto result =
    389             generateKey(key_name, key_parameters, &hardware_enforced_characteristics,
    390                         &software_enforced_characteristics);
    391         if (!result.isOk()) {
    392             ALOGE("Failed to generate encryption key: %d", int32_t(result));
    393             return false;
    394         }
    395         if (hardware_enforced_characteristics.size() == 0) {
    396             ALOGW("WARNING: Encryption key is not hardware-backed.");
    397         }
    398     }
    399     return true;
    400 }
    401 
    402 bool KeystoreClientImpl::createOrVerifyAuthenticationKey(const std::string& key_name) {
    403     bool key_exists = doesKeyExist(key_name);
    404     if (key_exists) {
    405         bool verified = false;
    406         if (!verifyAuthenticationKeyAttributes(key_name, &verified)) {
    407             return false;
    408         }
    409         if (!verified) {
    410             auto result = deleteKey(key_name);
    411             if (!result.isOk()) {
    412                 ALOGE("Failed to delete invalid authentication key: %d", int32_t(result));
    413                 return false;
    414             }
    415             key_exists = false;
    416         }
    417     }
    418     if (!key_exists) {
    419         AuthorizationSetBuilder key_parameters;
    420         key_parameters.HmacKey(kHMACKeySize)
    421             .Digest(Digest::SHA_2_256)
    422             .Authorization(TAG_MIN_MAC_LENGTH, kHMACOutputSize)
    423             .Authorization(TAG_NO_AUTH_REQUIRED);
    424         AuthorizationSet hardware_enforced_characteristics;
    425         AuthorizationSet software_enforced_characteristics;
    426         auto result =
    427             generateKey(key_name, key_parameters, &hardware_enforced_characteristics,
    428                         &software_enforced_characteristics);
    429         if (!result.isOk()) {
    430             ALOGE("Failed to generate authentication key: %d", int32_t(result));
    431             return false;
    432         }
    433         if (hardware_enforced_characteristics.size() == 0) {
    434             ALOGW("WARNING: Authentication key is not hardware-backed.");
    435         }
    436     }
    437     return true;
    438 }
    439 
    440 bool KeystoreClientImpl::verifyEncryptionKeyAttributes(const std::string& key_name,
    441                                                        bool* verified) {
    442     AuthorizationSet hardware_enforced_characteristics;
    443     AuthorizationSet software_enforced_characteristics;
    444     auto result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
    445                                            &software_enforced_characteristics);
    446     if (!result.isOk()) {
    447         ALOGE("Failed to query encryption key: %d", int32_t(result));
    448         return false;
    449     }
    450     *verified = true;
    451     auto algorithm = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_ALGORITHM),
    452             software_enforced_characteristics.GetTagValue(TAG_ALGORITHM));
    453     if (!algorithm.isOk() || algorithm.value() != Algorithm::AES) {
    454         ALOGW("Found encryption key with invalid algorithm.");
    455         *verified = false;
    456     }
    457     auto key_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_KEY_SIZE),
    458             software_enforced_characteristics.GetTagValue(TAG_KEY_SIZE));
    459     if (!key_size.isOk() || key_size.value() != kAESKeySize) {
    460         ALOGW("Found encryption key with invalid size.");
    461         *verified = false;
    462     }
    463     auto block_mode = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_BLOCK_MODE),
    464             software_enforced_characteristics.GetTagValue(TAG_BLOCK_MODE));
    465     if (!block_mode.isOk() || block_mode.value() != BlockMode::CBC) {
    466         ALOGW("Found encryption key with invalid block mode.");
    467         *verified = false;
    468     }
    469     auto padding_mode = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_PADDING),
    470             software_enforced_characteristics.GetTagValue(TAG_PADDING));
    471     if (!padding_mode.isOk() || padding_mode.value() != PaddingMode::PKCS7) {
    472         ALOGW("Found encryption key with invalid padding mode.");
    473         *verified = false;
    474     }
    475     if (hardware_enforced_characteristics.size() == 0) {
    476         ALOGW("WARNING: Encryption key is not hardware-backed.");
    477     }
    478     return true;
    479 }
    480 
    481 bool KeystoreClientImpl::verifyAuthenticationKeyAttributes(const std::string& key_name,
    482                                                            bool* verified) {
    483     AuthorizationSet hardware_enforced_characteristics;
    484     AuthorizationSet software_enforced_characteristics;
    485     auto result = getKeyCharacteristics(key_name, &hardware_enforced_characteristics,
    486                                            &software_enforced_characteristics);
    487     if (!result.isOk()) {
    488         ALOGE("Failed to query authentication key: %d", int32_t(result));
    489         return false;
    490     }
    491     *verified = true;
    492     auto algorithm = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_ALGORITHM),
    493             software_enforced_characteristics.GetTagValue(TAG_ALGORITHM));
    494     if (!algorithm.isOk() || algorithm.value() != Algorithm::HMAC){
    495         ALOGW("Found authentication key with invalid algorithm.");
    496         *verified = false;
    497     }
    498     auto key_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_KEY_SIZE),
    499             software_enforced_characteristics.GetTagValue(TAG_KEY_SIZE));
    500     if (!key_size.isOk() || key_size.value() != kHMACKeySize) {
    501         ALOGW("Found authentication key with invalid size.");
    502         *verified = false;
    503     }
    504     auto mac_size = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_MIN_MAC_LENGTH),
    505             software_enforced_characteristics.GetTagValue(TAG_MIN_MAC_LENGTH));
    506     if (!mac_size.isOk() || mac_size.value() != kHMACOutputSize) {
    507         ALOGW("Found authentication key with invalid minimum mac size.");
    508         *verified = false;
    509     }
    510     auto digest = NullOrOr(hardware_enforced_characteristics.GetTagValue(TAG_DIGEST),
    511             software_enforced_characteristics.GetTagValue(TAG_DIGEST));
    512     if (!digest.isOk() || digest.value() != Digest::SHA_2_256) {
    513         ALOGW("Found authentication key with invalid digest list.");
    514         *verified = false;
    515     }
    516     if (hardware_enforced_characteristics.size() == 0) {
    517         ALOGW("WARNING: Authentication key is not hardware-backed.");
    518     }
    519     return true;
    520 }
    521 
    522 }  // namespace keystore
    523