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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_WEBCRYPTO_JWK_H_
#define COMPONENTS_WEBCRYPTO_JWK_H_
#include <stdint.h>
#include <vector>
#include "base/strings/string_piece.h"
#include "base/values.h"
#include "third_party/WebKit/public/platform/WebCrypto.h"
namespace webcrypto {
class CryptoData;
class Status;
// Helper class for parsing a JWK from JSON.
//
// This primarily exists to ensure strict enforcement of the JWK schema, as the
// type and presence of particular members is security relevant. For example,
// GetString() will ensure a given JSON member is present and is a string type,
// and will fail if these conditions aren't met.
//
// Users of JwkReader must call Init() successfully before any other method can
// be called.
class JwkReader {
public:
JwkReader();
~JwkReader();
// Initializes a JWK reader by parsing the JSON |bytes|. To succeed, the JWK
// must:
// * Have "kty" matching |expected_kty|
// * Have "ext" compatible with |expected_extractable|
// * Have usages ("use", "key_ops") compatible with |expected_usages|
// * Have an "alg" matching |expected_alg|
//
// NOTE: If |expected_alg| is empty, then the test on "alg" is skipped.
Status Init(const CryptoData& bytes,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
const std::string& expected_kty,
const std::string& expected_alg);
// Returns true if the member |member_name| is present.
bool HasMember(const std::string& member_name) const;
// Extracts the required string member |member_name| and saves the result to
// |*result|. If the member does not exist or is not a string, returns an
// error.
Status GetString(const std::string& member_name, std::string* result) const;
// Extracts the optional string member |member_name| and saves the result to
// |*result| if it was found. If the member exists and is not a string,
// returns an error. Otherwise returns success, and sets |*member_exists| if
// it was found.
Status GetOptionalString(const std::string& member_name,
std::string* result,
bool* member_exists) const;
// Extracts the optional array member |member_name| and saves the result to
// |*result| if it was found. If the member exists and is not an array,
// returns an error. Otherwise returns success, and sets |*member_exists| if
// it was found.
//
// NOTE: |*result| is owned by the JwkReader.
Status GetOptionalList(const std::string& member_name,
base::ListValue** result,
bool* member_exists) const;
// Extracts the required string member |member_name| and saves the
// base64url-decoded bytes to |*result|. If the member does not exist or is
// not a string, or could not be base64url-decoded, returns an error.
Status GetBytes(const std::string& member_name, std::string* result) const;
// Extracts the required base64url member, which is interpreted as being a
// big-endian unsigned integer.
//
// Sequences that contain leading zeros will be rejected.
Status GetBigInteger(const std::string& member_name,
std::string* result) const;
// Extracts the optional boolean member |member_name| and saves the result to
// |*result| if it was found. If the member exists and is not a boolean,
// returns an error. Otherwise returns success, and sets |*member_exists| if
// it was found.
Status GetOptionalBool(const std::string& member_name,
bool* result,
bool* member_exists) const;
// Gets the optional algorithm ("alg") string.
Status GetAlg(std::string* alg, bool* has_alg) const;
// Checks if the "alg" member matches |expected_alg|.
Status VerifyAlg(const std::string& expected_alg) const;
private:
scoped_ptr<base::DictionaryValue> dict_;
};
// Helper class for building the JSON for a JWK.
class JwkWriter {
public:
// Initializes a writer, and sets the standard JWK members as indicated.
// |algorithm| is optional, and is only written if the provided |algorithm| is
// non-empty.
JwkWriter(const std::string& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
const std::string& kty);
// Sets a string member |member_name| to |value|.
void SetString(const std::string& member_name, const std::string& value);
// Sets a bytes member |value| to |value| by base64 url-safe encoding it.
void SetBytes(const std::string& member_name, const CryptoData& value);
// Flattens the JWK to JSON (UTF-8 encoded if necessary, however in practice
// it will be ASCII).
void ToJson(std::vector<uint8_t>* utf8_bytes) const;
private:
base::DictionaryValue dict_;
};
// Writes a JWK-formatted symmetric key to |jwk_key_data|.
// * raw_key_data: The actual key data
// * algorithm: The JWK algorithm name (i.e. "alg")
// * extractable: The JWK extractability (i.e. "ext")
// * usages: The JWK usages (i.e. "key_ops")
void WriteSecretKeyJwk(const CryptoData& raw_key_data,
const std::string& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
std::vector<uint8_t>* jwk_key_data);
// Parses a UTF-8 encoded JWK (key_data), and extracts the key material to
// |*raw_key_data|. Returns Status::Success() on success, otherwise an error.
// In order for this to succeed:
// * expected_alg must match the JWK's "alg", if present.
// * expected_extractable must be consistent with the JWK's "ext", if
// present.
// * expected_usages must be a subset of the JWK's "key_ops" if present.
Status ReadSecretKeyJwk(const CryptoData& key_data,
const std::string& expected_alg,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
std::vector<uint8_t>* raw_key_data);
// Creates an AES algorithm name for the given key size (in bytes). For
// instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16.
std::string MakeJwkAesAlgorithmName(const std::string& suffix,
size_t keylen_bytes);
// This is very similar to ReadSecretKeyJwk(), except instead of specifying an
// absolute "expected_alg", the suffix for an AES algorithm name is given
// (See MakeJwkAesAlgorithmName() for an explanation of what the suffix is).
//
// This is because the algorithm name for AES keys is dependent on the length
// of the key. This function expects key lengths to be either 128, 192, or 256
// bits.
Status ReadAesSecretKeyJwk(const CryptoData& key_data,
const std::string& algorithm_name_suffix,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
std::vector<uint8_t>* raw_key_data);
// Writes a JWK-formated RSA public key and saves the result to
// |*jwk_key_data|.
void WriteRsaPublicKeyJwk(const CryptoData& n,
const CryptoData& e,
const std::string& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
std::vector<uint8_t>* jwk_key_data);
// Writes a JWK-formated RSA private key and saves the result to
// |*jwk_key_data|.
void WriteRsaPrivateKeyJwk(const CryptoData& n,
const CryptoData& e,
const CryptoData& d,
const CryptoData& p,
const CryptoData& q,
const CryptoData& dp,
const CryptoData& dq,
const CryptoData& qi,
const std::string& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usages,
std::vector<uint8_t>* jwk_key_data);
// Describes the RSA components for a parsed key. The names of the properties
// correspond with those from the JWK spec. Note that Chromium's WebCrypto
// implementation does not support multi-primes, so there is no parsed field
// for othinfo.
struct JwkRsaInfo {
JwkRsaInfo();
~JwkRsaInfo();
bool is_private_key;
std::string n;
std::string e;
std::string d;
std::string p;
std::string q;
std::string dp;
std::string dq;
std::string qi;
};
// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to
// |*result|. Returns Status::Success() on success, otherwise an error.
// In order for this to succeed:
// * expected_alg must match the JWK's "alg", if present.
// * expected_extractable must be consistent with the JWK's "ext", if
// present.
// * expected_usages must be a subset of the JWK's "key_ops" if present.
Status ReadRsaKeyJwk(const CryptoData& key_data,
const std::string& expected_alg,
bool expected_extractable,
blink::WebCryptoKeyUsageMask expected_usages,
JwkRsaInfo* result);
const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash);
// This decodes JWK's flavor of base64 encoding, as described by:
// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-36#section-2
//
// In essence it is RFC 4648 'base64url' encoding where padding is omitted.
bool Base64DecodeUrlSafe(const std::string& input, std::string* output);
// Encodes |input| using JWK's flavor of base64 encoding. See the description
// above for details.
std::string Base64EncodeUrlSafe(const base::StringPiece& input);
std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input);
// Converts a JWK "key_ops" array to the corresponding WebCrypto usages. Used by
// testing.
Status GetWebCryptoUsagesFromJwkKeyOpsForTest(
const base::ListValue* key_ops,
blink::WebCryptoKeyUsageMask* usages);
} // namespace webcrypto
#endif // COMPONENTS_WEBCRYPTO_JWK_H_
|