1 /*
2  * Copyright 2020, 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  **
18  ** The original Work has been changed by NXP.
19  **
20  ** Licensed under the Apache License, Version 2.0 (the "License");
21  ** you may not use this file except in compliance with the License.
22  ** You may obtain a copy of the License at
23  **
24  ** http://www.apache.org/licenses/LICENSE-2.0
25  **
26  ** Unless required by applicable law or agreed to in writing, software
27  ** distributed under the License is distributed on an "AS IS" BASIS,
28  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29  ** See the License for the specific language governing permissions and
30  ** limitations under the License.
31  **
32  ** Copyright 2022,2024 NXP
33  **
34  *********************************************************************************/
35 #define LOG_TAG "javacard.strongbox.keymint.operation-impl"
36 
37 #include "JavacardKeyMintOperation.h"
38 
39 #include <KeyMintUtils.h>
40 #include <aidl/android/hardware/security/keymint/ErrorCode.h>
41 #include <aidl/android/hardware/security/secureclock/ISecureClock.h>
42 #include <android-base/logging.h>
43 
44 #include "CborConverter.h"
45 
46 namespace aidl::android::hardware::security::keymint {
47 using cppbor::Bstr;
48 using cppbor::Uint;
49 using secureclock::TimeStampToken;
50 
~JavacardKeyMintOperation()51 JavacardKeyMintOperation::~JavacardKeyMintOperation() {
52 #ifdef NXP_EXTNS
53     card_->setOperationState(::keymint::javacard::CryptoOperationState::FINISHED);
54 #endif
55     if (opHandle_ != 0) {
56         JavacardKeyMintOperation::abort();
57     }
58 }
59 
updateAad(const vector<uint8_t> & input,const optional<HardwareAuthToken> & authToken,const optional<TimeStampToken> & timestampToken)60 ScopedAStatus JavacardKeyMintOperation::updateAad(const vector<uint8_t>& input,
61                                                   const optional<HardwareAuthToken>& authToken,
62                                                   const optional<TimeStampToken>& timestampToken) {
63     cppbor::Array request;
64     request.add(Uint(opHandle_));
65     request.add(Bstr(input));
66     cbor_.addHardwareAuthToken(request, authToken.value_or(HardwareAuthToken()));
67     cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken()));
68     auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request);
69     if (err != KM_ERROR_OK) {
70         return km_utils::kmError2ScopedAStatus(err);
71     }
72     return ScopedAStatus::ok();
73 }
74 
update(const vector<uint8_t> & input,const optional<HardwareAuthToken> & authToken,const optional<TimeStampToken> & timestampToken,vector<uint8_t> * output)75 ScopedAStatus JavacardKeyMintOperation::update(const vector<uint8_t>& input,
76                                                const optional<HardwareAuthToken>& authToken,
77                                                const optional<TimeStampToken>& timestampToken,
78                                                vector<uint8_t>* output) {
79     HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken());
80     TimeStampToken tToken = timestampToken.value_or(TimeStampToken());
81     DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()};
82     keymaster_error_t err = bufferData(view);
83     if (err != KM_ERROR_OK) {
84         return km_utils::kmError2ScopedAStatus(err);
85     }
86     if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST ||
87           bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) {
88         if (view.length > MAX_CHUNK_SIZE) {
89             err = updateInChunks(view, aToken, tToken, output);
90             if (err != KM_ERROR_OK) {
91                 return km_utils::kmError2ScopedAStatus(err);
92             }
93         }
94         vector<uint8_t> remaining = popNextChunk(view, view.length);
95         err = sendUpdate(remaining, aToken, tToken, *output);
96     }
97     return km_utils::kmError2ScopedAStatus(err);
98 }
99 
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)100 ScopedAStatus JavacardKeyMintOperation::finish(const optional<vector<uint8_t>>& input,
101                                                const optional<vector<uint8_t>>& signature,
102                                                const optional<HardwareAuthToken>& authToken,
103                                                const optional<TimeStampToken>& timestampToken,
104                                                const optional<vector<uint8_t>>& confirmationToken,
105                                                vector<uint8_t>* output) {
106     HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken());
107     TimeStampToken tToken = timestampToken.value_or(TimeStampToken());
108     const vector<uint8_t> confToken = confirmationToken.value_or(vector<uint8_t>());
109     const vector<uint8_t> inData = input.value_or(vector<uint8_t>());
110     DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()};
111     const vector<uint8_t> sign = signature.value_or(vector<uint8_t>());
112     if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST ||
113           bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) {
114         appendBufferedData(view);
115         if (view.length > MAX_CHUNK_SIZE) {
116             auto err = updateInChunks(view, aToken, tToken, output);
117             if (err != KM_ERROR_OK) {
118                 return km_utils::kmError2ScopedAStatus(err);
119             }
120         }
121     } else {
122         keymaster_error_t err = bufferData(view);
123         if (err != KM_ERROR_OK) {
124             return km_utils::kmError2ScopedAStatus(err);
125         }
126         appendBufferedData(view);
127     }
128     vector<uint8_t> remaining = popNextChunk(view, view.length);
129     return km_utils::kmError2ScopedAStatus(
130         sendFinish(remaining, sign, aToken, tToken, confToken, *output));
131 }
132 
abort()133 ScopedAStatus JavacardKeyMintOperation::abort() {
134     Array request;
135     request.add(Uint(opHandle_));
136     auto [item, err] = card_->sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request);
137     opHandle_ = 0;
138     buffer_.clear();
139     return km_utils::kmError2ScopedAStatus(err);
140 }
141 
blockAlign(DataView & view,uint16_t blockSize)142 void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) {
143     appendBufferedData(view);
144     uint16_t offset = getDataViewOffset(view, blockSize);
145     if (view.buffer.empty() && view.data.empty()) {
146         offset = 0;
147     } else if (view.buffer.empty()) {
148         buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end());
149     } else if (view.data.empty()) {
150         buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end());
151     } else {
152         if (offset < view.buffer.size()) {
153             buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end());
154             buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
155         } else {
156             offset = offset - view.buffer.size();
157             buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end());
158         }
159     }
160     // adjust the view length by removing the buffered data size from it.
161     view.length = view.length - buffer_.size();
162 }
163 
getDataViewOffset(DataView & view,uint16_t blockSize)164 uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) {
165     uint16_t offset = 0;
166     uint16_t remaining = 0;
167     switch (bufferingMode_) {
168     case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED:
169     case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED:
170         offset = ((view.length / blockSize)) * blockSize;
171         remaining = (view.length % blockSize);
172         if (offset >= blockSize && remaining == 0) {
173             offset -= blockSize;
174         }
175         break;
176     case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
177     case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
178         offset = ((view.length / blockSize)) * blockSize;
179         break;
180     case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED:
181         if (view.length > macLength_) {
182             offset = (view.length - macLength_);
183         }
184         break;
185     default:
186         break;
187     }
188     return offset;
189 }
190 
bufferData(DataView & view)191 keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) {
192     if (view.data.empty()) return KM_ERROR_OK;  // nothing to buffer
193     switch (bufferingMode_) {
194     case BufferingMode::RSA_DECRYPT_OR_NO_DIGEST:
195         buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
196         if (buffer_.size() > RSA_BUFFER_SIZE) {
197             abort();
198             return KM_ERROR_INVALID_INPUT_LENGTH;
199         }
200         view.start = 0;
201         view.length = 0;
202         break;
203     case BufferingMode::EC_NO_DIGEST:
204         if (buffer_.size() < EC_BUFFER_SIZE) {
205             buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
206             // Truncate the buffered data if greater than allowed EC buffer size.
207             if (buffer_.size() > EC_BUFFER_SIZE) {
208                 buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end());
209             }
210         }
211         view.start = 0;
212         view.length = 0;
213         break;
214     case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
215     case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED:
216         blockAlign(view, AES_BLOCK_SIZE);
217         break;
218     case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED:
219         blockAlign(view, macLength_);
220         break;
221     case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
222     case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED:
223         blockAlign(view, DES_BLOCK_SIZE);
224         break;
225     case BufferingMode::NONE:
226         break;
227     }
228     return KM_ERROR_OK;
229 }
230 
231 // Incrementally send the request using multiple updates.
updateInChunks(DataView & view,HardwareAuthToken & authToken,TimeStampToken & timestampToken,vector<uint8_t> * output)232 keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view,
233                                                            HardwareAuthToken& authToken,
234                                                            TimeStampToken& timestampToken,
235                                                            vector<uint8_t>* output) {
236     keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR;
237     while (view.length > MAX_CHUNK_SIZE) {
238         vector<uint8_t> chunk = popNextChunk(view, MAX_CHUNK_SIZE);
239         sendError = sendUpdate(chunk, authToken, timestampToken, *output);
240         if (sendError != KM_ERROR_OK) {
241             return sendError;
242         }
243         // Clear tokens
244         if (!authToken.mac.empty()) authToken = HardwareAuthToken();
245         if (!timestampToken.mac.empty()) timestampToken = TimeStampToken();
246     }
247     return KM_ERROR_OK;
248 }
249 
popNextChunk(DataView & view,uint32_t chunkSize)250 vector<uint8_t> JavacardKeyMintOperation::popNextChunk(DataView& view, uint32_t chunkSize) {
251     uint32_t start = view.start;
252     uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize);
253     vector<uint8_t> chunk;
254     if (start < view.buffer.size()) {
255         if (end < view.buffer.size()) {
256             chunk = {view.buffer.begin() + start, view.buffer.begin() + end};
257         } else {
258             end = end - view.buffer.size();
259             chunk = {view.buffer.begin() + start, view.buffer.end()};
260             chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end);
261         }
262     } else {
263         start = start - view.buffer.size();
264         end = end - view.buffer.size();
265         chunk = {view.data.begin() + start, view.data.begin() + end};
266     }
267     view.start = view.start + chunk.size();
268     view.length = view.length - chunk.size();
269     return chunk;
270 }
271 
sendUpdate(const vector<uint8_t> & input,const HardwareAuthToken & authToken,const TimeStampToken & timestampToken,vector<uint8_t> & output)272 keymaster_error_t JavacardKeyMintOperation::sendUpdate(const vector<uint8_t>& input,
273                                                        const HardwareAuthToken& authToken,
274                                                        const TimeStampToken& timestampToken,
275                                                        vector<uint8_t>& output) {
276     if (input.empty()) {
277         return KM_ERROR_OK;
278     }
279     cppbor::Array request;
280     request.add(Uint(opHandle_));
281     request.add(Bstr(input));
282     cbor_.addHardwareAuthToken(request, authToken);
283     cbor_.addTimeStampToken(request, timestampToken);
284     auto [item, error] = card_->sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request);
285     if (error != KM_ERROR_OK) {
286         return error;
287     }
288     auto optTemp = cbor_.getByteArrayVec(item, 1);
289     if (!optTemp) {
290         return KM_ERROR_UNKNOWN_ERROR;
291     }
292     output.insert(output.end(), optTemp.value().begin(), optTemp.value().end());
293     return KM_ERROR_OK;
294 }
295 
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)296 keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector<uint8_t>& data,
297                                                        const vector<uint8_t>& sign,
298                                                        const HardwareAuthToken& authToken,
299                                                        const TimeStampToken& timestampToken,
300                                                        const vector<uint8_t>& confToken,
301                                                        vector<uint8_t>& output) {
302     cppbor::Array request;
303     request.add(Uint(opHandle_));
304     request.add(Bstr(data));
305     request.add(Bstr(sign));
306     cbor_.addHardwareAuthToken(request, authToken);
307     cbor_.addTimeStampToken(request, timestampToken);
308     request.add(Bstr(confToken));
309 
310     auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request);
311     if (err != KM_ERROR_OK) {
312         return err;
313     }
314     auto optTemp = cbor_.getByteArrayVec(item, 1);
315     if (!optTemp) {
316         return KM_ERROR_UNKNOWN_ERROR;
317     }
318     opHandle_ = 0;
319     output.insert(output.end(), optTemp.value().begin(), optTemp.value().end());
320 #ifdef NXP_EXTNS
321     LOG(INFO) << "(finish) completed Successfully";
322 #endif
323     return KM_ERROR_OK;
324 }
325 
326 }  // namespace aidl::android::hardware::security::keymint
327