Add ECDSA key generation.

Change-Id: I68a1d46e617124a8ccb7a4b2c09baae89603a5e0
diff --git a/ecdsa_operation.cpp b/ecdsa_operation.cpp
new file mode 100644
index 0000000..4435679
--- /dev/null
+++ b/ecdsa_operation.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 <openssl/ecdsa.h>
+
+#include "ecdsa_operation.h"
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+struct ECDSA_Delete {
+    void operator()(EC_KEY* p) { EC_KEY_free(p); }
+};
+
+struct EC_GROUP_Delete {
+    void operator()(EC_GROUP* p) { EC_GROUP_free(p); }
+};
+
+/* static */
+keymaster_error_t EcdsaOperation::Generate(uint32_t key_size_bits, UniquePtr<uint8_t[]>* key_data,
+                                           size_t* key_data_size) {
+    if (key_data == NULL || key_data_size == NULL)
+        return KM_ERROR_OUTPUT_PARAMETER_NULL;
+
+    UniquePtr<EC_KEY, ECDSA_Delete> ecdsa_key(EC_KEY_new());
+    UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new());
+    if (ecdsa_key.get() == NULL || pkey.get() == NULL)
+        return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+    UniquePtr<EC_GROUP, EC_GROUP_Delete> group;
+    switch (key_size_bits) {
+    case 192:
+        group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime192v1));
+        break;
+    case 224:
+        group.reset(EC_GROUP_new_by_curve_name(NID_secp224r1));
+        break;
+    case 256:
+        group.reset(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+        break;
+    case 384:
+        group.reset(EC_GROUP_new_by_curve_name(NID_secp384r1));
+        break;
+    case 521:
+        group.reset(EC_GROUP_new_by_curve_name(NID_secp521r1));
+        break;
+    default:
+        break;
+    }
+
+    if (group.get() == NULL)
+        // Technically, could also have been a memory allocation problem.
+        return KM_ERROR_UNSUPPORTED_KEY_SIZE;
+
+    EC_GROUP_set_point_conversion_form(group.get(), POINT_CONVERSION_UNCOMPRESSED);
+    EC_GROUP_set_asn1_flag(group.get(), OPENSSL_EC_NAMED_CURVE);
+
+    if (EC_KEY_set_group(ecdsa_key.get(), group.get()) != 1 ||
+        EC_KEY_generate_key(ecdsa_key.get()) != 1 || EC_KEY_check_key(ecdsa_key.get()) < 0 ||
+        !EVP_PKEY_assign_EC_KEY(pkey.get(), ecdsa_key.get()))
+        return KM_ERROR_UNKNOWN_ERROR;
+    else
+        release_because_ownership_transferred(ecdsa_key);
+
+    *key_data_size = i2d_PrivateKey(pkey.get(), NULL);
+    if (*key_data_size <= 0)
+        return KM_ERROR_UNKNOWN_ERROR;
+
+    key_data->reset(new uint8_t[*key_data_size]);
+    uint8_t* tmp = key_data->get();
+    i2d_PrivateKey(pkey.get(), &tmp);
+
+    return KM_ERROR_OK;
+}
+
+}  // namespace keymaster