| /* |
| * Copyright 2014 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. |
| */ |
| |
| #include <stdio.h> |
| |
| #include <openssl/aes.h> |
| #include <openssl/rand.h> |
| |
| #include <keymaster/authorization_set.h> |
| #include <keymaster/logger.h> |
| |
| #include "aead_mode_operation.h" |
| #include "openssl_err.h" |
| |
| namespace keymaster { |
| |
| keymaster_error_t AeadModeOperation::Begin(const AuthorizationSet& input_params, |
| AuthorizationSet* output_params) { |
| keymaster_error_t error = Initialize(key_, key_size_, nonce_length_, tag_length_); |
| if (error != KM_ERROR_OK) |
| return error; |
| |
| buffer_end_ = 0; |
| buffer_.reset(new uint8_t[processing_unit_]); |
| if (!buffer_.get()) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| return HandleNonce(input_params, output_params); |
| } |
| |
| inline size_t min(size_t a, size_t b) { |
| if (a < b) |
| return a; |
| return b; |
| } |
| |
| keymaster_error_t AeadModeOperation::Update(const AuthorizationSet& additional_params, |
| const Buffer& input, Buffer* output, |
| size_t* input_consumed) { |
| // Make an effort to reserve enough output space. The output buffer will be extended if needed, |
| // but this reduces reallocations. |
| if (!output->reserve(EstimateOutputSize(input, output))) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| |
| keymaster_error_t error = KM_ERROR_OK; |
| *input_consumed = 0; |
| |
| keymaster_blob_t associated_data = {0, 0}; |
| additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &associated_data); |
| |
| const uint8_t* plaintext = input.peek_read(); |
| const uint8_t* plaintext_end = plaintext + input.available_read(); |
| while (plaintext < plaintext_end && error == KM_ERROR_OK) { |
| if (buffered_data_length() == processing_unit_) { |
| error = ProcessChunk(associated_data, output); |
| ClearBuffer(); |
| IncrementNonce(); |
| } |
| plaintext = AppendToBuffer(plaintext, plaintext_end - plaintext); |
| *input_consumed = plaintext - input.peek_read(); |
| } |
| return error; |
| } |
| |
| keymaster_error_t AeadModeOperation::Finish(const AuthorizationSet& additional_params, |
| const Buffer& /* signature */, Buffer* output) { |
| keymaster_blob_t associated_data = {0, 0}; |
| additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &associated_data); |
| return ProcessChunk(associated_data, output); |
| } |
| |
| keymaster_error_t AeadModeOperation::ProcessChunk(const keymaster_blob_t& additional_data, |
| Buffer* output) { |
| keymaster_error_t error = KM_ERROR_OK; |
| if (purpose() == KM_PURPOSE_DECRYPT) { |
| if (buffered_data_length() < tag_length_) |
| return KM_ERROR_INVALID_INPUT_LENGTH; |
| ExtractTagFromBuffer(); |
| LOG_D("AeadMode decrypting %d", buffered_data_length()); |
| if (!output->reserve(output->available_read() + buffered_data_length())) |
| error = KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| else |
| error = DecryptChunk(nonce_, nonce_length_, tag_, tag_length_, additional_data, |
| buffer_.get(), buffered_data_length(), output); |
| } else { |
| if (!output->reserve(output->available_read() + buffered_data_length() + tag_length_)) |
| error = KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| else |
| error = EncryptChunk(nonce_, nonce_length_, tag_length_, additional_data, buffer_.get(), |
| buffered_data_length(), output); |
| } |
| return error; |
| } |
| |
| size_t AeadModeOperation::EstimateOutputSize(const Buffer& input, Buffer* output) { |
| switch (purpose()) { |
| case KM_PURPOSE_ENCRYPT: { |
| size_t chunk_length = processing_unit_; |
| size_t chunk_count = (input.available_read() + chunk_length - 1) / chunk_length; |
| return output->available_read() + nonce_length_ + |
| chunk_count * (chunk_length + tag_length_); |
| } |
| case KM_PURPOSE_DECRYPT: { |
| size_t chunk_length = processing_unit_ - tag_length_; |
| size_t chunk_count = |
| (input.available_read() - nonce_length_ + processing_unit_ - 1) / processing_unit_; |
| return output->available_read() + chunk_length * chunk_count; |
| } |
| default: |
| LOG_E("Encountered invalid purpose %d", purpose()); |
| return 0; |
| } |
| } |
| |
| keymaster_error_t AeadModeOperation::HandleNonce(const AuthorizationSet& input_params, |
| AuthorizationSet* output_params) { |
| if (!output_params) |
| return KM_ERROR_OUTPUT_PARAMETER_NULL; |
| |
| switch (purpose()) { |
| case KM_PURPOSE_ENCRYPT: { |
| keymaster_error_t error; |
| if (caller_nonce_) |
| error = ExtractNonce(input_params); |
| else { |
| if (input_params.find(TAG_NONCE) != -1) |
| return KM_ERROR_CALLER_NONCE_PROHIBITED; |
| error = GenerateNonce(); |
| } |
| |
| if (error != KM_ERROR_OK) |
| return error; |
| |
| output_params->push_back(TAG_NONCE, nonce_, nonce_length_); |
| if (output_params->is_valid() != AuthorizationSet::OK) |
| return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| break; |
| } |
| case KM_PURPOSE_DECRYPT: |
| return ExtractNonce(input_params); |
| |
| default: |
| return KM_ERROR_UNSUPPORTED_PURPOSE; |
| } |
| return KM_ERROR_OK; |
| } |
| |
| keymaster_error_t AeadModeOperation::GenerateNonce() { |
| if (RAND_bytes(nonce_, nonce_length_) == 1) |
| return KM_ERROR_OK; |
| LOG_S("Failed to generate %d-byte nonce", nonce_length_); |
| return TranslateLastOpenSslError(); |
| } |
| |
| keymaster_error_t AeadModeOperation::ExtractNonce(const AuthorizationSet& input_params) { |
| keymaster_blob_t nonce_blob; |
| if (!input_params.GetTagValue(TAG_NONCE, &nonce_blob)) |
| return KM_ERROR_MISSING_NONCE; |
| |
| if (nonce_blob.data_length != nonce_length_) { |
| LOG_E("Expected %d-byte nonce, got %d bytes", nonce_length_, nonce_blob.data_length); |
| return KM_ERROR_INVALID_NONCE; |
| } |
| |
| memcpy(nonce_, nonce_blob.data, nonce_length_); |
| return KM_ERROR_OK; |
| } |
| |
| void AeadModeOperation::IncrementNonce() { |
| for (int i = nonce_length_ - 1; i > 0; --i) |
| if (++nonce_[i]) |
| break; |
| } |
| |
| const uint8_t* AeadModeOperation::AppendToBuffer(const uint8_t* data, size_t data_length) { |
| // Only take as much data as we can fit. |
| if (data_length > buffer_free_space()) |
| data_length = buffer_free_space(); |
| memcpy(buffer_.get() + buffer_end_, data, data_length); |
| buffer_end_ += data_length; |
| return data + data_length; |
| } |
| |
| void AeadModeOperation::ExtractTagFromBuffer() { |
| assert(buffered_data_length() >= tag_length_); |
| memcpy(tag_, buffer_.get() + buffer_end_ - tag_length_, tag_length_); |
| buffer_end_ -= tag_length_; |
| } |
| |
| } // namespace keymaster |