File: ecdh.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (132 lines) | stat: -rw-r--r-- 5,087 bytes parent folder | download | duplicates (9)
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
131
132
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/containers/span.h"
#include "components/webcrypto/algorithm_implementation.h"
#include "components/webcrypto/algorithms/ec.h"
#include "components/webcrypto/algorithms/util.h"
#include "components/webcrypto/blink_key_handle.h"
#include "components/webcrypto/generate_key_result.h"
#include "components/webcrypto/status.h"
#include "crypto/openssl_util.h"
#include "crypto/secure_util.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
#include "third_party/boringssl/src/include/openssl/ec.h"
#include "third_party/boringssl/src/include/openssl/ecdh.h"
#include "third_party/boringssl/src/include/openssl/evp.h"

namespace webcrypto {

namespace {

// TODO(eroman): Support the "raw" format for ECDH key import + export, as
// specified by WebCrypto spec.

// TODO(eroman): Allow id-ecDH in SPKI and PKCS#8 import
// (http://crbug.com/389400)

class EcdhImplementation : public EcAlgorithm {
 public:
  EcdhImplementation()
      : EcAlgorithm(0,
                    blink::kWebCryptoKeyUsageDeriveKey |
                        blink::kWebCryptoKeyUsageDeriveBits) {}

  const char* GetJwkAlgorithm(
      const blink::WebCryptoNamedCurve curve) const override {
    // JWK import for ECDH does not enforce any required value for "alg".
    return "";
  }

  Status DeriveBits(const blink::WebCryptoAlgorithm& algorithm,
                    const blink::WebCryptoKey& base_key,
                    std::optional<unsigned int> length_bits,
                    std::vector<uint8_t>* derived_bytes) const override {
    if (base_key.GetType() != blink::kWebCryptoKeyTypePrivate)
      return Status::ErrorUnexpectedKeyType();

    // Verify the "publicKey" parameter. The only guarantee from Blink is that
    // it is a valid WebCryptoKey, but it could be any type.
    const blink::WebCryptoKey& public_key =
        algorithm.EcdhKeyDeriveParams()->PublicKey();

    if (public_key.GetType() != blink::kWebCryptoKeyTypePublic)
      return Status::ErrorEcdhPublicKeyWrongType();

    // Make sure it is an EC key.
    if (!public_key.Algorithm().EcParams())
      return Status::ErrorEcdhPublicKeyWrongType();

    // TODO(eroman): This is not described by the spec:
    // https://www.w3.org/Bugs/Public/show_bug.cgi?id=27404
    if (public_key.Algorithm().Id() != blink::kWebCryptoAlgorithmIdEcdh)
      return Status::ErrorEcdhPublicKeyWrongAlgorithm();

    // The public and private keys come from different key pairs, however their
    // curves must match.
    if (public_key.Algorithm().EcParams()->NamedCurve() !=
        base_key.Algorithm().EcParams()->NamedCurve()) {
      return Status::ErrorEcdhCurveMismatch();
    }

    EC_KEY* public_key_ec = EVP_PKEY_get0_EC_KEY(GetEVP_PKEY(public_key));

    const EC_POINT* public_key_point = EC_KEY_get0_public_key(public_key_ec);

    EC_KEY* private_key_ec = EVP_PKEY_get0_EC_KEY(GetEVP_PKEY(base_key));

    // The size of the shared secret is the field size in bytes (rounded up).
    // Note that, if rounding was required, the most significant bits of the
    // secret are zero. So for P-521, the maximum length is 528 bits, not 521.
    int field_size_bytes =
        NumBitsToBytes(EC_GROUP_get_degree(EC_KEY_get0_group(private_key_ec)));
    unsigned int field_size_bits =
        static_cast<unsigned int>(field_size_bytes * 8);

    // If a desired key length was not specified, default to the field size
    // (rounded up to nearest byte).
    unsigned int actual_length_bits = length_bits.value_or(field_size_bits);

    // Short-circuit when deriving an empty key.
    // TODO(eroman): ECDH_compute_key() is not happy when given a NULL output.
    //               http://crbug.com/464194.
    if (actual_length_bits == 0) {
      derived_bytes->clear();
      return Status::Success();
    }

    if (actual_length_bits > field_size_bits) {
      return Status::ErrorEcdhLengthTooBig(field_size_bits);
    }

    // Resize to target length in bytes (BoringSSL can operate on a shorter
    // buffer than field_size_bytes).
    derived_bytes->resize(NumBitsToBytes(actual_length_bits));

    int result = ECDH_compute_key(derived_bytes->data(), derived_bytes->size(),
                                  public_key_point, private_key_ec, nullptr);
    if (result < 0 || static_cast<size_t>(result) != derived_bytes->size())
      return Status::OperationError();

    TruncateToBitLength(actual_length_bits, derived_bytes);
    return actual_length_bits < field_size_bits
               ? Status::SuccessDeriveBitsTruncation()
               : Status::Success();
  }
};

}  // namespace

std::unique_ptr<AlgorithmImplementation> CreateEcdhImplementation() {
  return std::make_unique<EcdhImplementation>();
}

}  // namespace webcrypto