GCM tags in ciphertext, rather than in params.
Also, handle AAD correctly.
Bug: 21786749
Change-Id: I735f3d277c96bf4d3fed3ef433f96238b1f21187
diff --git a/aead_mode_operation.cpp b/aead_mode_operation.cpp
deleted file mode 100644
index 357dce6..0000000
--- a/aead_mode_operation.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * 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
diff --git a/aead_mode_operation.h b/aead_mode_operation.h
deleted file mode 100644
index ff73a7f..0000000
--- a/aead_mode_operation.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef SYSTEM_KEYMASTER_AEAD_MODE_OPERATION_H_
-#define SYSTEM_KEYMASTER_AEAD_MODE_OPERATION_H_
-
-#include "operation.h"
-
-namespace keymaster {
-
-class AeadModeOperation : public Operation {
- public:
- static const size_t MAX_CHUNK_LENGTH = 64 * 1024;
- static const size_t MAX_NONCE_LENGTH = 12;
- static const size_t MAX_TAG_LENGTH = 16;
- static const size_t MAX_KEY_LENGTH = 32;
-
- AeadModeOperation(keymaster_purpose_t purpose, const uint8_t* key, size_t key_size,
- size_t chunk_length, size_t tag_length, size_t nonce_length,
- bool caller_nonce)
- : Operation(purpose), key_size_(key_size), tag_length_(tag_length),
- nonce_length_(nonce_length),
- processing_unit_(purpose == KM_PURPOSE_DECRYPT ? chunk_length + tag_length
- : chunk_length),
- caller_nonce_(caller_nonce) {
- assert(key_size <= MAX_KEY_LENGTH);
- memcpy(key_, key, key_size);
- }
- ~AeadModeOperation() {
- // Wipe sensitive buffers.
- memset_s(buffer_.get(), 0, processing_unit_);
- memset_s(key_, 0, MAX_KEY_LENGTH);
- }
-
- virtual keymaster_error_t Begin(const AuthorizationSet& input_params,
- AuthorizationSet* output_params);
- virtual keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
- AuthorizationSet* output_params, Buffer* output,
- size_t* input_consumed);
- virtual keymaster_error_t Finish(const AuthorizationSet& additional_params,
- const Buffer& signature, AuthorizationSet* output_params,
- Buffer* output);
-
- protected:
- size_t buffered_data_length() const { return buffer_end_; }
- const uint8_t* key() const { return key_; }
- size_t key_size() const { return key_size_; }
-
- private:
- /*
- * These methods do the actual crypto operations.
- *
- * TODO(swillden): Consider refactoring these to a separate class, integrating them via
- * composition rather than inheritance.
- */
- virtual keymaster_error_t Initialize(uint8_t* key, size_t key_size, size_t nonce_length,
- size_t tag_length) = 0;
- virtual keymaster_error_t EncryptChunk(const uint8_t* nonce, size_t nonce_length,
- size_t tag_length,
- const keymaster_blob_t additional_data, uint8_t* chunk,
- size_t chunk_size, Buffer* output) = 0;
- virtual keymaster_error_t DecryptChunk(const uint8_t* nonce, size_t nonce_length,
- const uint8_t* tag, size_t tag_length,
- const keymaster_blob_t additional_data, uint8_t* chunk,
- size_t chunk_size, Buffer* output) = 0;
-
- size_t EstimateOutputSize(const Buffer& input, Buffer* output);
- keymaster_error_t ProcessChunk(const keymaster_blob_t& associated_data, Buffer* output);
-
- size_t buffer_free_space() const { return processing_unit_ - buffer_end_; }
-
- const uint8_t* AppendToBuffer(const uint8_t* data, size_t data_length);
- void ExtractTagFromBuffer();
- void ClearBuffer() { buffer_end_ = 0; }
- keymaster_error_t HandleNonce(const AuthorizationSet& input_params,
- AuthorizationSet* output_params);
- keymaster_error_t ExtractNonce(const AuthorizationSet& input_params);
- keymaster_error_t GenerateNonce();
- void IncrementNonce();
-
- const size_t key_size_;
- const size_t tag_length_;
- const size_t nonce_length_;
- const size_t processing_unit_;
- const bool caller_nonce_;
- UniquePtr<uint8_t[]> buffer_;
- size_t buffer_end_;
- uint8_t __attribute__((aligned(16))) key_[MAX_KEY_LENGTH];
- uint8_t __attribute__((aligned(16))) tag_[MAX_TAG_LENGTH];
- uint8_t __attribute__((aligned(16))) nonce_[MAX_NONCE_LENGTH];
-};
-
-} // namespace keymaster
-
-#endif // SYSTEM_KEYMASTER_AEAD_MODE_OPERATION_H_
diff --git a/aes_operation.cpp b/aes_operation.cpp
index 7c62d74..c71a1c3 100644
--- a/aes_operation.cpp
+++ b/aes_operation.cpp
@@ -63,7 +63,7 @@
}
size_t tag_length = 0;
- if (block_mode == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
+ if (block_mode == KM_MODE_GCM) {
uint32_t tag_length_bits;
if (!begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length_bits))
*error = KM_ERROR_MISSING_MAC_LENGTH;
@@ -93,21 +93,15 @@
if (*error != KM_ERROR_OK)
return nullptr;
- return CreateEvpOperation(*symmetric_key, block_mode, padding, caller_nonce, tag_length, error);
-}
-
-Operation* AesOperationFactory::CreateEvpOperation(const SymmetricKey& key,
- keymaster_block_mode_t block_mode,
- keymaster_padding_t padding, bool caller_iv,
- size_t tag_length, keymaster_error_t* error) {
Operation* op = NULL;
switch (purpose()) {
case KM_PURPOSE_ENCRYPT:
- op = new AesEvpEncryptOperation(block_mode, padding, caller_iv, tag_length, key.key_data(),
- key.key_data_size());
+ op = new AesEvpEncryptOperation(block_mode, padding, caller_nonce, tag_length,
+ symmetric_key->key_data(), symmetric_key->key_data_size());
break;
case KM_PURPOSE_DECRYPT:
- op = new AesEvpDecryptOperation(block_mode, padding, key.key_data(), key.key_data_size());
+ op = new AesEvpDecryptOperation(block_mode, padding, tag_length, symmetric_key->key_data(),
+ symmetric_key->key_data_size());
break;
default:
*error = KM_ERROR_UNSUPPORTED_PURPOSE;
@@ -136,16 +130,90 @@
}
AesEvpOperation::AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
- keymaster_padding_t padding, bool caller_iv, const uint8_t* key,
- size_t key_size)
- : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), aad_provided_(false),
- key_size_(key_size), padding_(padding) {
+ keymaster_padding_t padding, bool caller_iv, size_t tag_length,
+ const uint8_t* key, size_t key_size)
+ : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), tag_length_(tag_length),
+ data_started_(false), key_size_(key_size), padding_(padding) {
memcpy(key_, key, key_size_);
EVP_CIPHER_CTX_init(&ctx_);
}
AesEvpOperation::~AesEvpOperation() {
EVP_CIPHER_CTX_cleanup(&ctx_);
+ memset_s(aad_block_buf_.get(), AES_BLOCK_SIZE, 0);
+}
+
+keymaster_error_t AesEvpOperation::Begin(const AuthorizationSet& /* input_params */,
+ AuthorizationSet* /* output_params */) {
+ if (block_mode_ == KM_MODE_GCM) {
+ aad_block_buf_length_ = 0;
+ aad_block_buf_.reset(new uint8_t[AES_BLOCK_SIZE]);
+ if (!aad_block_buf_.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ return InitializeCipher();
+}
+
+keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& additional_params,
+ const Buffer& input,
+ AuthorizationSet* /* output_params */, Buffer* output,
+ size_t* input_consumed) {
+ keymaster_error_t error;
+ if (block_mode_ == KM_MODE_GCM)
+ if (!HandleAad(additional_params, input, &error))
+ return error;
+
+ if (!InternalUpdate(input.peek_read(), input.available_read(), output, &error))
+ return error;
+ *input_consumed = input.available_read();
+
+ return KM_ERROR_OK;
+}
+
+inline bool is_bad_decrypt(unsigned long error) {
+ return (ERR_GET_LIB(error) == ERR_LIB_CIPHER && //
+ ERR_GET_REASON(error) == CIPHER_R_BAD_DECRYPT);
+}
+
+keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_params */,
+ const Buffer& /* signature */,
+ AuthorizationSet* /* output_params */, Buffer* output) {
+ if (!output->reserve(AES_BLOCK_SIZE))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ keymaster_error_t error;
+ if (block_mode_ == KM_MODE_GCM && aad_block_buf_length_ > 0 &&
+ !ProcessBufferedAadBlock(&error)) {
+ return error;
+ }
+
+ int output_written = -1;
+ if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) {
+ if (tag_length_ > 0)
+ return KM_ERROR_VERIFICATION_FAILED;
+ LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL));
+ return TranslateLastOpenSslError();
+ }
+
+ assert(output_written <= AES_BLOCK_SIZE);
+ output->advance_write(output_written);
+ return KM_ERROR_OK;
+}
+
+bool AesEvpOperation::need_iv() const {
+ switch (block_mode_) {
+ case KM_MODE_CBC:
+ case KM_MODE_CTR:
+ case KM_MODE_GCM:
+ return true;
+ case KM_MODE_ECB:
+ return false;
+ default:
+ // Shouldn't get here.
+ assert(false);
+ return false;
+ }
}
keymaster_error_t AesEvpOperation::InitializeCipher() {
@@ -229,36 +297,128 @@
return KM_ERROR_UNSUPPORTED_PADDING_MODE;
}
+ if (block_mode_ == KM_MODE_GCM) {
+ aad_block_buf_length_ = 0;
+ aad_block_buf_.reset(new uint8_t[AES_BLOCK_SIZE]);
+ if (!aad_block_buf_.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
return KM_ERROR_OK;
}
-bool AesEvpOperation::need_iv() const {
- switch (block_mode_) {
- case KM_MODE_CBC:
- case KM_MODE_CTR:
- case KM_MODE_GCM:
- return true;
- case KM_MODE_ECB:
- return false;
- default:
- // Shouldn't get here.
- assert(false);
- return false;
+keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
+ keymaster_blob_t iv_blob;
+ if (!input_params.GetTagValue(TAG_NONCE, &iv_blob)) {
+ LOG_E("No IV provided", 0);
+ return KM_ERROR_INVALID_ARGUMENT;
}
+ if (block_mode_ != KM_MODE_GCM && iv_blob.data_length != AES_BLOCK_SIZE) {
+ LOG_E("Expected %d-byte IV for AES operation, but got %d bytes", AES_BLOCK_SIZE,
+ iv_blob.data_length);
+ return KM_ERROR_INVALID_NONCE;
+ }
+ iv_.reset(dup_array(iv_blob.data, iv_blob.data_length));
+ if (!iv_.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ iv_length_ = iv_blob.data_length;
+ return KM_ERROR_OK;
}
-keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params,
- AuthorizationSet* output_params) {
- if (!output_params)
- return KM_ERROR_OUTPUT_PARAMETER_NULL;
+/*
+ * Process Incoming Associated Authentication Data.
+ *
+ * This method is more complex than might be expected, because the underlying library silently does
+ * the wrong thing when given partial AAD blocks, so we have to take care to process AAD in
+ * AES_BLOCK_SIZE increments, buffering (in aad_block_buf_) when given smaller amounts of data.
+ */
+bool AesEvpOperation::HandleAad(const AuthorizationSet& input_params, const Buffer& input,
+ keymaster_error_t* error) {
+ assert(tag_length_ > 0);
+ assert(error);
- if (need_iv()) {
- keymaster_error_t error = GetIv(input_params);
- if (error != KM_ERROR_OK)
- return error;
+ keymaster_blob_t aad;
+ if (input_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) {
+ if (data_started_) {
+ *error = KM_ERROR_INVALID_TAG;
+ return false;
+ }
+
+ if (aad_block_buf_length_ > 0) {
+ FillBufferedAadBlock(&aad);
+ if (aad_block_buf_length_ == AES_BLOCK_SIZE && !ProcessBufferedAadBlock(error))
+ return false;
+ }
+
+ size_t blocks_to_process = aad.data_length / AES_BLOCK_SIZE;
+ if (blocks_to_process && !ProcessAadBlocks(aad.data, blocks_to_process, error))
+ return false;
+ aad.data += blocks_to_process * AES_BLOCK_SIZE;
+ aad.data_length -= blocks_to_process * AES_BLOCK_SIZE;
+
+ FillBufferedAadBlock(&aad);
+ assert(aad.data_length == 0);
}
- return InitializeCipher();
+ if (input.available_read()) {
+ data_started_ = true;
+ // Data has begun, no more AAD is allowed. Process any buffered AAD.
+ if (aad_block_buf_length_ > 0 && !ProcessBufferedAadBlock(error))
+ return false;
+ }
+
+ return true;
+}
+
+bool AesEvpOperation::ProcessBufferedAadBlock(keymaster_error_t* error) {
+ int output_written;
+ if (EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, aad_block_buf_.get(),
+ aad_block_buf_length_)) {
+ aad_block_buf_length_ = 0;
+ return true;
+ }
+ *error = TranslateLastOpenSslError();
+ return false;
+}
+
+bool AesEvpOperation::ProcessAadBlocks(const uint8_t* data, size_t blocks,
+ keymaster_error_t* error) {
+ int output_written;
+ if (EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, data, blocks * AES_BLOCK_SIZE))
+ return true;
+ *error = TranslateLastOpenSslError();
+ return false;
+}
+
+inline size_t min(size_t a, size_t b) {
+ return (a < b) ? a : b;
+}
+
+void AesEvpOperation::FillBufferedAadBlock(keymaster_blob_t* aad) {
+ size_t to_buffer = min(AES_BLOCK_SIZE - aad_block_buf_length_, aad->data_length);
+ memcpy(aad_block_buf_.get() + aad_block_buf_length_, aad->data, to_buffer);
+ aad->data += to_buffer;
+ aad->data_length -= to_buffer;
+ aad_block_buf_length_ += to_buffer;
+}
+
+bool AesEvpOperation::InternalUpdate(const uint8_t* input, size_t input_length, Buffer* output,
+ keymaster_error_t* error) {
+ assert(output);
+ assert(error);
+
+ if (!output->reserve(input_length + AES_BLOCK_SIZE)) {
+ *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return false;
+ }
+
+ int output_written = -1;
+ if (!EVP_CipherUpdate(&ctx_, output->peek_write(), &output_written, input, input_length)) {
+ *error = TranslateLastOpenSslError();
+ return false;
+ }
+ output->advance_write(output_written);
+ return true;
}
keymaster_error_t AesEvpEncryptOperation::Begin(const AuthorizationSet& input_params,
@@ -281,30 +441,29 @@
return error;
}
- return InitializeCipher();
+ return AesEvpOperation::Begin(input_params, output_params);
}
-keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) {
- keymaster_blob_t iv_blob;
- if (!input_params.GetTagValue(TAG_NONCE, &iv_blob)) {
- LOG_E("No IV provided", 0);
- return KM_ERROR_INVALID_ARGUMENT;
- }
- if (block_mode_ == KM_MODE_GCM) {
- if (iv_blob.data_length != GCM_NONCE_SIZE) {
- LOG_E("Expected %d-byte nonce for AES-GCM operation, but got %d bytes", GCM_NONCE_SIZE,
- iv_blob.data_length);
- return KM_ERROR_INVALID_NONCE;
- }
- } else if (iv_blob.data_length != AES_BLOCK_SIZE) {
- LOG_E("Expected %d-byte IV for AES operation, but got %d bytes", AES_BLOCK_SIZE,
- iv_blob.data_length);
- return KM_ERROR_INVALID_NONCE;
- }
- iv_.reset(dup_array(iv_blob.data, iv_blob.data_length));
- if (!iv_.get())
+keymaster_error_t AesEvpEncryptOperation::Finish(const AuthorizationSet& additional_params,
+ const Buffer& signature,
+ AuthorizationSet* output_params, Buffer* output) {
+ if (!output->reserve(AES_BLOCK_SIZE + tag_length_))
return KM_ERROR_MEMORY_ALLOCATION_FAILED;
- iv_length_ = iv_blob.data_length;
+
+ keymaster_error_t error =
+ AesEvpOperation::Finish(additional_params, signature, output_params, output);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ if (tag_length_ > 0) {
+ if (!output->reserve(output->available_read() + tag_length_))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, tag_length_, output->peek_write()))
+ return TranslateLastOpenSslError();
+ output->advance_write(tag_length_);
+ }
+
return KM_ERROR_OK;
}
@@ -318,107 +477,105 @@
return KM_ERROR_OK;
}
-inline size_t min(size_t a, size_t b) {
- if (a < b)
- return a;
- return b;
-}
-
-keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& additional_params,
- const Buffer& input,
- AuthorizationSet* /* output_params */, Buffer* output,
- size_t* input_consumed) {
- if (!aad_provided_ && block_mode_ == KM_MODE_GCM) {
- keymaster_blob_t aad;
- if (additional_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) {
- // Incantation to add AAD is to call update with null output. Ugly.
- int output_written;
- if (!EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, aad.data,
- aad.data_length))
- return TranslateLastOpenSslError();
- }
- aad_provided_ = true;
+keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) {
+ if (need_iv()) {
+ keymaster_error_t error = GetIv(input_params);
+ if (error != KM_ERROR_OK)
+ return error;
}
- output->reserve(input.available_read() + AES_BLOCK_SIZE);
+ if (tag_length_ > 0) {
+ tag_buf_length_ = 0;
+ tag_buf_.reset(new uint8_t[tag_length_]);
+ if (!tag_buf_.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
- const uint8_t* input_pos = input.peek_read();
- const uint8_t* input_end = input_pos + input.available_read();
-
- int output_written = -1;
- if (!EVP_CipherUpdate(&ctx_, output->peek_write(), &output_written, input_pos,
- input_end - input_pos))
- return TranslateLastOpenSslError();
-
- assert(output_written >= 0);
- assert(output_written <= (int)output->available_write());
- output->advance_write(output_written);
- *input_consumed = input.available_read();
- return KM_ERROR_OK;
+ return AesEvpOperation::Begin(input_params, output_params);
}
keymaster_error_t AesEvpDecryptOperation::Update(const AuthorizationSet& additional_params,
const Buffer& input,
- AuthorizationSet* output_params, Buffer* output,
- size_t* input_consumed) {
- if (!tag_provided_ && block_mode_ == KM_MODE_GCM && purpose() == KM_PURPOSE_DECRYPT) {
- keymaster_blob_t tag;
- if (additional_params.GetTagValue(TAG_AEAD_TAG, &tag)) {
- if (tag.data_length < GCM_MIN_TAG_LENGTH || tag.data_length > GCM_MAX_TAG_LENGTH)
- return KM_ERROR_UNSUPPORTED_MAC_LENGTH;
+ AuthorizationSet* /* output_params */,
+ Buffer* output, size_t* input_consumed) {
+ if (!output || !input_consumed)
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
- if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_TAG, tag.data_length,
- const_cast<uint8_t*>(tag.data)))
- return TranslateLastOpenSslError();
- tag_provided_ = true;
- }
- if (!tag_provided_)
- return KM_ERROR_INVALID_TAG;
+ // Barring error, we'll consume it all.
+ *input_consumed = input.available_read();
+
+ keymaster_error_t error;
+ if (block_mode_ == KM_MODE_GCM) {
+ if (!HandleAad(additional_params, input, &error))
+ return error;
+ return ProcessAllButTagLengthBytes(input, output);
}
- return AesEvpOperation::Update(additional_params, input, output_params, output, input_consumed);
-}
-
-inline bool is_bad_decrypt(unsigned long error) {
- return (ERR_GET_LIB(error) == ERR_LIB_CIPHER && //
- ERR_GET_REASON(error) == CIPHER_R_BAD_DECRYPT);
-}
-
-keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& /* additional_params */,
- const Buffer& /* signature */,
- AuthorizationSet* /* output_params */, Buffer* output) {
- output->reserve(AES_BLOCK_SIZE);
-
- int output_written = -1;
- if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) {
- if (block_mode_ == KM_MODE_GCM && is_bad_decrypt(ERR_peek_last_error()))
- return KM_ERROR_VERIFICATION_FAILED;
- LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL));
- return TranslateLastOpenSslError();
- }
-
- assert(output_written <= AES_BLOCK_SIZE);
- output->advance_write(output_written);
+ if (!InternalUpdate(input.peek_read(), input.available_read(), output, &error))
+ return error;
return KM_ERROR_OK;
}
-keymaster_error_t AesEvpEncryptOperation::Finish(const AuthorizationSet& additional_params,
- const Buffer& signature,
- AuthorizationSet* output_params, Buffer* output) {
- keymaster_error_t error =
- AesEvpOperation::Finish(additional_params, signature, output_params, output);
- if (error != KM_ERROR_OK)
+keymaster_error_t AesEvpDecryptOperation::ProcessAllButTagLengthBytes(const Buffer& input,
+ Buffer* output) {
+ if (input.available_read() <= tag_buf_unused()) {
+ BufferCandidateTagData(input.peek_read(), input.available_read());
+ return KM_ERROR_OK;
+ }
+
+ const size_t data_available = tag_buf_length_ + input.available_read();
+
+ const size_t to_process = data_available - tag_length_;
+ const size_t to_process_from_tag_buf = min(to_process, tag_buf_length_);
+ const size_t to_process_from_input = to_process - to_process_from_tag_buf;
+
+ if (!output->reserve(to_process + AES_BLOCK_SIZE))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ keymaster_error_t error;
+ if (!ProcessTagBufContentsAsData(to_process_from_tag_buf, output, &error))
return error;
- if (block_mode_ == KM_MODE_GCM && purpose() == KM_PURPOSE_ENCRYPT) {
- uint8_t tag[tag_length_];
- if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, tag_length_, tag))
- return TranslateLastOpenSslError();
- output_params->push_back(TAG_AEAD_TAG, tag, tag_length_);
- }
+ if (!InternalUpdate(input.peek_read(), to_process_from_input, output, &error))
+ return error;
+
+ BufferCandidateTagData(input.peek_read() + to_process_from_input,
+ input.available_read() - to_process_from_input);
+ assert(tag_buf_unused() == 0);
return KM_ERROR_OK;
}
+
+bool AesEvpDecryptOperation::ProcessTagBufContentsAsData(size_t to_process, Buffer* output,
+ keymaster_error_t* error) {
+ assert(to_process <= tag_buf_length_);
+ if (!InternalUpdate(tag_buf_.get(), to_process, output, error))
+ return false;
+ if (to_process < tag_buf_length_)
+ memmove(tag_buf_.get(), tag_buf_.get() + to_process, tag_buf_length_ - to_process);
+ tag_buf_length_ -= to_process;
+ return true;
+}
+
+void AesEvpDecryptOperation::BufferCandidateTagData(const uint8_t* data, size_t data_length) {
+ assert(data_length <= tag_length_ - tag_buf_length_);
+ memcpy(tag_buf_.get() + tag_buf_length_, data, data_length);
+ tag_buf_length_ += data_length;
+}
+
+keymaster_error_t AesEvpDecryptOperation::Finish(const AuthorizationSet& additional_params,
+ const Buffer& signature,
+ AuthorizationSet* output_params, Buffer* output) {
+ if (tag_buf_length_ < tag_length_)
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ else if (tag_length_ > 0 &&
+ !EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_TAG, tag_length_, tag_buf_.get()))
+ return TranslateLastOpenSslError();
+
+ return AesEvpOperation::Finish(additional_params, signature, output_params, output);
+}
+
keymaster_error_t AesEvpOperation::Abort() {
return KM_ERROR_OK;
}
diff --git a/aes_operation.h b/aes_operation.h
index dc902a7..7c0bd72 100644
--- a/aes_operation.h
+++ b/aes_operation.h
@@ -19,21 +19,52 @@
#include <openssl/evp.h>
-#include "aead_mode_operation.h"
#include "ocb_utils.h"
#include "operation.h"
namespace keymaster {
+/**
+ * Abstract base for AES operation factories. This class does all of the work to create
+ * AES operations.
+ */
+class AesOperationFactory : public OperationFactory {
+ public:
+ KeyType registry_key() const override { return KeyType(KM_ALGORITHM_AES, purpose()); }
+
+ Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params,
+ keymaster_error_t* error) override;
+ const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const override;
+ const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const override;
+
+ virtual keymaster_purpose_t purpose() const = 0;
+};
+
+/**
+ * Concrete factory for AES encryption operations.
+ */
+class AesEncryptionOperationFactory : public AesOperationFactory {
+ keymaster_purpose_t purpose() const override { return KM_PURPOSE_ENCRYPT; }
+};
+
+/**
+ * Concrete factory for AES decryption operations.
+ */
+class AesDecryptionOperationFactory : public AesOperationFactory {
+ keymaster_purpose_t purpose() const override { return KM_PURPOSE_DECRYPT; }
+};
+
static const size_t MAX_EVP_KEY_SIZE = 32;
class AesEvpOperation : public Operation {
public:
AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode,
- keymaster_padding_t padding, bool caller_iv, const uint8_t* key,
- size_t key_size);
+ keymaster_padding_t padding, bool caller_iv, size_t tag_length,
+ const uint8_t* key, size_t key_size);
~AesEvpOperation();
+ keymaster_error_t Begin(const AuthorizationSet& input_params,
+ AuthorizationSet* output_params) override;
keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
AuthorizationSet* output_params, Buffer* output,
size_t* input_consumed) override;
@@ -47,15 +78,25 @@
bool need_iv() const;
keymaster_error_t InitializeCipher();
keymaster_error_t GetIv(const AuthorizationSet& input_params);
+ bool HandleAad(const AuthorizationSet& input_params, const Buffer& input,
+ keymaster_error_t* error);
+ bool ProcessAadBlocks(const uint8_t* data, size_t blocks, keymaster_error_t* error);
+ void FillBufferedAadBlock(keymaster_blob_t* aad);
+ bool ProcessBufferedAadBlock(keymaster_error_t* error);
+ bool InternalUpdate(const uint8_t* input, size_t input_length, Buffer* output,
+ keymaster_error_t* error);
const keymaster_block_mode_t block_mode_;
EVP_CIPHER_CTX ctx_;
UniquePtr<uint8_t[]> iv_;
size_t iv_length_;
const bool caller_iv_;
+ size_t tag_length_;
+ UniquePtr<uint8_t[]> aad_block_buf_;
+ size_t aad_block_buf_length_;
private:
- bool aad_provided_;
+ bool data_started_;
const size_t key_size_;
const keymaster_padding_t padding_;
uint8_t key_[MAX_EVP_KEY_SIZE];
@@ -65,8 +106,8 @@
public:
AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
bool caller_iv, size_t tag_length, const uint8_t* key, size_t key_size)
- : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, key, key_size),
- tag_length_(tag_length) {}
+ : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, tag_length, key,
+ key_size) {}
keymaster_error_t Begin(const AuthorizationSet& input_params,
AuthorizationSet* output_params) override;
@@ -77,64 +118,34 @@
private:
keymaster_error_t GenerateIv();
-
- size_t tag_length_;
};
class AesEvpDecryptOperation : public AesEvpOperation {
public:
AesEvpDecryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding,
- const uint8_t* key, size_t key_size)
+ size_t tag_length, const uint8_t* key, size_t key_size)
: AesEvpOperation(KM_PURPOSE_DECRYPT, block_mode, padding,
- false /* caller_iv -- don't care */, key, key_size),
- tag_provided_(false) {}
+ false /* caller_iv -- don't care */, tag_length, key, key_size) {}
keymaster_error_t Begin(const AuthorizationSet& input_params,
AuthorizationSet* output_params) override;
keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input,
AuthorizationSet* output_params, Buffer* output,
size_t* input_consumed) override;
+ keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& signature,
+ AuthorizationSet* output_params, Buffer* output) override;
int evp_encrypt_mode() override { return 0; }
private:
- bool tag_provided_;
-};
+ size_t tag_buf_unused() { return tag_length_ - tag_buf_length_; }
-/**
- * Abstract base for AES operation factories. This class does all of the work to create
- * AES operations.
- */
-class AesOperationFactory : public OperationFactory {
- public:
- virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_AES, purpose()); }
+ keymaster_error_t ProcessAllButTagLengthBytes(const Buffer& input, Buffer* output);
+ bool ProcessTagBufContentsAsData(size_t to_process, Buffer* output, keymaster_error_t* error);
+ void BufferCandidateTagData(const uint8_t* data, size_t data_length);
- virtual Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params,
- keymaster_error_t* error);
- virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const;
- virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const;
-
- virtual keymaster_purpose_t purpose() const = 0;
-
- private:
- virtual Operation* CreateEvpOperation(const SymmetricKey& key,
- keymaster_block_mode_t block_mode,
- keymaster_padding_t padding, bool caller_iv,
- size_t tag_length, keymaster_error_t* error);
-};
-
-/**
- * Concrete factory for AES encryption operations.
- */
-class AesEncryptionOperationFactory : public AesOperationFactory {
- keymaster_purpose_t purpose() const { return KM_PURPOSE_ENCRYPT; }
-};
-
-/**
- * Concrete factory for AES decryption operations.
- */
-class AesDecryptionOperationFactory : public AesOperationFactory {
- keymaster_purpose_t purpose() const { return KM_PURPOSE_DECRYPT; }
+ UniquePtr<uint8_t[]> tag_buf_;
+ size_t tag_buf_length_;
};
} // namespace keymaster
diff --git a/android_keymaster_test.cpp b/android_keymaster_test.cpp
index 1a56ddd..e5af555 100644
--- a/android_keymaster_test.cpp
+++ b/android_keymaster_test.cpp
@@ -398,8 +398,7 @@
for (size_t size : valid_sizes) {
EXPECT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(
KM_DIGEST_NONE)))
- << "Failed to generate size: "
- << size;
+ << "Failed to generate size: " << size;
}
if (GetParam()->algorithm_in_hardware(KM_ALGORITHM_EC))
@@ -2227,40 +2226,249 @@
begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
begin_params.push_back(TAG_MAC_LENGTH, 128);
- AuthorizationSet begin_out_params;
AuthorizationSet update_params;
update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
- AuthorizationSet update_out_params;
-
- AuthorizationSet finish_params;
- AuthorizationSet finish_out_params;
-
- string ciphertext;
- string discard;
- string plaintext;
-
- size_t input_consumed;
// Encrypt
+ AuthorizationSet begin_out_params;
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
&input_consumed));
EXPECT_EQ(message.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
- // Grab nonce & tag.
+ // Grab nonce
EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
- EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
begin_params.push_back(begin_out_params);
- update_params.push_back(finish_out_params);
// Decrypt.
- EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
&plaintext, &input_consumed));
EXPECT_EQ(ciphertext.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_OK, FinishOperation(&discard));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) {
+ uint8_t nonce[] = {
+ 0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
+ };
+ uint8_t ciphertext[] = {
+ 0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc, 0xd2, 0xcb, 0x16,
+ 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78, 0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a,
+ 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d, 0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76,
+ 0x76, 0x5e, 0xfb, 0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
+ 0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
+ };
+ string ciphertext_str(reinterpret_cast<char*>(ciphertext), sizeof(ciphertext));
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ begin_params.push_back(TAG_NONCE, nonce, sizeof(nonce));
+
+ string plaintext;
+ size_t input_consumed;
+
+ // Import correct key and decrypt
+ uint8_t good_key[] = {
+ 0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+ 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+ };
+ string good_key_str(reinterpret_cast<char*>(good_key), sizeof(good_key));
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_CALLER_NONCE),
+ KM_KEY_FORMAT_RAW, good_key_str));
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ // Import bad key and decrypt
+ uint8_t bad_key[] = {
+ 0xbb, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+ 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+ };
+ string bad_key_str(reinterpret_cast<char*>(bad_key), sizeof(bad_key));
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE),
+ KM_KEY_FORMAT_RAW, bad_key_str));
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmAadNoData) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string aad = "123456789012345678";
+ string empty_message;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, empty_message, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(empty_message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmIncremental) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "b", 1);
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+
+ // Send AAD, incrementally
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(0U, ciphertext.size());
+ }
+
+ // Now send data, incrementally, no data.
+ AuthorizationSet empty_params;
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, "a", &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(1U, input_consumed);
+ }
+ EXPECT_EQ(1000U, ciphertext.size());
+
+ // And finish.
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(1016U, ciphertext.size());
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+
+ // Send AAD, incrementally, no data
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &plaintext,
+ &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(0U, plaintext.size());
+ }
+
+ // Now send data, incrementally.
+ for (size_t i = 0; i < ciphertext.length(); ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, string(ciphertext.data() + i, 1),
+ &update_out_params, &plaintext, &input_consumed));
+ EXPECT_EQ(1U, input_consumed);
+ }
+ EXPECT_EQ(1000U, plaintext.size());
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foo", 3);
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+
+ // No data, AAD only.
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "" /* message */, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+
+ // AAD and data.
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt
+ update_params.Clear();
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foofoo", 6);
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
EXPECT_EQ(message, plaintext);
EXPECT_EQ(0, GetParam()->keymaster0_calls());
@@ -2271,50 +2479,44 @@
.AesEncryptionKey(128)
.Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
.Authorization(TAG_PADDING, KM_PAD_NONE)));
- string aad = "foobar";
string message = "12345678901234567890123456789012";
AuthorizationSet begin_params(client_params());
begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
begin_params.push_back(TAG_MAC_LENGTH, 128);
- AuthorizationSet begin_out_params;
AuthorizationSet update_params;
- update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
- AuthorizationSet update_out_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6);
AuthorizationSet finish_params;
AuthorizationSet finish_out_params;
- string ciphertext;
- string discard;
- string plaintext;
-
- size_t input_consumed;
-
// Encrypt
+ AuthorizationSet begin_out_params;
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ AuthorizationSet update_out_params;
+ string ciphertext;
+ size_t input_consumed;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
&input_consumed));
EXPECT_EQ(message.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
- // Grab nonce & tag.
+ // Grab nonce
EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
- EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
begin_params.push_back(begin_out_params);
+
update_params.Clear();
update_params.push_back(TAG_ASSOCIATED_DATA, "barfoo" /* Wrong AAD */, 6);
- update_params.push_back(finish_out_params);
// Decrypt.
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
&plaintext, &input_consumed));
EXPECT_EQ(ciphertext.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
- EXPECT_EQ(message, plaintext);
EXPECT_EQ(0, GetParam()->keymaster0_calls());
}
@@ -2323,54 +2525,35 @@
.AesEncryptionKey(128)
.Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
.Authorization(TAG_PADDING, KM_PAD_NONE)));
- string aad = "foobar";
string message = "12345678901234567890123456789012";
AuthorizationSet begin_params(client_params());
begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
begin_params.push_back(TAG_MAC_LENGTH, 128);
- AuthorizationSet begin_out_params;
AuthorizationSet update_params;
- update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
- AuthorizationSet update_out_params;
-
- AuthorizationSet finish_params;
- AuthorizationSet finish_out_params;
-
- string ciphertext;
- string discard;
- string plaintext;
-
- size_t input_consumed;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6);
// Encrypt
+ AuthorizationSet begin_out_params;
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ AuthorizationSet update_out_params;
+ string ciphertext;
+ size_t input_consumed;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
&input_consumed));
EXPECT_EQ(message.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
- // Try with wrong-length nonce. Should fail immediately.
- AuthorizationSet long_nonce(begin_params);
- long_nonce.push_back(TAG_NONCE, "1234567890123", 13);
- EXPECT_EQ(KM_ERROR_INVALID_NONCE,
- BeginOperation(KM_PURPOSE_DECRYPT, long_nonce, &begin_out_params));
- AuthorizationSet short_nonce(begin_params);
- short_nonce.push_back(TAG_NONCE, "12345678901", 11);
- EXPECT_EQ(KM_ERROR_INVALID_NONCE,
- BeginOperation(KM_PURPOSE_DECRYPT, short_nonce, &begin_out_params));
-
- // Now with correct length, but incorrect content.
- EXPECT_NE(-1, finish_out_params.find(TAG_AEAD_TAG));
- update_params.push_back(finish_out_params);
begin_params.push_back(TAG_NONCE, "123456789012", 12);
+ // Decrypt
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
&plaintext, &input_consumed));
EXPECT_EQ(ciphertext.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
// With wrong nonce, should have gotten garbage plaintext.
EXPECT_NE(message, plaintext);
@@ -2392,92 +2575,36 @@
AuthorizationSet update_params;
update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
- AuthorizationSet update_out_params;
-
- AuthorizationSet finish_params;
- AuthorizationSet finish_out_params;
-
- string ciphertext;
- string discard;
- string plaintext;
-
- size_t input_consumed;
// Encrypt
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ AuthorizationSet update_out_params;
+ string ciphertext;
+ size_t input_consumed;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
&input_consumed));
EXPECT_EQ(message.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
- // Grab nonce & tag; corrupt tag.
+ // Corrupt tag
+ (*ciphertext.rbegin())++;
+
+ // Grab nonce.
EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
begin_params.push_back(begin_out_params);
- keymaster_blob_t tag;
- EXPECT_TRUE(finish_out_params.GetTagValue(TAG_AEAD_TAG, &tag));
- const_cast<uint8_t*>(tag.data)[tag.data_length / 2]++;
- update_params.push_back(TAG_AEAD_TAG, tag);
// Decrypt.
EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
&plaintext, &input_consumed));
EXPECT_EQ(ciphertext.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&discard));
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
EXPECT_EQ(message, plaintext);
EXPECT_EQ(0, GetParam()->keymaster0_calls());
}
-TEST_P(EncryptionOperationsTest, AesGcmShortTag) {
- ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
- .AesEncryptionKey(128)
- .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
- .Authorization(TAG_PADDING, KM_PAD_NONE)));
- string aad = "foobar";
- string message = "123456789012345678901234567890123456";
- AuthorizationSet begin_params(client_params());
- begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
- begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
- begin_params.push_back(TAG_MAC_LENGTH, 128);
- AuthorizationSet begin_out_params;
-
- AuthorizationSet update_params;
- update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
- AuthorizationSet update_out_params;
-
- AuthorizationSet finish_params;
- AuthorizationSet finish_out_params;
-
- string ciphertext;
- string discard;
- string plaintext;
-
- size_t input_consumed;
-
- // Encrypt
- EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
- EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
- &input_consumed));
- EXPECT_EQ(message.size(), input_consumed);
- EXPECT_EQ(KM_ERROR_OK, FinishOperation(finish_params, "", &finish_out_params, &discard));
-
- EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
- begin_params.push_back(begin_out_params);
- keymaster_blob_t tag;
- EXPECT_TRUE(finish_out_params.GetTagValue(TAG_AEAD_TAG, &tag));
- tag.data_length = 11;
- update_params.push_back(TAG_AEAD_TAG, tag);
-
- // Decrypt.
- EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
- EXPECT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
- UpdateOperation(update_params, ciphertext, &update_out_params, &plaintext,
- &input_consumed));
-
- EXPECT_EQ(0, GetParam()->keymaster0_calls());
-}
-
typedef Keymaster1Test AddEntropyTest;
INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AddEntropyTest, test_params);
diff --git a/include/keymaster/keymaster_tags.h b/include/keymaster/keymaster_tags.h
index 3213a2e..954a93d 100644
--- a/include/keymaster/keymaster_tags.h
+++ b/include/keymaster/keymaster_tags.h
@@ -161,7 +161,6 @@
DEFINE_KEYMASTER_TAG(KM_INVALID, TAG_INVALID);
DEFINE_KEYMASTER_TAG(KM_INT, TAG_KEY_SIZE);
DEFINE_KEYMASTER_TAG(KM_INT, TAG_MAC_LENGTH);
-DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_AEAD_TAG);
DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE);
DEFINE_KEYMASTER_TAG(KM_LONG, TAG_RSA_PUBLIC_EXPONENT);
DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME);