Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / gcm_driver / crypto / gcm_message_cryptographer_nss.cc
blobbfe85dfc95c25c2b0d39428b6055ceec1e6801b2
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
3 // found in the LICENSE file.
5 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
7 #include <pk11pub.h>
8 #include <secerr.h>
10 #include "base/logging.h"
11 #include "base/numerics/safe_math.h"
12 #include "base/strings/string_util.h"
13 #include "crypto/aes_128_gcm_helpers_nss.h"
14 #include "crypto/scoped_nss_types.h"
16 namespace gcm {
18 bool GCMMessageCryptographer::EncryptDecryptRecordInternal(
19 Mode mode,
20 const base::StringPiece& input,
21 const base::StringPiece& key,
22 const base::StringPiece& nonce,
23 std::string* output) const {
24 DCHECK(output);
26 SECItem key_item;
27 key_item.type = siBuffer;
28 key_item.data = const_cast<unsigned char*>(
29 reinterpret_cast<const unsigned char*>(key.data()));
30 key_item.len = key.size();
32 const CK_ATTRIBUTE_TYPE cka_mode = mode == ENCRYPT ? CKA_ENCRYPT
33 : CKA_DECRYPT;
35 // TODO(peter): For AES-GCM we should be using CKM_AES_GCM as the mechanism,
36 // but because of an NSS bug we need to use CKM_AES_ECB as a work-around until
37 // we require NSS 3.15+. https://bugzilla.mozilla.org/show_bug.cgi?id=853285
38 // This does not affect the call to PK11{Decrypt,Encrypt}Helper below.
39 const CK_MECHANISM_TYPE key_mechanism = CKM_AES_ECB;
41 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot());
42 crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(slot.get(), key_mechanism,
43 PK11_OriginUnwrap, cka_mode, &key_item, nullptr));
45 CK_GCM_PARAMS gcm_params;
46 gcm_params.pIv = const_cast<unsigned char*>(
47 reinterpret_cast<const unsigned char*>(nonce.data()));
48 gcm_params.ulIvLen = nonce.size();
50 gcm_params.pAAD = nullptr;
51 gcm_params.ulAADLen = 0;
53 gcm_params.ulTagBits = kAuthenticationTagBytes * 8;
55 SECItem param;
56 param.type = siBuffer;
57 param.data = reinterpret_cast<unsigned char*>(&gcm_params);
58 param.len = sizeof(gcm_params);
60 base::CheckedNumeric<size_t> maximum_output_length(input.size());
61 if (mode == ENCRYPT)
62 maximum_output_length += kAuthenticationTagBytes;
64 // WriteInto requires the buffer to finish with a NULL-byte.
65 maximum_output_length += 1;
67 unsigned int output_length = 0;
68 unsigned char* raw_input = const_cast<unsigned char*>(
69 reinterpret_cast<const unsigned char*>(input.data()));
70 unsigned char* raw_output = reinterpret_cast<unsigned char*>(
71 base::WriteInto(output, maximum_output_length.ValueOrDie()));
73 if (mode == ENCRYPT) {
74 if (crypto::PK11EncryptHelper(aead_key.get(), CKM_AES_GCM, &param,
75 raw_output, &output_length, output->size(),
76 raw_input, input.size())
77 != SECSuccess) {
78 return false;
80 } else {
81 if (crypto::PK11DecryptHelper(aead_key.get(), CKM_AES_GCM, &param,
82 raw_output, &output_length, output->size(),
83 raw_input, input.size())
84 != SECSuccess) {
85 return false;
89 base::CheckedNumeric<size_t> expected_output_length(input.size());
90 if (mode == ENCRYPT)
91 expected_output_length += kAuthenticationTagBytes;
92 else
93 expected_output_length -= kAuthenticationTagBytes;
95 DCHECK_EQ(expected_output_length.ValueOrDie(), output_length);
97 output->resize(output_length);
98 return true;
101 } // namespace gcm