1 /* 2 * Copyright 2017 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 <stdint.h> 18 #include <stdio.h> 19 #include <string.h> 20 21 #include <openssl/ec.h> 22 #include <openssl/ecdh.h> 23 #include <openssl/obj_mac.h> 24 25 #define ECDH_KEY_LEN 33 26 #define ECDH_SHARED_SECRET_LEN 32 27 28 /* Computes P256 ECDH shared secret using |private_key| as generated by 29 * generate_p256_key() as the private key, and |other_public_key| as the public 30 * key. Writes ECDH_SHARED_SECRET_LEN bytes to |shared_secret| and returns 0 on 31 * success, returns -1 on failure. 32 */ 33 int shared_secret_compute(const uint8_t* private_key, 34 uint32_t private_key_len, 35 const uint8_t other_public_key[ECDH_KEY_LEN], 36 uint8_t shared_secret[ECDH_SHARED_SECRET_LEN]) { 37 int ret = -1; 38 EC_GROUP* group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); 39 EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_COMPRESSED); 40 EC_POINT* other_point = EC_POINT_new(group); 41 EC_KEY* pkey = EC_KEY_new(); 42 43 if (!EC_POINT_oct2point(group, other_point, other_public_key, ECDH_KEY_LEN, 44 NULL)) { 45 fprintf(stderr, "Deserializing other_public_key failed\n"); 46 goto end; 47 } 48 if (!d2i_ECPrivateKey(&pkey, &private_key, private_key_len)) { 49 fprintf(stderr, "Deserializing private_key failed\n"); 50 goto end; 51 } 52 EC_KEY_set_group(pkey, group); 53 ret = ECDH_compute_key(shared_secret, ECDH_SHARED_SECRET_LEN, other_point, 54 pkey, NULL); 55 if (ret != ECDH_SHARED_SECRET_LEN) { 56 fprintf(stderr, "Failed to compute shared secret: %d\n", ret); 57 ret = -1; 58 goto end; 59 } 60 ret = 0; 61 62 end: 63 EC_POINT_free(other_point); 64 EC_GROUP_free(group); 65 EC_KEY_free(pkey); 66 return ret; 67 } 68 69 /* Generates a new EC Key to be used for computing a shared secret with the 70 * device. On success, returns 0 and writes key material and number of bytes 71 * allocated for |*private_key| to |*private_key_len|, and writes ECDH_KEY_LEN 72 * bytes to public_key. Returns -1 on failure. 73 */ 74 int generate_p256_key(uint8_t** private_key, uint32_t* private_key_len, 75 uint8_t public_key[ECDH_KEY_LEN]) { 76 int pkey_len = 0, ret = -1; 77 EC_GROUP* group = NULL; 78 const EC_POINT* point = NULL; 79 EC_KEY* pkey = NULL; 80 uint8_t* tmp; 81 82 if (!private_key || !private_key_len || !public_key) { 83 fprintf(stderr, "Invalid input parameters\n"); 84 return -1; 85 } 86 pkey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); 87 if (!EC_KEY_generate_key(pkey)) { 88 fprintf(stderr, "Failed to generate key\n"); 89 return -1; 90 } 91 pkey_len = i2d_ECPrivateKey(pkey, NULL); 92 if (pkey_len == -1) { 93 fprintf(stderr, "Failed to get private key length\n"); 94 goto end; 95 } 96 *private_key_len = pkey_len; 97 *private_key = (uint8_t*)malloc(pkey_len); 98 tmp = *private_key; 99 if (!i2d_ECPrivateKey(pkey, &tmp)) { 100 fprintf(stderr, "Failed to serialize private key\n"); 101 goto end; 102 } 103 group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); 104 point = EC_KEY_get0_public_key(pkey); 105 if (!EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, 106 public_key, ECDH_KEY_LEN, NULL)) { 107 fprintf(stderr, "Failed to serialize public key\n"); 108 goto end; 109 } 110 ret = 0; 111 112 end: 113 EC_GROUP_free(group); 114 EC_KEY_free(pkey); 115 if (ret == -1 && *private_key) { 116 free(*private_key); 117 } 118 return ret; 119 } 120