huangcm
2025-02-24 69ed55dec4b2116a19e4cca4393cbc014fce5fb2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Copyright 2015 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 <keymaster/km_openssl/nist_curve_key_exchange.h>
 
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
 
#include <keymaster/km_openssl/openssl_err.h>
 
namespace keymaster {
 
NistCurveKeyExchange::NistCurveKeyExchange(EC_KEY* private_key, keymaster_error_t* error)
    : private_key_(private_key) {
    if (!private_key_.get() || !EC_KEY_check_key(private_key_.get())) {
        *error = KM_ERROR_INVALID_ARGUMENT;
        return;
    }
    *error = ExtractPublicKey();
}
 
/* static */
NistCurveKeyExchange* NistCurveKeyExchange::GenerateKeyExchange(keymaster_ec_curve_t curve) {
    int curve_name;
    switch (curve) {
    case KM_EC_CURVE_P_224:
        curve_name = NID_secp224r1;
        break;
    case KM_EC_CURVE_P_256:
        curve_name = NID_X9_62_prime256v1;
        break;
    case KM_EC_CURVE_P_384:
        curve_name = NID_secp384r1;
        break;
    case KM_EC_CURVE_P_521:
        curve_name = NID_secp521r1;
        break;
    default:
        LOG_E("Not a NIST curve: %d", curve);
        return nullptr;
    }
 
    UniquePtr<EC_KEY, EC_KEY_Delete> key(EC_KEY_new_by_curve_name(curve_name));
    if (!key.get() || !EC_KEY_generate_key(key.get())) {
        return nullptr;
    }
    keymaster_error_t error;
    UniquePtr<NistCurveKeyExchange> key_exchange(new (std::nothrow)
                                                     NistCurveKeyExchange(key.get(), &error));
    if (!key_exchange.get()) error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
    if (error != KM_ERROR_OK) return nullptr;
    (void)key.release();
 
    return key_exchange.release();
}
 
keymaster_error_t NistCurveKeyExchange::ExtractPublicKey() {
    const EC_GROUP* group = EC_KEY_get0_group(private_key_.get());
    size_t field_len_bits;
    keymaster_error_t error = ec_get_group_size(group, &field_len_bits);
    if (error != KM_ERROR_OK) return error;
 
    shared_secret_len_ = (field_len_bits + 7) / 8;
    public_key_len_ = 1 + 2 * shared_secret_len_;
    public_key_.reset(new (std::nothrow) uint8_t[public_key_len_]);
    if (!public_key_.get()) return KM_ERROR_MEMORY_ALLOCATION_FAILED;
    if (EC_POINT_point2oct(group, EC_KEY_get0_public_key(private_key_.get()),
                           POINT_CONVERSION_UNCOMPRESSED, public_key_.get(), public_key_len_,
                           nullptr /* ctx */) != public_key_len_) {
        return TranslateLastOpenSslError();
    }
    return KM_ERROR_OK;
}
 
bool NistCurveKeyExchange::CalculateSharedKey(const Buffer& peer_public_value,
                                              Buffer* out_result) const {
 
    return CalculateSharedKey(peer_public_value.peek_read(), peer_public_value.available_read(),
                              out_result);
}
 
bool NistCurveKeyExchange::CalculateSharedKey(const uint8_t* peer_public_value,
                                              size_t peer_public_value_len,
                                              Buffer* out_result) const {
    const EC_GROUP* group = EC_KEY_get0_group(private_key_.get());
    UniquePtr<EC_POINT, EC_POINT_Delete> point(EC_POINT_new(group));
    if (!point.get() ||
        !EC_POINT_oct2point(/* also test if point is on curve */
                            group, point.get(), peer_public_value, peer_public_value_len,
                            nullptr /* ctx */) ||
        !EC_POINT_is_on_curve(group, point.get(), nullptr /* ctx */)) {
        LOG_E("Can't convert peer public value to point: %d", TranslateLastOpenSslError());
        return false;
    }
 
    UniquePtr<uint8_t[]> result(new (std::nothrow) uint8_t[shared_secret_len_]);
    if (!result.get()) return false;
    if (ECDH_compute_key(result.get(), shared_secret_len_, point.get(), private_key_.get(),
                         nullptr /* kdf */) != static_cast<int>(shared_secret_len_)) {
        LOG_E("Can't compute ECDH shared key: %d", TranslateLastOpenSslError());
        return false;
    }
 
    out_result->Reinitialize(result.get(), shared_secret_len_);
    return true;
}
 
bool NistCurveKeyExchange::public_value(Buffer* public_value) const {
    if (public_key_.get() != nullptr && public_key_len_ != 0) {
        return public_value->Reinitialize(public_key_.get(), public_key_len_);
    }
    return false;
}
 
}  // namespace keymaster