OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" | 5 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <sstream> |
8 | 9 |
9 #include "base/big_endian.h" | 10 #include "base/big_endian.h" |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "crypto/hkdf.h" | 12 #include "crypto/hkdf.h" |
12 | 13 |
13 namespace gcm { | 14 namespace gcm { |
14 namespace { | 15 namespace { |
15 | 16 |
16 // Size, in bytes, of the nonce for a record. This must be at least the size | 17 // Size, in bytes, of the nonce for a record. This must be at least the size |
17 // of a uint64_t, which is used to indicate the record sequence number. | 18 // of a uint64_t, which is used to indicate the record sequence number. |
18 const uint64_t kNonceSize = 12; | 19 const uint64_t kNonceSize = 12; |
19 | 20 |
20 // The default record size as defined by draft-thomson-http-encryption-01. | 21 // The default record size as defined by draft-thomson-http-encryption-01. |
21 const size_t kDefaultRecordSize = 4096; | 22 const size_t kDefaultRecordSize = 4096; |
22 | 23 |
23 // Key size, in bytes, of a valid AEAD_AES_128_GCM key. | 24 // Key size, in bytes, of a valid AEAD_AES_128_GCM key. |
24 const size_t kContentEncryptionKeySize = 16; | 25 const size_t kContentEncryptionKeySize = 16; |
25 | 26 |
| 27 // The label of the curve used by Web Push encryption, as mandated by the |
| 28 // draft-ietf-webpush-encryption-01 draft. |
| 29 const char kP256Label[] = "P-256"; |
| 30 |
26 } // namespace | 31 } // namespace |
27 | 32 |
28 const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16; | 33 const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16; |
29 const size_t GCMMessageCryptographer::kSaltSize = 16; | 34 const size_t GCMMessageCryptographer::kSaltSize = 16; |
30 | 35 |
31 GCMMessageCryptographer::GCMMessageCryptographer() {} | 36 template <typename I> std::string n2hexstr(I w, size_t hex_len = sizeof(I)<<1) { |
| 37 static const char* digits = "0123456789abcdef"; |
| 38 std::string rc(hex_len,'0'); |
| 39 for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4) |
| 40 rc[i] = digits[(w>>j) & 0x0f]; |
| 41 return rc; |
| 42 } |
| 43 |
| 44 GCMMessageCryptographer::GCMMessageCryptographer( |
| 45 const base::StringPiece& local_public_key, |
| 46 const base::StringPiece& peer_public_key) { |
| 47 std::stringstream context_stream; |
| 48 context_stream << '\0' << kP256Label << '\0'; |
| 49 context_stream << '\0' << (char)local_public_key.size() << local_public_key; |
| 50 context_stream << '\0' << (char)peer_public_key.size() << peer_public_key; |
| 51 |
| 52 // TODO: \0 + size is necessary because the length must be written as a two- |
| 53 // byte integer in big endian byte order. |
| 54 |
| 55 // TODO: Without (char) the size is written as two ascii literals: '6' '5', |
| 56 // rather than the binary representation of 0x41. |
| 57 |
| 58 nonce_info_ = |
| 59 "Content-Encoding: nonce" + context_stream.str(); |
| 60 content_encryption_key_info_ = |
| 61 "Content-Encoding: aesgcm128" + context_stream.str(); |
| 62 } |
32 | 63 |
33 GCMMessageCryptographer::~GCMMessageCryptographer() {} | 64 GCMMessageCryptographer::~GCMMessageCryptographer() {} |
34 | 65 |
35 bool GCMMessageCryptographer::Encrypt(const base::StringPiece& plaintext, | 66 bool GCMMessageCryptographer::Encrypt(const base::StringPiece& plaintext, |
36 const base::StringPiece& key, | 67 const base::StringPiece& key, |
37 const base::StringPiece& salt, | 68 const base::StringPiece& salt, |
38 size_t* record_size, | 69 size_t* record_size, |
39 std::string* ciphertext) const { | 70 std::string* ciphertext) const { |
40 DCHECK(ciphertext); | 71 DCHECK(ciphertext); |
41 DCHECK(record_size); | 72 DCHECK(record_size); |
(...skipping 28 matching lines...) Expand all Loading... |
70 } | 101 } |
71 | 102 |
72 bool GCMMessageCryptographer::Decrypt( | 103 bool GCMMessageCryptographer::Decrypt( |
73 const base::StringPiece& ciphertext, | 104 const base::StringPiece& ciphertext, |
74 const base::StringPiece& key, | 105 const base::StringPiece& key, |
75 const base::StringPiece& salt, | 106 const base::StringPiece& salt, |
76 size_t record_size, | 107 size_t record_size, |
77 std::string* plaintext) const { | 108 std::string* plaintext) const { |
78 DCHECK(plaintext); | 109 DCHECK(plaintext); |
79 | 110 |
80 if (salt.size() != kSaltSize || record_size <= 1) | 111 if (salt.size() != kSaltSize || record_size <= 1) { |
| 112 LOG(INFO) << "--1"; |
81 return false; | 113 return false; |
| 114 } |
82 | 115 |
83 // The |ciphertext| must be at least kAuthenticationTagBytes + 1 bytes, which | 116 // The |ciphertext| must be at least kAuthenticationTagBytes + 1 bytes, which |
84 // would be used for an empty message. Per | 117 // would be used for an empty message. Per |
85 // https://tools.ietf.org/html/draft-thomson-webpush-encryption-01#section-3, | 118 // https://tools.ietf.org/html/draft-thomson-webpush-encryption-01#section-3, |
86 // the |record_size| parameter must be large enough to use only one record. | 119 // the |record_size| parameter must be large enough to use only one record. |
87 if (ciphertext.size() < kAuthenticationTagBytes + 1 || | 120 if (ciphertext.size() < kAuthenticationTagBytes + 1 || |
88 ciphertext.size() >= record_size + kAuthenticationTagBytes + 1) { | 121 ciphertext.size() >= record_size + kAuthenticationTagBytes + 1) { |
| 122 LOG(INFO) << "--2"; |
89 return false; | 123 return false; |
90 } | 124 } |
91 | 125 |
92 std::string content_encryption_key = DeriveContentEncryptionKey(key, salt); | 126 std::string content_encryption_key = DeriveContentEncryptionKey(key, salt); |
93 std::string nonce = DeriveNonce(key, salt); | 127 std::string nonce = DeriveNonce(key, salt); |
94 | 128 |
95 std::string decrypted_record; | 129 std::string decrypted_record; |
96 if (!EncryptDecryptRecordInternal(DECRYPT, ciphertext, content_encryption_key, | 130 if (!EncryptDecryptRecordInternal(DECRYPT, ciphertext, content_encryption_key, |
97 nonce, &decrypted_record)) { | 131 nonce, &decrypted_record)) { |
| 132 LOG(INFO) << "--3"; |
98 return false; | 133 return false; |
99 } | 134 } |
100 | 135 |
101 DCHECK(!decrypted_record.empty()); | 136 DCHECK(!decrypted_record.empty()); |
102 | 137 |
103 // Records can contain between 0 and 255 octets of padding, indicated by the | 138 // Records can contain between 0 and 255 octets of padding, indicated by the |
104 // first octet of the decrypted message. Padding bytes that are not set to | 139 // first octet of the decrypted message. Padding bytes that are not set to |
105 // zero are considered a fatal decryption failure as well. Since AES-GCM | 140 // zero are considered a fatal decryption failure as well. Since AES-GCM |
106 // includes an authentication check, neither verification nor removing the | 141 // includes an authentication check, neither verification nor removing the |
107 // padding have to be done in constant time. | 142 // padding have to be done in constant time. |
108 size_t padding_length = static_cast<size_t>(decrypted_record[0]); | 143 size_t padding_length = static_cast<size_t>(decrypted_record[0]); |
109 if (padding_length >= decrypted_record.size()) | 144 if (padding_length >= decrypted_record.size()) { |
| 145 LOG(INFO) << "--4"; |
110 return false; | 146 return false; |
| 147 } |
111 | 148 |
112 for (size_t i = 1; i <= padding_length; ++i) { | 149 for (size_t i = 1; i <= padding_length; ++i) { |
113 if (decrypted_record[i] != 0) | 150 if (decrypted_record[i] != 0) { |
| 151 LOG(INFO) << "--5"; |
114 return false; | 152 return false; |
| 153 } |
115 } | 154 } |
116 | 155 |
117 base::StringPiece decoded_record_string_piece(decrypted_record); | 156 base::StringPiece decoded_record_string_piece(decrypted_record); |
118 decoded_record_string_piece.remove_prefix(1 + padding_length); | 157 decoded_record_string_piece.remove_prefix(1 + padding_length); |
119 decoded_record_string_piece.CopyToString(plaintext); | 158 decoded_record_string_piece.CopyToString(plaintext); |
120 | 159 |
121 return true; | 160 return true; |
122 } | 161 } |
123 | 162 |
124 std::string GCMMessageCryptographer::DeriveContentEncryptionKey( | 163 std::string GCMMessageCryptographer::DeriveContentEncryptionKey( |
125 const base::StringPiece& key, | 164 const base::StringPiece& key, |
126 const base::StringPiece& salt) const { | 165 const base::StringPiece& salt) const { |
127 crypto::HKDF hkdf(key, salt, | 166 crypto::HKDF hkdf(key, salt, |
128 "Content-Encoding: aesgcm128", | 167 content_encryption_key_info_, |
129 kContentEncryptionKeySize, | 168 kContentEncryptionKeySize, |
130 0, /* iv_bytes_to_generate */ | 169 0, /* iv_bytes_to_generate */ |
131 0 /* subkey_secret_bytes_to_generate */); | 170 0 /* subkey_secret_bytes_to_generate */); |
132 | 171 |
133 return hkdf.client_write_key().as_string(); | 172 return hkdf.client_write_key().as_string(); |
134 } | 173 } |
135 | 174 |
136 std::string GCMMessageCryptographer::DeriveNonce( | 175 std::string GCMMessageCryptographer::DeriveNonce( |
137 const base::StringPiece& key, | 176 const base::StringPiece& key, |
138 const base::StringPiece& salt) const { | 177 const base::StringPiece& salt) const { |
139 crypto::HKDF hkdf(key, salt, | 178 crypto::HKDF hkdf(key, salt, |
140 "Content-Encoding: nonce", | 179 nonce_info_, |
141 kNonceSize, | 180 kNonceSize, |
142 0, /* iv_bytes_to_generate */ | 181 0, /* iv_bytes_to_generate */ |
143 0 /* subkey_secret_bytes_to_generate */); | 182 0 /* subkey_secret_bytes_to_generate */); |
144 | 183 |
145 // draft-thomson-http-encryption-01 defines that the result should be XOR'ed | 184 // draft-thomson-http-encryption-01 defines that the result should be XOR'ed |
146 // with the record's sequence number, but because Web Push encryption is | 185 // with the record's sequence number, but because Web Push encryption is |
147 // limited to a single record we do not have to do that. | 186 // limited to a single record we do not have to do that. |
148 | 187 |
149 return hkdf.client_write_key().as_string(); | 188 return hkdf.client_write_key().as_string(); |
150 } | 189 } |
151 | 190 |
152 } // namespace gcm | 191 } // namespace gcm |
OLD | NEW |