1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/quic/crypto/p256_key_exchange.h"
6
7#include "base/logging.h"
8#include "base/sys_byteorder.h"
9
10using base::StringPiece;
11using std::string;
12using std::vector;
13
14namespace net {
15
16namespace {
17
18// Password used by |NewPrivateKey| to encrypt exported EC private keys.
19// This is not used to provide any security, but to workaround NSS being
20// unwilling to export unencrypted EC keys. Note that SPDY and ChannelID
21// use the same approach.
22const char kExportPassword[] = "";
23
24// Convert StringPiece to vector of uint8.
25static vector<uint8> StringPieceToVector(StringPiece piece) {
26  return vector<uint8>(piece.data(), piece.data() + piece.length());
27}
28
29}  // namespace
30
31P256KeyExchange::P256KeyExchange(crypto::ECPrivateKey* key_pair,
32                                 const uint8* public_key)
33    : key_pair_(key_pair) {
34  memcpy(public_key_, public_key, sizeof(public_key_));
35}
36
37P256KeyExchange::~P256KeyExchange() {
38}
39
40// static
41P256KeyExchange* P256KeyExchange::New(StringPiece key) {
42  if (key.size() < 2) {
43    DVLOG(1) << "Key pair is too small.";
44    return NULL;
45  }
46
47  const uint8* data = reinterpret_cast<const uint8*>(key.data());
48  size_t size = static_cast<size_t>(data[0]) |
49                (static_cast<size_t>(data[1]) << 8);
50  key.remove_prefix(2);
51  if (key.size() < size) {
52    DVLOG(1) << "Key pair does not contain key material.";
53    return NULL;
54  }
55
56  StringPiece private_piece(key.data(), size);
57  key.remove_prefix(size);
58  if (key.empty()) {
59    DVLOG(1) << "Key pair does not contain public key.";
60    return NULL;
61  }
62
63  StringPiece public_piece(key);
64
65  scoped_ptr<crypto::ECPrivateKey> key_pair(
66      crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
67          kExportPassword,
68          // TODO(thaidn): fix this interface to avoid copying secrets.
69          StringPieceToVector(private_piece),
70          StringPieceToVector(public_piece)));
71
72  if (!key_pair.get()) {
73    DVLOG(1) << "Can't decrypt private key.";
74    return NULL;
75  }
76
77  // Perform some sanity checks on the public key.
78  SECKEYPublicKey* public_key = key_pair->public_key();
79  if (public_key->keyType != ecKey ||
80      public_key->u.ec.publicValue.len != kUncompressedP256PointBytes ||
81      !public_key->u.ec.publicValue.data ||
82      public_key->u.ec.publicValue.data[0] != kUncompressedECPointForm) {
83    DVLOG(1) << "Key is invalid.";
84    return NULL;
85  }
86
87  // Ensure that the key is using the correct curve, i.e., NIST P-256.
88  const SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
89  if (!oid_data) {
90    DVLOG(1) << "Can't get P-256's OID.";
91    return NULL;
92  }
93
94  if (public_key->u.ec.DEREncodedParams.len != oid_data->oid.len + 2 ||
95      !public_key->u.ec.DEREncodedParams.data ||
96      public_key->u.ec.DEREncodedParams.data[0] != SEC_ASN1_OBJECT_ID ||
97      public_key->u.ec.DEREncodedParams.data[1] != oid_data->oid.len ||
98      memcmp(public_key->u.ec.DEREncodedParams.data + 2,
99             oid_data->oid.data, oid_data->oid.len) != 0) {
100    DVLOG(1) << "Key is invalid.";
101  }
102
103  return new P256KeyExchange(key_pair.release(),
104                             public_key->u.ec.publicValue.data);
105}
106
107// static
108string P256KeyExchange::NewPrivateKey() {
109  scoped_ptr<crypto::ECPrivateKey> key_pair(crypto::ECPrivateKey::Create());
110
111  if (!key_pair.get()) {
112    DVLOG(1) << "Can't generate new key pair.";
113    return string();
114  }
115
116  vector<uint8> private_key;
117  if (!key_pair->ExportEncryptedPrivateKey(kExportPassword,
118                                           1 /* iteration */,
119                                           &private_key)) {
120    DVLOG(1) << "Can't export private key.";
121    return string();
122  }
123
124  // NSS lacks the ability to import an ECC private key without
125  // also importing the public key, so it is necessary to also
126  // store the public key.
127  vector<uint8> public_key;
128  if (!key_pair->ExportPublicKey(&public_key)) {
129    DVLOG(1) << "Can't export public key.";
130    return string();
131  }
132
133  // TODO(thaidn): determine how large encrypted private key can be
134  uint16 private_key_size = private_key.size();
135  const size_t result_size = sizeof(private_key_size) +
136                             private_key_size +
137                             public_key.size();
138  vector<char> result(result_size);
139  char* resultp = &result[0];
140  // Export the key string.
141  // The first two bytes are the private key's size in little endian.
142  private_key_size = base::ByteSwapToLE16(private_key_size);
143  memcpy(resultp, &private_key_size, sizeof(private_key_size));
144  resultp += sizeof(private_key_size);
145  memcpy(resultp, &private_key[0], private_key.size());
146  resultp += private_key.size();
147  memcpy(resultp, &public_key[0], public_key.size());
148
149  return string(&result[0], result_size);
150}
151
152KeyExchange* P256KeyExchange::NewKeyPair(QuicRandom* /*rand*/) const {
153  // TODO(agl): avoid the serialisation/deserialisation in this function.
154  const string private_value = NewPrivateKey();
155  return P256KeyExchange::New(private_value);
156}
157
158bool P256KeyExchange::CalculateSharedKey(const StringPiece& peer_public_value,
159                                         string* out_result) const {
160  if (peer_public_value.size() != kUncompressedP256PointBytes ||
161      peer_public_value[0] != kUncompressedECPointForm) {
162    DVLOG(1) << "Peer public value is invalid.";
163    return false;
164  }
165
166  DCHECK(key_pair_.get());
167  DCHECK(key_pair_->public_key());
168
169  SECKEYPublicKey peer_public_key;
170  memset(&peer_public_key, 0, sizeof(peer_public_key));
171
172  peer_public_key.keyType = ecKey;
173  // Both sides of a ECDH key exchange need to use the same EC params.
174  peer_public_key.u.ec.DEREncodedParams.len =
175      key_pair_->public_key()->u.ec.DEREncodedParams.len;
176  peer_public_key.u.ec.DEREncodedParams.data =
177      key_pair_->public_key()->u.ec.DEREncodedParams.data;
178
179  peer_public_key.u.ec.publicValue.type = siBuffer;
180  peer_public_key.u.ec.publicValue.data =
181      reinterpret_cast<uint8*>(const_cast<char*>(peer_public_value.data()));
182  peer_public_key.u.ec.publicValue.len = peer_public_value.size();
183
184  // The NSS function performing ECDH key exchange is PK11_PubDeriveWithKDF.
185  // As this function is used for SSL/TLS's ECDH key exchanges it has many
186  // arguments, most of which are not required in QUIC.
187  // Key derivation function CKD_NULL is used because the return value of
188  // |CalculateSharedKey| is the actual ECDH shared key, not any derived keys
189  // from it.
190  crypto::ScopedPK11SymKey premaster_secret(
191      PK11_PubDeriveWithKDF(
192          key_pair_->key(),
193          &peer_public_key,
194          PR_FALSE,
195          NULL,
196          NULL,
197          CKM_ECDH1_DERIVE, /* mechanism */
198          CKM_GENERIC_SECRET_KEY_GEN, /* target */
199          CKA_DERIVE,
200          0,
201          CKD_NULL, /* kdf */
202          NULL,
203          NULL));
204
205  if (!premaster_secret.get()) {
206    DVLOG(1) << "Can't derive ECDH shared key.";
207    return false;
208  }
209
210  if (PK11_ExtractKeyValue(premaster_secret.get()) != SECSuccess) {
211    DVLOG(1) << "Can't extract raw ECDH shared key.";
212    return false;
213  }
214
215  SECItem* key_data = PK11_GetKeyData(premaster_secret.get());
216  if (!key_data || !key_data->data || key_data->len != kP256FieldBytes) {
217    DVLOG(1) << "ECDH shared key is invalid.";
218    return false;
219  }
220
221  out_result->assign(reinterpret_cast<char*>(key_data->data), key_data->len);
222  return true;
223}
224
225StringPiece P256KeyExchange::public_value() const {
226  return StringPiece(reinterpret_cast<const char*>(public_key_),
227                     sizeof(public_key_));
228}
229
230QuicTag P256KeyExchange::tag() const { return kP256; }
231
232}  // namespace net
233
234