1/*
2 * Copyright 2015 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 "ecies_kem.h"
18
19#include "nist_curve_key_exchange.h"
20#include "openssl_err.h"
21
22namespace keymaster {
23
24EciesKem::EciesKem(const AuthorizationSet& kem_description, keymaster_error_t* error) {
25    AuthorizationSet authorizations(kem_description);
26
27    if (!authorizations.GetTagValue(TAG_EC_CURVE, &curve_)) {
28        LOG_E("%s", "EciesKem: no curve specified");
29        *error = KM_ERROR_INVALID_ARGUMENT;
30        return;
31    }
32
33    switch (curve_) {
34    case KM_EC_CURVE_P_224:
35    case KM_EC_CURVE_P_256:
36    case KM_EC_CURVE_P_384:
37    case KM_EC_CURVE_P_521:
38        break;
39    default:
40        LOG_E("EciesKem: curve %d is unsupported", curve_);
41        *error = KM_ERROR_UNSUPPORTED_EC_CURVE;
42        return;
43    }
44
45    keymaster_kdf_t kdf;
46    if (!authorizations.GetTagValue(TAG_KDF, &kdf)) {
47        LOG_E("EciesKem: No KDF specified", 0);
48        *error = KM_ERROR_UNSUPPORTED_KDF;
49        return;
50    }
51    switch (kdf) {
52    case KM_KDF_RFC5869_SHA256:
53        kdf_.reset(new Rfc5869Sha256Kdf());
54        break;
55    default:
56        LOG_E("Kdf %d is unsupported", kdf);
57        *error = KM_ERROR_UNSUPPORTED_KDF;
58        return;
59    }
60    if (!kdf_.get()) {
61        *error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
62        return;
63    }
64
65    if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_bytes_to_generate_)) {
66        LOG_E("%s", "EciesKem: no key length specified");
67        *error = KM_ERROR_UNSUPPORTED_KEY_SIZE;
68        return;
69    }
70
71    single_hash_mode_ = authorizations.GetTagValue(TAG_ECIES_SINGLE_HASH_MODE);
72    *error = KM_ERROR_OK;
73}
74
75bool EciesKem::Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key,
76                       Buffer* output_encrypted_key) {
77    return Encrypt(peer_public_value.peek_read(), peer_public_value.available_read(),
78                   output_clear_key, output_encrypted_key);
79}
80
81// http://www.shoup.net/iso/std6.pdf, section 10.2.3.
82bool EciesKem::Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len,
83                       Buffer* output_clear_key, Buffer* output_encrypted_key) {
84
85    key_exchange_.reset(NistCurveKeyExchange::GenerateKeyExchange(curve_));
86    if (!key_exchange_.get()) {
87        return false;
88    }
89
90    Buffer shared_secret;
91    if (!key_exchange_->CalculateSharedKey(peer_public_value, peer_public_value_len,
92                                           &shared_secret)) {
93        LOG_E("EciesKem: ECDH failed, can't obtain shared secret", 0);
94        return false;
95    }
96    if (!key_exchange_->public_value(output_encrypted_key)) {
97        LOG_E("EciesKem: Can't obtain public value", 0);
98        return false;
99    }
100
101    Buffer z;
102    if (single_hash_mode_) {
103        // z is empty.
104    } else {
105        // z = C0
106        z.Reinitialize(output_encrypted_key->peek_read(), output_encrypted_key->available_read());
107    }
108
109    Buffer actual_secret(z.available_read() + shared_secret.available_read());
110    actual_secret.write(z.peek_read(), z.available_read());
111    actual_secret.write(shared_secret.peek_read(), shared_secret.available_read());
112
113    if (!kdf_->Init(actual_secret.peek_read(), actual_secret.available_read(), nullptr /* salt */,
114                    0 /* salt_len */)) {
115        LOG_E("EciesKem: KDF failed, can't derived keys", 0);
116        return false;
117    }
118    output_clear_key->Reinitialize(key_bytes_to_generate_);
119    if (!kdf_->GenerateKey(nullptr /* info */, 0 /* info length */, output_clear_key->peek_write(),
120                           key_bytes_to_generate_)) {
121        LOG_E("EciesKem: KDF failed, can't derived keys", 0);
122        return false;
123    }
124    output_clear_key->advance_write(key_bytes_to_generate_);
125
126    return true;
127}
128
129bool EciesKem::Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) {
130    return Decrypt(private_key, encrypted_key.peek_read(), encrypted_key.available_read(),
131                   output_key);
132}
133
134// http://www.shoup.net/iso/std6.pdf, section 10.2.4.
135bool EciesKem::Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key, size_t encrypted_key_len,
136                       Buffer* output_key) {
137
138    keymaster_error_t error;
139    key_exchange_.reset(new NistCurveKeyExchange(private_key, &error));
140    if (!key_exchange_.get() || error != KM_ERROR_OK) {
141        return false;
142    }
143
144    Buffer shared_secret;
145    if (!key_exchange_->CalculateSharedKey(encrypted_key, encrypted_key_len, &shared_secret)) {
146        LOG_E("EciesKem: ECDH failed, can't obtain shared secret", 0);
147        return false;
148    }
149
150    Buffer public_value;
151    if (!key_exchange_->public_value(&public_value)) {
152        LOG_E("%s", "EciesKem: Can't obtain public value");
153        return false;
154    }
155
156    Buffer z;
157    if (single_hash_mode_) {
158        // z is empty.
159    } else {
160        // z = C0
161        z.Reinitialize(public_value.peek_read(), public_value.available_read());
162    }
163
164    Buffer actual_secret(z.available_read() + shared_secret.available_read());
165    actual_secret.write(z.peek_read(), z.available_read());
166    actual_secret.write(shared_secret.peek_read(), shared_secret.available_read());
167
168    if (!kdf_->Init(actual_secret.peek_read(), actual_secret.available_read(), nullptr /* salt */,
169                    0 /* salt_len */)) {
170        LOG_E("%s", "EciesKem: KDF failed, can't derived keys");
171        return false;
172    }
173
174    output_key->Reinitialize(key_bytes_to_generate_);
175    if (!kdf_->GenerateKey(nullptr /* info */, 0 /* info_len */, output_key->peek_write(),
176                           key_bytes_to_generate_)) {
177        LOG_E("%s", "EciesKem: KDF failed, can't derived keys");
178        return false;
179    }
180    output_key->advance_write(key_bytes_to_generate_);
181
182    return true;
183}
184
185}  // namespace keymaster
186