Shawn Willden | 907c301 | 2014-12-08 15:51:55 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2014 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 <openssl/aes.h> |
| 18 | #include <openssl/rand.h> |
| 19 | |
| 20 | #include "aes_operation.h" |
| 21 | |
| 22 | namespace keymaster { |
| 23 | |
| 24 | keymaster_error_t AesOcbEncryptOperation::Begin() { |
| 25 | chunk_.reset(new uint8_t[chunk_length_]); |
| 26 | if (!chunk_.get()) |
| 27 | return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| 28 | |
| 29 | if (!RAND_bytes(nonce_, NONCE_LENGTH)) |
| 30 | return KM_ERROR_UNKNOWN_ERROR; |
| 31 | |
| 32 | if (ae_init(ctx_.get(), key_, key_size_, array_size(nonce_), tag_length_) != AE_SUCCESS) { |
| 33 | memset_s(ctx_.get(), 0, ae_ctx_sizeof()); |
| 34 | return KM_ERROR_UNKNOWN_ERROR; |
| 35 | } |
| 36 | |
| 37 | return KM_ERROR_OK; |
| 38 | } |
| 39 | |
| 40 | keymaster_error_t AesOcbEncryptOperation::Update(const Buffer& input, Buffer* output, |
| 41 | size_t* input_consumed) { |
| 42 | const uint8_t* plaintext = input.peek_read(); |
| 43 | const uint8_t* plaintext_end = plaintext + input.available_read(); |
| 44 | |
| 45 | while (plaintext + chunk_unfilled_space() < plaintext_end) { |
| 46 | size_t to_process = chunk_unfilled_space(); |
| 47 | memcpy(chunk_.get() + chunk_offset_, plaintext, to_process); |
| 48 | chunk_offset_ += to_process; |
| 49 | assert(chunk_offset_ == chunk_length_); |
| 50 | |
| 51 | keymaster_error_t error = ProcessChunk(output); |
| 52 | if (error != KM_ERROR_OK) |
| 53 | return error; |
| 54 | plaintext += to_process; |
| 55 | } |
| 56 | |
| 57 | // Copy remaining data into chunk_. |
| 58 | assert(plaintext_end - plaintext < chunk_unfilled_space()); |
| 59 | memcpy(chunk_.get() + chunk_offset_, plaintext, plaintext_end - plaintext); |
| 60 | chunk_offset_ += (plaintext_end - plaintext); |
| 61 | |
| 62 | *input_consumed = input.available_read(); |
| 63 | return KM_ERROR_OK; |
| 64 | } |
| 65 | |
| 66 | keymaster_error_t AesOcbEncryptOperation::Finish(const Buffer& /* signature */, Buffer* output) { |
| 67 | keymaster_error_t error = KM_ERROR_OK; |
| 68 | if (chunk_offset_ > 0) |
| 69 | error = ProcessChunk(output); |
| 70 | return error; |
| 71 | } |
| 72 | |
| 73 | keymaster_error_t AesOcbEncryptOperation::ProcessChunk(Buffer* output) { |
| 74 | if (!nonce_written_) { |
| 75 | if (!output->reserve(NONCE_LENGTH + chunk_length_ + tag_length_)) |
| 76 | return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| 77 | output->write(nonce_, NONCE_LENGTH); |
| 78 | nonce_written_ = true; |
| 79 | } else { |
| 80 | IncrementNonce(); |
| 81 | } |
| 82 | |
| 83 | if (!output->reserve(output->available_read() + chunk_offset_ + tag_length_)) |
| 84 | return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
| 85 | |
| 86 | int ae_err = ae_encrypt(ctx_.get(), nonce_, chunk_.get(), chunk_offset_, additional_data_.data, |
| 87 | additional_data_.data_length, output->peek_write(), tag_, AE_FINALIZE); |
| 88 | if (ae_err < 0) |
| 89 | return KM_ERROR_UNKNOWN_ERROR; |
| 90 | output->advance_write(chunk_offset_); |
| 91 | chunk_offset_ = 0; |
| 92 | |
| 93 | // Output the tag. |
| 94 | output->write(tag_, tag_length_); |
| 95 | |
| 96 | return KM_ERROR_OK; |
| 97 | } |
| 98 | |
| 99 | void AesOcbEncryptOperation::IncrementNonce() { |
| 100 | for (int i = NONCE_LENGTH - 1; i > 0; --i) |
| 101 | if (++nonce_[i]) |
| 102 | break; |
| 103 | } |
| 104 | |
| 105 | } // namespace keymaster |