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