blob: 44d98ccea7b95dcb6aafa274e5b07b8de7333e55 [file] [log] [blame]
Shawn Willden6dde87c2014-12-11 14:08:48 -07001/*
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 <stdio.h>
18
19#include <openssl/aes.h>
20#include <openssl/rand.h>
21
22#include "aead_mode_operation.h"
23
24namespace keymaster {
25
26keymaster_error_t AeadModeOperation::Begin() {
27 keymaster_error_t error = Initialize(key_, key_size_, nonce_length_, tag_length_);
28 if (error == KM_ERROR_OK) {
29 buffer_end_ = 0;
30 buffer_.reset(new uint8_t[processing_unit_]);
31 if (!buffer_.get())
32 error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
33 }
34 return error;
35}
36
37inline size_t min(size_t a, size_t b) {
38 if (a < b)
39 return a;
40 return b;
41}
42
43keymaster_error_t AeadModeOperation::Update(const Buffer& input, Buffer* output,
44 size_t* input_consumed) {
45 // Make an effort to reserve enough output space. The output buffer will be extended if needed,
46 // but this reduces reallocations.
47 if (!output->reserve(EstimateOutputSize(input, output)))
48 return KM_ERROR_MEMORY_ALLOCATION_FAILED;
49
50 keymaster_error_t error = KM_ERROR_OK;
51 *input_consumed = 0;
52
53 const uint8_t* plaintext = input.peek_read();
54 const uint8_t* plaintext_end = plaintext + input.available_read();
55 while (plaintext < plaintext_end && error == KM_ERROR_OK) {
56 if (buffered_data_length() == processing_unit_) {
57 assert(nonce_handled_);
58 if (!nonce_handled_)
59 return KM_ERROR_UNKNOWN_ERROR;
60 error = ProcessChunk(output);
61 ClearBuffer();
62 IncrementNonce();
63 }
64 plaintext = AppendToBuffer(plaintext, plaintext_end - plaintext);
65 *input_consumed = plaintext - input.peek_read();
66 if (!nonce_handled_)
67 error = HandleNonce(output);
68 }
69 return error;
70}
71
72keymaster_error_t AeadModeOperation::Finish(const Buffer& /* signature */, Buffer* output) {
73 keymaster_error_t error = KM_ERROR_OK;
74 if (!nonce_handled_)
75 error = HandleNonce(output);
76 if (error != KM_ERROR_OK)
77 return error;
78 return ProcessChunk(output);
79}
80
81keymaster_error_t AeadModeOperation::ProcessChunk(Buffer* output) {
82 if (!nonce_handled_)
83 return KM_ERROR_INVALID_INPUT_LENGTH;
84
85 keymaster_error_t error = KM_ERROR_OK;
86 if (purpose() == KM_PURPOSE_DECRYPT) {
87 if (buffered_data_length() < tag_length_)
88 return KM_ERROR_INVALID_INPUT_LENGTH;
89 ExtractTagFromBuffer();
90 logger().info("AeadMode decrypting %d", buffered_data_length());
91 if (!output->reserve(output->available_read() + buffered_data_length()))
92 error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
93 else
94 error = DecryptChunk(nonce_, nonce_length_, tag_, tag_length_, additional_data_,
95 buffer_.get(), buffered_data_length(), output);
96 } else {
97 if (!output->reserve(output->available_read() + buffered_data_length() + tag_length_))
98 error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
99 else
100 error = EncryptChunk(nonce_, nonce_length_, tag_length_, additional_data_,
101 buffer_.get(), buffered_data_length(), output);
102 }
103 return error;
104}
105
106size_t AeadModeOperation::EstimateOutputSize(const Buffer& input, Buffer* output) {
107 switch (purpose()) {
108 case KM_PURPOSE_ENCRYPT: {
109 size_t chunk_length = processing_unit_;
110 size_t chunk_count = (input.available_read() + chunk_length - 1) / chunk_length;
111 return output->available_read() + nonce_length_ +
112 chunk_count * (chunk_length + tag_length_);
113 }
114 case KM_PURPOSE_DECRYPT: {
115 size_t chunk_length = processing_unit_ - tag_length_;
116 size_t chunk_count =
117 (input.available_read() - nonce_length_ + processing_unit_ - 1) / processing_unit_;
118 return output->available_read() + chunk_length * chunk_count;
119 }
120 default:
121 logger().error("Encountered invalid purpose %d", purpose());
122 return 0;
123 }
124}
125
126keymaster_error_t AeadModeOperation::HandleNonce(Buffer* output) {
127 switch (purpose()) {
128 case KM_PURPOSE_ENCRYPT:
129 if (!RAND_bytes(nonce_, nonce_length_)) {
130 logger().error("Failed to generate nonce");
131 return KM_ERROR_UNKNOWN_ERROR;
132 }
133 if (!output->reserve(nonce_length_))
134 return KM_ERROR_MEMORY_ALLOCATION_FAILED;
135 output->write(nonce_, nonce_length_);
136 nonce_handled_ = true;
137 break;
138 case KM_PURPOSE_DECRYPT:
139 if (buffered_data_length() >= nonce_length_) {
140 memcpy(nonce_, buffer_.get(), nonce_length_);
141 memmove(buffer_.get(), buffer_.get() + nonce_length_,
142 buffered_data_length() - nonce_length_);
143 buffer_end_ -= nonce_length_;
144 nonce_handled_ = true;
145 }
146 break;
147 default:
148 return KM_ERROR_UNSUPPORTED_PURPOSE;
149 }
150 return KM_ERROR_OK;
151}
152
153void AeadModeOperation::IncrementNonce() {
154 for (int i = nonce_length_ - 1; i > 0; --i)
155 if (++nonce_[i])
156 break;
157}
158
159const uint8_t* AeadModeOperation::AppendToBuffer(const uint8_t* data, size_t data_length) {
160 // Only take as much data as we can fit.
161 if (data_length > buffer_free_space())
162 data_length = buffer_free_space();
163 memcpy(buffer_.get() + buffer_end_, data, data_length);
164 buffer_end_ += data_length;
165 return data + data_length;
166}
167
168void AeadModeOperation::ExtractTagFromBuffer() {
169 assert(buffered_data_length() >= tag_length_);
170 memcpy(tag_, buffer_.get() + buffer_end_ - tag_length_, tag_length_);
171 buffer_end_ -= tag_length_;
172}
173
174} // namespace keymaster