Add AES OCB decryption.

Also, refactor to extract functionality that will be common to all AEAD modes.

Change-Id: I4bcf12c9d2d464ab1af559c69031904ffae45e25
diff --git a/aead_mode_operation.cpp b/aead_mode_operation.cpp
new file mode 100644
index 0000000..44d98cc
--- /dev/null
+++ b/aead_mode_operation.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 "aead_mode_operation.h"
+
+namespace keymaster {
+
+keymaster_error_t AeadModeOperation::Begin() {
+    keymaster_error_t error = Initialize(key_, key_size_, nonce_length_, tag_length_);
+    if (error == KM_ERROR_OK) {
+        buffer_end_ = 0;
+        buffer_.reset(new uint8_t[processing_unit_]);
+        if (!buffer_.get())
+            error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+    }
+    return error;
+}
+
+inline size_t min(size_t a, size_t b) {
+    if (a < b)
+        return a;
+    return b;
+}
+
+keymaster_error_t AeadModeOperation::Update(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;
+
+    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_) {
+            assert(nonce_handled_);
+            if (!nonce_handled_)
+                return KM_ERROR_UNKNOWN_ERROR;
+            error = ProcessChunk(output);
+            ClearBuffer();
+            IncrementNonce();
+        }
+        plaintext = AppendToBuffer(plaintext, plaintext_end - plaintext);
+        *input_consumed = plaintext - input.peek_read();
+        if (!nonce_handled_)
+            error = HandleNonce(output);
+    }
+    return error;
+}
+
+keymaster_error_t AeadModeOperation::Finish(const Buffer& /* signature */, Buffer* output) {
+    keymaster_error_t error = KM_ERROR_OK;
+    if (!nonce_handled_)
+        error = HandleNonce(output);
+    if (error != KM_ERROR_OK)
+        return error;
+    return ProcessChunk(output);
+}
+
+keymaster_error_t AeadModeOperation::ProcessChunk(Buffer* output) {
+    if (!nonce_handled_)
+        return KM_ERROR_INVALID_INPUT_LENGTH;
+
+    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();
+        logger().info("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:
+        logger().error("Encountered invalid purpose %d", purpose());
+        return 0;
+    }
+}
+
+keymaster_error_t AeadModeOperation::HandleNonce(Buffer* output) {
+    switch (purpose()) {
+    case KM_PURPOSE_ENCRYPT:
+        if (!RAND_bytes(nonce_, nonce_length_)) {
+            logger().error("Failed to generate nonce");
+            return KM_ERROR_UNKNOWN_ERROR;
+        }
+        if (!output->reserve(nonce_length_))
+            return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+        output->write(nonce_, nonce_length_);
+        nonce_handled_ = true;
+        break;
+    case KM_PURPOSE_DECRYPT:
+        if (buffered_data_length() >= nonce_length_) {
+            memcpy(nonce_, buffer_.get(), nonce_length_);
+            memmove(buffer_.get(), buffer_.get() + nonce_length_,
+                    buffered_data_length() - nonce_length_);
+            buffer_end_ -= nonce_length_;
+            nonce_handled_ = true;
+        }
+        break;
+    default:
+        return KM_ERROR_UNSUPPORTED_PURPOSE;
+    }
+    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