/* * Copyright 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /****************************************************************************** ** ** The original Work has been changed by NXP. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. ** ** Copyright 2022 NXP ** *********************************************************************************/ #define LOG_TAG "javacard.strongbox.keymint.operation-impl" #include "JavacardKeyMintOperation.h" #include <JavacardKeyMintUtils.h> #include <aidl/android/hardware/security/keymint/ErrorCode.h> #include <aidl/android/hardware/security/secureclock/ISecureClock.h> #include <android-base/logging.h> namespace aidl::android::hardware::security::keymint { using namespace ::keymint::javacard; using secureclock::TimeStampToken; JavacardKeyMintOperation::~JavacardKeyMintOperation() { if (opHandle_ != 0) { abort(); } } ScopedAStatus JavacardKeyMintOperation::updateAad(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken, const optional<TimeStampToken>& timestampToken) { cppbor::Array request; request.add(Uint(opHandle_)); request.add(Bstr(input)); cbor_.addHardwareAuthToken(request, authToken.value_or(HardwareAuthToken())); cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken())); auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request); if (err != KM_ERROR_OK) { return km_utils::kmError2ScopedAStatus(err); } return ScopedAStatus::ok(); } ScopedAStatus JavacardKeyMintOperation::update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken, const optional<TimeStampToken>& timestampToken, vector<uint8_t>* output) { HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()}; keymaster_error_t err = bufferData(view); if (err != KM_ERROR_OK) { return km_utils::kmError2ScopedAStatus(err); } if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { if (view.length > MAX_CHUNK_SIZE) { err = updateInChunks(view, aToken, tToken, output); if (err != KM_ERROR_OK) { return km_utils::kmError2ScopedAStatus(err); } } vector<uint8_t> remaining = popNextChunk(view, view.length); err = sendUpdate(remaining, aToken, tToken, *output); } return km_utils::kmError2ScopedAStatus(err); } ScopedAStatus JavacardKeyMintOperation::finish( const optional<vector<uint8_t>>& input, const optional<vector<uint8_t>>& signature, const optional<HardwareAuthToken>& authToken, const optional<TimeStampToken>& timestampToken, const optional<vector<uint8_t>>& confirmationToken, vector<uint8_t>* output) { HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); const vector<uint8_t> confToken = confirmationToken.value_or(vector<uint8_t>()); const vector<uint8_t> inData = input.value_or(vector<uint8_t>()); DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; const vector<uint8_t> sign = signature.value_or(vector<uint8_t>()); if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { appendBufferedData(view); if (view.length > MAX_CHUNK_SIZE) { auto err = updateInChunks(view, aToken, tToken, output); if (err != KM_ERROR_OK) { return km_utils::kmError2ScopedAStatus(err); } } } else { keymaster_error_t err = bufferData(view); if (err != KM_ERROR_OK) { return km_utils::kmError2ScopedAStatus(err); } appendBufferedData(view); } vector<uint8_t> remaining = popNextChunk(view, view.length); return km_utils::kmError2ScopedAStatus(sendFinish(remaining, sign, aToken, tToken, confToken, *output)); } ScopedAStatus JavacardKeyMintOperation::abort() { Array request; request.add(Uint(opHandle_)); auto [item, err] = card_->sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request); opHandle_ = 0; buffer_.clear(); return km_utils::kmError2ScopedAStatus(err); } void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) { appendBufferedData(view); uint16_t offset = getDataViewOffset(view, blockSize); if (view.buffer.empty() && view.data.empty()) { offset = 0; } else if (view.buffer.empty()) { buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); } else if (view.data.empty()) { buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); } else { if (offset < view.buffer.size()) { buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); } else { offset = offset - view.buffer.size(); buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); } } // adjust the view length by removing the buffered data size from it. view.length = view.length - buffer_.size(); } uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) { uint16_t offset = 0; uint16_t remaining = 0; switch(bufferingMode_) { case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: offset = ((view.length / blockSize)) * blockSize; remaining = (view.length % blockSize); if (offset >= blockSize && remaining == 0) { offset -= blockSize; } break; case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: offset = ((view.length / blockSize)) * blockSize; break; case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: if (view.length > macLength_) { offset = (view.length - macLength_); } break; default: break; } return offset; } keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer switch (bufferingMode_) { case BufferingMode::RSA_NO_DIGEST: buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); if (buffer_.size() > RSA_BUFFER_SIZE) { abort(); return KM_ERROR_INVALID_INPUT_LENGTH; } view.start = 0; view.length = 0; break; case BufferingMode::EC_NO_DIGEST: if (buffer_.size() < EC_BUFFER_SIZE) { buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); // Truncate the buffered data if greater than allowed EC buffer size. if (buffer_.size() > EC_BUFFER_SIZE) { buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end()); } } view.start = 0; view.length = 0; break; case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: blockAlign(view, AES_BLOCK_SIZE); break; case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: blockAlign(view, macLength_); break; case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: blockAlign(view, DES_BLOCK_SIZE); break; case BufferingMode::NONE: break; } return KM_ERROR_OK; } // Incrementally send the request using multiple updates. keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view, HardwareAuthToken& authToken, TimeStampToken& timestampToken, vector<uint8_t>* output) { keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR; while (view.length > MAX_CHUNK_SIZE) { vector<uint8_t> chunk = popNextChunk(view, MAX_CHUNK_SIZE); sendError = sendUpdate(chunk, authToken, timestampToken, *output); if (sendError != KM_ERROR_OK) { return sendError; } // Clear tokens if (!authToken.mac.empty()) authToken = HardwareAuthToken(); if (!timestampToken.mac.empty()) timestampToken = TimeStampToken(); } return KM_ERROR_OK; } vector<uint8_t> JavacardKeyMintOperation::popNextChunk(DataView& view, uint32_t chunkSize) { uint32_t start = view.start; uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize); vector<uint8_t> chunk; if (start < view.buffer.size()) { if (end < view.buffer.size()) { chunk = {view.buffer.begin() + start, view.buffer.begin() + end}; } else { end = end - view.buffer.size(); chunk = {view.buffer.begin() + start, view.buffer.end()}; chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end); } } else { start = start - view.buffer.size(); end = end - view.buffer.size(); chunk = {view.data.begin() + start, view.data.begin() + end}; } view.start = view.start + chunk.size(); view.length = view.length - chunk.size(); return chunk; } keymaster_error_t JavacardKeyMintOperation::sendUpdate(const vector<uint8_t>& input, const HardwareAuthToken& authToken, const TimeStampToken& timestampToken, vector<uint8_t>& output) { if (input.empty()) { return KM_ERROR_OK; } cppbor::Array request; request.add(Uint(opHandle_)); request.add(Bstr(input)); cbor_.addHardwareAuthToken(request, authToken); cbor_.addTimeStampToken(request, timestampToken); auto [item, error] = card_->sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request); if (error != KM_ERROR_OK) { return error; } vector<uint8_t> respData; if (!cbor_.getBinaryArray(item, 1, respData)) { return KM_ERROR_UNKNOWN_ERROR; } output.insert(output.end(), respData.begin(), respData.end()); return KM_ERROR_OK; } keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector<uint8_t>& data, const vector<uint8_t>& sign, const HardwareAuthToken& authToken, const TimeStampToken& timestampToken, const vector<uint8_t>& confToken, vector<uint8_t>& output) { cppbor::Array request; request.add(Uint(opHandle_)); request.add(Bstr(data)); request.add(Bstr(sign)); cbor_.addHardwareAuthToken(request, authToken); cbor_.addTimeStampToken(request, timestampToken); request.add(Bstr(confToken)); auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request); if (err != KM_ERROR_OK) { return err; } vector<uint8_t> respData; if (!cbor_.getBinaryArray(item, 1, respData)) { return KM_ERROR_UNKNOWN_ERROR; } opHandle_ = 0; output.insert(output.end(), respData.begin(), respData.end()); #ifdef NXP_EXTNS LOG(INFO) << "(finish) completed Successfully"; #endif return KM_ERROR_OK; } } // namespace aidl::android::hardware::security::keymint