Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(396)

Side by Side Diff: crypto/openpgp_symmetric_encryption_openssl.cc

Issue 7247005: crypto: add OpenPGP symmetric encryption for ChromeOS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "crypto/openpgp_symmetric_encryption.h"
6
7 #include <vector>
8 #include <stdlib.h>
9
10 #include <openssl/evp.h>
11 #include <openssl/aes.h>
12 #include <openssl/sha.h>
13
14 #include "base/rand_util.h"
15 #include "base/logging.h"
16
17 namespace crypto {
18
19 namespace {
20
21 // Reader wraps a StringPiece and provides methods to read several datatypes
22 // while advancing the StringPiece.
23 class Reader {
24 public:
25 Reader(base::StringPiece input)
26 : s_(input) {
27 }
28
29 bool U8(uint8* out) {
30 if (s_.size() < 1)
31 return false;
32 *out = static_cast<uint8>(s_[0]);
33 s_.remove_prefix(1);
34 return true;
35 }
36
37 bool U32(unsigned* out) {
38 if (s_.size() < 4)
39 return false;
40 *out = static_cast<unsigned>(s_[0]) << 24 |
41 static_cast<unsigned>(s_[1]) << 16 |
42 static_cast<unsigned>(s_[2]) << 8 |
43 static_cast<unsigned>(s_[3]);
44 s_.remove_prefix(4);
45 return true;
46 }
47
48 // Prefix sets |*out| to the first |n| bytes of the StringPiece and advances
49 // the StringPiece by |n|.
50 bool Prefix(unsigned n, base::StringPiece *out) {
51 if (s_.size() < n)
52 return false;
53 *out = base::StringPiece(s_.data(), n);
54 s_.remove_prefix(n);
55 return true;
56 }
57
58 // Remainder returns the remainer of the StringPiece and advances it to the
59 // end.
60 base::StringPiece Remainder() {
61 base::StringPiece ret = s_;
62 s_ = base::StringPiece();
63 return ret;
64 }
65
66 typedef base::StringPiece Position;
67
68 Position tell() const {
69 return s_;
70 }
71
72 void Seek(Position p) {
73 s_ = p;
74 }
75
76 bool Skip(unsigned n) {
77 if (s_.size() < n)
78 return false;
79 s_.remove_prefix(n);
80 return true;
81 }
82
83 bool empty() const {
84 return s_.empty();
85 }
86
87 size_t size() const {
88 return s_.size();
89 }
90
91 private:
92 base::StringPiece s_;
Greg Spencer (Chromium) 2011/06/23 20:55:00 Please name this something better: "string_" or "d
agl 2011/06/27 20:42:53 Done.
93 };
94
95 // SaltedIteratedS2K implements the salted and iterated string-to-key
96 // convertion. See RFC 4880, section 3.7.1.3.
97 void SaltedIteratedS2K(unsigned cipher_key_length,
98 const EVP_MD *md,
Greg Spencer (Chromium) 2011/06/23 20:55:00 Please change "md" to "digest" or "message_digest"
agl 2011/06/27 20:42:53 Done.
99 base::StringPiece passphrase,
100 base::StringPiece salt,
101 unsigned count,
102 uint8 *out_key) {
103 const std::string combined = salt.as_string() + passphrase.as_string();
104 const size_t combined_len = combined.size();
105
106 unsigned done = 0;
107 uint8 zero[1] = {0};
108
109 EVP_MD_CTX ctx;
Greg Spencer (Chromium) 2011/06/23 20:55:00 It'd be nice to call this "context" as well. I kn
agl 2011/06/27 20:42:53 Done.
110 EVP_MD_CTX_init(&ctx);
111
112 for (unsigned i = 0; done < cipher_key_length; i++) {
113 CHECK_EQ(EVP_DigestInit_ex(&ctx, md, NULL), 1);
114
115 for (unsigned j = 0; j < i; j++)
116 EVP_DigestUpdate(&ctx, zero, sizeof(zero));
117
118 unsigned written = 0;
Greg Spencer (Chromium) 2011/06/23 20:55:00 Please use uint32_t here (or uint32) and elsewhere
agl 2011/06/27 20:42:53 Although I don't mind changing unsigned for uint32
Greg Spencer (Chromium) 2011/06/27 21:00:07 Well, while I'm not entirely in this camp myself,
119 while (written < count) {
120 if (written + combined_len > count) {
121 unsigned todo = count - written;
122 EVP_DigestUpdate(&ctx, combined.data(), todo);
123 written = count;
124 } else {
125 EVP_DigestUpdate(&ctx, combined.data(), combined_len);
126 written += combined_len;
127 }
128 }
129
130 unsigned num_hash_bytes;
131 uint8 hash[EVP_MAX_MD_SIZE];
132 CHECK_EQ(EVP_DigestFinal_ex(&ctx, hash, &num_hash_bytes), 1);
133
134 unsigned todo = cipher_key_length - done;
135 if (todo > num_hash_bytes)
136 todo = num_hash_bytes;
137 memcpy(out_key + done, hash, todo);
138 done += todo;
139 }
140
141 EVP_MD_CTX_cleanup(&ctx);
142 }
143
144 // These constants are the tag numbers for the various packet types that we
145 // use.
146 static const unsigned kSymmetricKeyEncryptedTag = 3;
147 static const unsigned kSymmetricallyEncryptedTag = 18;
148 static const unsigned kCompressedTag = 8;
149 static const unsigned kLiteralDataTag = 11;
150
151 class Decrypter {
152 public:
153 ~Decrypter() {
154 for (std::vector<void*>::iterator
155 i = arena_.begin(); i != arena_.end(); i++) {
156 free(*i);
157 }
158 arena_.clear();
159 }
160
161 OpenPGPSymmetricEncrytion::Result Decrypt(base::StringPiece in,
Greg Spencer (Chromium) 2011/06/23 20:55:00 This should be a const reference: const base::Stri
agl 2011/06/27 20:42:53 That's a misguided optimization in non-performance
Greg Spencer (Chromium) 2011/06/27 21:00:07 It's not meant to just be an optimization. It is
162 base::StringPiece passphrase,
163 base::StringPiece *out_contents) {
164 Reader r(in);
Greg Spencer (Chromium) 2011/06/23 20:55:00 Please call this "reader" or something more descri
agl 2011/06/27 20:42:53 Done.
165 unsigned tag;
166 base::StringPiece contents;
167 AES_KEY key;
168
169 if (!ParsePacket(&r, &tag, &contents))
170 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
171 if (tag != kSymmetricKeyEncryptedTag)
172 return OpenPGPSymmetricEncrytion::NOT_SYMMETRICALLY_ENCRYPTED;
173 Reader inner(contents);
174 OpenPGPSymmetricEncrytion::Result result =
175 ParseSymmetricKeyEncrypted(&inner, passphrase, &key);
176 if (result != OpenPGPSymmetricEncrytion::OK)
177 return result;
178
179 if (!ParsePacket(&r, &tag, &contents))
180 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
181 if (tag != kSymmetricallyEncryptedTag)
182 return OpenPGPSymmetricEncrytion::NOT_SYMMETRICALLY_ENCRYPTED;
183 if (!r.empty())
184 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
185 inner = Reader(contents);
186 if (!ParseSymmetricallyEncrypted(&inner, &key, &contents))
187 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
188
189 r = Reader(contents);
190 if (!ParsePacket(&r, &tag, &contents))
191 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
192 if (tag == kCompressedTag)
193 return OpenPGPSymmetricEncrytion::COMPRESSED;
194 if (tag != kLiteralDataTag)
195 return OpenPGPSymmetricEncrytion::NOT_SYMMETRICALLY_ENCRYPTED;
196 inner = Reader(contents);
197 if (!ParseLiteralData(&inner, out_contents))
198 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
199
200 return OpenPGPSymmetricEncrytion::OK;
201 }
202
203 private:
204 // ParsePacket parses an OpenPGP packet from r. See RFC 4880, section 4.2.2.
205 bool ParsePacket(Reader *r,
206 unsigned *out_tag,
207 base::StringPiece *out_contents) {
208 uint8 b;
Greg Spencer (Chromium) 2011/06/23 20:55:00 Please use a better name here.
agl 2011/06/27 20:42:53 Done.
209 if (!r->U8(&b))
210 return false;
211 if ((b & 0x80) == 0) {
212 // Tag byte must have MSB set.
213 return false;
214 }
215
216 if ((b & 0x40) == 0) {
217 // Old format packet.
218 *out_tag = (b & 0x3f) >> 2;
219
220 uint8 length_type = b & 3;
221 if (length_type == 3) {
222 *out_contents = r->Remainder();
223 return true;
224 }
225
226 const unsigned length_bytes = 1 << length_type;
227 unsigned length = 0;
228 for (unsigned i = 0; i < length_bytes; i++) {
229 uint8 length_byte;
230 if (!r->U8(&length_byte))
231 return false;
232 length <<= 8;
233 length |= length_byte;
234 }
235
236 return r->Prefix(length, out_contents);
237 }
238
239 // New format packet.
240 *out_tag = b & 0x3f;
241 unsigned length;
242 bool is_partial;
243 if (!ParseLength(r, &length, &is_partial))
244 return false;
245 if (is_partial)
246 return ParseStreamContents(r, length, out_contents);
247 return r->Prefix(length, out_contents);
248 }
249
250 // ParseStreamContents parses all the chunks of a partial length stream from
251 // r. See RFC 4880, section 4.2.2.4.
Greg Spencer (Chromium) 2011/06/23 20:55:00 Is there a url you could provide here to this spec
agl 2011/06/27 20:42:53 Done.
252 bool ParseStreamContents(Reader *r,
253 unsigned length,
254 base::StringPiece *out_contents) {
255 const Reader::Position beginning_of_stream = r->tell();
256 const unsigned first_chunk_length = length;
257
258 // First we parse the stream to find its length.
259 if (!r->Skip(length))
260 return false;
261
262 for (;;) {
Greg Spencer (Chromium) 2011/06/23 20:55:00 nit: I think "while (true)" is more readable, but
263 unsigned chunk_length;
264 bool is_partial;
265
266 if (!ParseLength(r, &chunk_length, &is_partial))
267 return false;
268 if (length + chunk_length < length)
269 return false;
270 length += chunk_length;
271 if (!r->Skip(chunk_length))
272 return false;
273 if (!is_partial)
274 break;
275 }
276
277 // Now we have the length of the whole stream in |length|.
278 char* buf = reinterpret_cast<char*>(malloc(length));
279 arena_.push_back(buf);
280 unsigned j = 0;
281 r->Seek(beginning_of_stream);
282
283 base::StringPiece first_chunk;
284 if (!r->Prefix(first_chunk_length, &first_chunk))
285 return false;
286 memcpy(buf + j, first_chunk.data(), first_chunk_length);
287 j += first_chunk_length;
288
289 // Now we parse the stream again, this time copying into |buf|
290 for (;;) {
291 unsigned chunk_length;
292 bool is_partial;
293
294 if (!ParseLength(r, &chunk_length, &is_partial))
295 return false;
296 base::StringPiece chunk;
297 if (!r->Prefix(chunk_length, &chunk))
298 return false;
299 memcpy(buf + j, chunk.data(), chunk_length);
300 j += chunk_length;
301 if (!is_partial)
302 break;
303 }
304
305 *out_contents = base::StringPiece(buf, length);
306 return true;
307 }
308
309 // ParseLength parses an OpenPGP length from r. See RFC 4880, section 4.2.2.
310 bool ParseLength(Reader *r, unsigned *out_length, bool *out_is_prefix) {
311 uint8 length_spec;
312 if (!r->U8(&length_spec))
313 return false;
314
315 *out_is_prefix = false;
316 if (length_spec < 192) {
Greg Spencer (Chromium) 2011/06/23 20:55:00 It might be good to document these seemingly-arbit
317 *out_length = length_spec;
318 return true;
319 } else if (length_spec < 224) {
320 uint8 next_byte;
321 if (!r->U8(&next_byte))
322 return false;
323
324 *out_length = (length_spec - 192) << 8;
325 *out_length += next_byte;
326 return true;
327 } else if (length_spec < 255) {
328 *out_length = 1u << (length_spec & 0x1f);
329 *out_is_prefix = true;
330 return true;
331 } else {
332 return r->U32(out_length);
333 }
334 }
335
336 // ParseSymmetricKeyEncrypted parses a passphrase protected session key. See
337 // RFC 4880, section 5.3.
338 OpenPGPSymmetricEncrytion::Result ParseSymmetricKeyEncrypted(
339 Reader *r,
340 base::StringPiece passphrase,
341 AES_KEY *out_key) {
342 uint8 version, cipher, s2k_type, hash_func;
343 if (!r->U8(&version) || version != 4)
344 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
345
346 if (!r->U8(&cipher) ||
347 !r->U8(&s2k_type) ||
348 !r->U8(&hash_func)) {
349 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
350 }
351
352 uint8 cipher_key_length = OpenPGPCipherIdToKeyLength(cipher);
353 if (cipher_key_length == 0)
354 return OpenPGPSymmetricEncrytion::UNKNOWN_CIPHER;
355
356 const EVP_MD *md;
357 switch (hash_func) {
358 case 2: // SHA-1
359 md = EVP_sha1();
360 break;
361 case 8: // SHA-256
362 md = EVP_sha256();
363 break;
364 default:
365 return OpenPGPSymmetricEncrytion::UNKNOWN_HASH;
366 }
367
368 base::StringPiece salt;
369 uint8 key[32], c;
Greg Spencer (Chromium) 2011/06/23 20:55:00 Don't declare multiple variables on one line. Sep
agl 2011/06/27 20:42:53 Done.
370 switch (s2k_type) {
371 case 1:
372 if (!r->Prefix(8, &salt))
373 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
374 case 0:
375 SaltedIteratedS2K(cipher_key_length, md, passphrase, salt,
376 passphrase.size() + salt.size(), key);
377 break;
378 case 3:
379 if (!r->Prefix(8, &salt) ||
380 !r->U8(&c)) {
381 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
382 }
383 SaltedIteratedS2K(
384 cipher_key_length, md, passphrase, salt,
385 static_cast<unsigned>(16 + (c&15)) << ((c >> 4) + 6), key);
386 break;
387 default:
388 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
389 }
390
391 if (AES_set_encrypt_key(key, 8 * cipher_key_length, out_key))
392 return OpenPGPSymmetricEncrytion::INTERNAL_ERROR;
393
394 if (r->empty()) {
395 // The resulting key is used directly.
396 return OpenPGPSymmetricEncrytion::OK;
397 }
398
399 // The S2K derived key encrypts another key that follows:
400 base::StringPiece encrypted_key = r->Remainder();
401 if (encrypted_key.size() < 1)
402 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
403
404 uint8* plaintext_key = reinterpret_cast<uint8*>(
405 malloc(encrypted_key.size()));
406 arena_.push_back(plaintext_key);
407
408 int num = 0;
409 uint8 iv[16] = {0};
410
411 AES_cfb128_encrypt(reinterpret_cast<const uint8*>(encrypted_key.data()),
412 plaintext_key,
413 encrypted_key.size(),
414 out_key,
415 iv,
416 &num,
417 AES_DECRYPT);
418
419 cipher_key_length = OpenPGPCipherIdToKeyLength(plaintext_key[0]);
420 if (cipher_key_length == 0)
421 return OpenPGPSymmetricEncrytion::UNKNOWN_CIPHER;
422 if (encrypted_key.size() != 1u + cipher_key_length)
423 return OpenPGPSymmetricEncrytion::PARSE_ERROR;
424 if (AES_set_encrypt_key(plaintext_key + 1, 8 * cipher_key_length,
425 out_key)) {
426 return OpenPGPSymmetricEncrytion::INTERNAL_ERROR;
427 }
428 return OpenPGPSymmetricEncrytion::OK;
429 }
430
431 unsigned OpenPGPCipherIdToKeyLength(uint8 cipher) {
432 switch (cipher) {
433 case 7: // AES-128
434 return 16;
435 case 8: // AES-192
436 return 24;
437 case 9: // AES-256
438 return 32;
439 default:
440 return 0;
441 }
442 }
443
444 // ParseSymmetricallyEncrypted parses a Symmetrically Encrypted packet. See
445 // RFC 4880, sections 5.7 and 5.13.
446 bool ParseSymmetricallyEncrypted(Reader *r,
447 AES_KEY *key,
448 base::StringPiece *out_plaintext) {
449 uint8 version;
450 if (!r->U8(&version) || version != 1)
451 return false;
452
453 base::StringPiece prefix_sp;
454 if (!r->Prefix(AES_BLOCK_SIZE + 2, &prefix_sp))
455 return false;
456 uint8 prefix[AES_BLOCK_SIZE + 2];
457 memcpy(prefix, prefix_sp.data(), sizeof(prefix));
458
459 uint8 prefix_copy[AES_BLOCK_SIZE + 2];
460 uint8 fre[AES_BLOCK_SIZE];
Greg Spencer (Chromium) 2011/06/23 20:55:00 Please rename fre to something more descriptive.
agl 2011/06/27 20:42:53 No. It matches the spec.
461
462 memset(prefix_copy, 0, AES_BLOCK_SIZE);
463 AES_ecb_encrypt(prefix_copy, fre, key, AES_ENCRYPT);
464 for (unsigned i = 0; i < AES_BLOCK_SIZE; i++)
465 prefix_copy[i] = fre[i] ^ prefix[i];
466 AES_ecb_encrypt(prefix, fre, key, AES_ENCRYPT);
467 prefix_copy[AES_BLOCK_SIZE] = prefix[AES_BLOCK_SIZE] ^ fre[0];
468 prefix_copy[AES_BLOCK_SIZE + 1] = prefix[AES_BLOCK_SIZE + 1] ^ fre[1];
469
470 if (prefix_copy[AES_BLOCK_SIZE - 2] != prefix_copy[AES_BLOCK_SIZE] ||
471 prefix_copy[AES_BLOCK_SIZE - 1] != prefix_copy[AES_BLOCK_SIZE + 1]) {
472 return false;
473 }
474
475 fre[0] = prefix[AES_BLOCK_SIZE];
476 fre[1] = prefix[AES_BLOCK_SIZE + 1];
477
478 unsigned out_used = 2;
479
480 const unsigned plaintext_size = r->size();
481 if (plaintext_size < 22) {
482 // Too small to contain an MDC trailer.
483 return false;
484 }
485
486 uint8* plaintext = reinterpret_cast<uint8*>(malloc(plaintext_size));
487 arena_.push_back(plaintext);
488
489 for (unsigned i = 0; i < plaintext_size; i++) {
490 uint8 b;
491 if (!r->U8(&b))
492 return false;
493 if (out_used == AES_BLOCK_SIZE) {
494 AES_ecb_encrypt(fre, fre, key, AES_ENCRYPT);
495 out_used = 0;
496 }
497
498 plaintext[i] = b ^ fre[out_used];
499 fre[out_used++] = b;
500 }
501
502 if (plaintext[plaintext_size - SHA_DIGEST_LENGTH - 2] != 0xd3 ||
Greg Spencer (Chromium) 2011/06/23 20:55:00 You might add a comment about why you're looking f
agl 2011/06/27 20:42:53 Done.
503 plaintext[plaintext_size - SHA_DIGEST_LENGTH - 1] != 0x14) {
504 // Not an MDC packet.
Greg Spencer (Chromium) 2011/06/23 20:55:00 What does MDC stand for?
505 return false;
506 }
507
508 SHA_CTX sha1;
509 SHA1_Init(&sha1);
510 SHA1_Update(&sha1, prefix_copy, sizeof(prefix_copy));
511 SHA1_Update(&sha1, plaintext, plaintext_size - SHA_DIGEST_LENGTH);
512 uint8 digest[SHA_DIGEST_LENGTH];
513 SHA1_Final(digest, &sha1);
514
515 if (memcmp(digest, &plaintext[plaintext_size - SHA_DIGEST_LENGTH],
516 SHA_DIGEST_LENGTH) != 0) {
517 return false;
518 }
519
520 *out_plaintext = base::StringPiece(reinterpret_cast<char*>(plaintext),
521 plaintext_size - SHA_DIGEST_LENGTH);
522 return true;
523 }
524
525 // ParseLiteralData parses a Literal Data packet. See RFC 4880, section 5.9.
526 bool ParseLiteralData(Reader *r, base::StringPiece *out_data) {
527 uint8 is_binary, filename_len;
528 if (!r->U8(&is_binary) ||
529 !r->U8(&filename_len) ||
530 !r->Skip(filename_len) ||
531 !r->Skip(sizeof(uint32) /* mtime */)) {
532 return false;
533 }
534
535 *out_data = r->Remainder();
536 return true;
537 }
538
539 // arena_ contains malloced pointers that are used as temporary space during
540 // the decryption.
541 std::vector<void*> arena_;
542 };
543
544 class Encrypter {
545 public:
546 // ByteString is used throughout in order to avoid signedness issues with a
547 // std::string.
548 typedef std::basic_string<uint8> ByteString;
549
550 static ByteString Encrypt(base::StringPiece plaintext,
551 base::StringPiece passphrase) {
552 ByteString key;
553 ByteString ske = SerializeSymmetricKeyEncrypted(passphrase, &key);
Greg Spencer (Chromium) 2011/06/23 20:55:00 Better name needed for "ske".
554
555 ByteString literal_data = SerializeLiteralData(plaintext);
556 ByteString se = SerializeSymmetricallyEncrypted(literal_data, key);
Greg Spencer (Chromium) 2011/06/23 20:55:00 Better name needed for "se"
557 return ske + se;
558 }
559
560 private:
561 static ByteString MakePacket(unsigned tag, const ByteString& contents) {
562 ByteString header;
563 header.push_back(0x80 | 0x40 | tag);
564
565 if (contents.size() < 192) {
566 header.push_back(contents.size());
567 } else if (contents.size() < 8384) {
568 size_t length = contents.size();
569 length -= 192;
570 header.push_back(192 + (length >> 8));
571 header.push_back(length & 0xff);
572 } else {
573 size_t length = contents.size();
574 header.push_back(255);
575 header.push_back(length >> 24);
576 header.push_back(length >> 16);
577 header.push_back(length >> 8);
578 header.push_back(length);
579 }
580
581 return header + contents;
582 }
583
584 static ByteString SerializeLiteralData(base::StringPiece contents) {
585 ByteString literal_data;
586 literal_data.push_back(0x74); // text mode
587 literal_data.push_back(0x00); // no filename
588 literal_data.push_back(0x00); // zero mtime
589 literal_data.push_back(0x00);
590 literal_data.push_back(0x00);
591 literal_data.push_back(0x00);
592 literal_data += ByteString(reinterpret_cast<const uint8*>(contents.data()),
593 contents.size());
594 return MakePacket(kLiteralDataTag, literal_data);
595 }
596
597 static ByteString SerializeSymmetricKeyEncrypted(base::StringPiece passphrase,
598 ByteString *out_key) {
599 ByteString ske;
600 ske.push_back(4); // version 4
601 ske.push_back(7); // AES-128
602 ske.push_back(3); // iterated and salted S2K
603 ske.push_back(2); // SHA-1
604
605 uint64 salt64 = base::RandUint64();
606 ByteString salt(sizeof(salt64), 0);
607
608 // It's a random value, so endianness doesn't matter.
Greg Spencer (Chromium) 2011/06/23 20:55:00 What? You're not going to byte-flip a completely
609 ske += ByteString(reinterpret_cast<uint8*>(&salt64), sizeof(salt64));
610 ske.push_back(96); // iteration count of 65536
611
612 uint8 key[16];
613 SaltedIteratedS2K(
614 sizeof(key), EVP_sha1(), passphrase,
615 base::StringPiece(reinterpret_cast<char*>(&salt64), sizeof(salt64)),
616 65536, key);
617 *out_key = ByteString(key, sizeof(key));
618 return MakePacket(kSymmetricKeyEncryptedTag, ske);
619 }
620
621 static ByteString SerializeSymmetricallyEncrypted(ByteString plaintext,
622 const ByteString& key) {
623 ByteString se;
Greg Spencer (Chromium) 2011/06/23 20:55:00 Better name needed for "se".
agl 2011/06/27 20:42:53 Done.
624 se.push_back(1); // version 1
625 static const unsigned kBlockSize = 16; // AES block size
626
627 uint8 prefix[kBlockSize + 2], fre[kBlockSize], iv[kBlockSize];
628 base::RandBytes(iv, kBlockSize);
629 memset(fre, 0, sizeof(fre));
630
631 AES_KEY aes_key;
632 AES_set_encrypt_key(key.data(), 8 * key.size(), &aes_key);
633
634 AES_ecb_encrypt(fre, fre, &aes_key, AES_ENCRYPT);
635 for (unsigned i = 0; i < 16; i++)
636 prefix[i] = iv[i] ^ fre[i];
637 AES_ecb_encrypt(prefix, fre, &aes_key, AES_ENCRYPT);
638 prefix[kBlockSize] = iv[kBlockSize - 2] ^ fre[0];
639 prefix[kBlockSize + 1] = iv[kBlockSize - 1] ^ fre[1];
640
641 se += ByteString(prefix, sizeof(prefix));
642
643 ByteString plaintext_copy = plaintext;
644 plaintext_copy.push_back(0xd3); // MDC packet
645 plaintext_copy.push_back(20); // packet length (20 bytes)
646
647 SHA_CTX sha1;
648 SHA1_Init(&sha1);
649 SHA1_Update(&sha1, iv, sizeof(iv));
650 SHA1_Update(&sha1, iv + kBlockSize - 2, 2);
651 SHA1_Update(&sha1, plaintext_copy.data(), plaintext_copy.size());
652 uint8 digest[SHA_DIGEST_LENGTH];
653 SHA1_Final(digest, &sha1);
654
655 plaintext_copy += ByteString(digest, sizeof(digest));
656
657 fre[0] = prefix[kBlockSize];
658 fre[1] = prefix[kBlockSize+1];
659 unsigned out_used = 2;
660
661 for (size_t i = 0; i < plaintext_copy.size(); i++) {
662 if (out_used == kBlockSize) {
663 AES_ecb_encrypt(fre, fre, &aes_key, AES_ENCRYPT);
664 out_used = 0;
665 }
666
667 uint8 c = plaintext_copy[i] ^ fre[out_used];
668 fre[out_used++] = c;
669 se.push_back(c);
670 }
671
672 return MakePacket(kSymmetricallyEncryptedTag, se);
673 }
674 };
675
676 } // anonymous namespace
677
678 // static
679 OpenPGPSymmetricEncrytion::Result OpenPGPSymmetricEncrytion::Decrypt(
680 base::StringPiece encrypted,
681 base::StringPiece passphrase,
682 std::string *out) {
683 Decrypter decrypter;
684
685 base::StringPiece result;
686 Result r = decrypter.Decrypt(encrypted, passphrase, &result);
687 if (r == OK)
688 *out = result.as_string();
689 return r;
690 }
691
692 // static
693 std::string OpenPGPSymmetricEncrytion::Encrypt(
694 base::StringPiece plaintext,
695 base::StringPiece passphrase) {
696 Encrypter::ByteString b =
697 Encrypter::Encrypt(plaintext, passphrase);
698 return std::string(reinterpret_cast<const char*>(b.data()), b.size());
699 }
700
701 } // namespace crypto
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698