diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-24 20:46:06 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-24 20:46:06 +0000 |
commit | 2377cdee7b2d027acf4dea98d747637b94213ff1 (patch) | |
tree | f423ef27f7bb0f7e20475746fce8061c4ab1bb25 /crypto/encryptor_nss.cc | |
parent | 93fdd2501f8714dbf1b120a4a276027e7c633d74 (diff) | |
download | chromium_src-2377cdee7b2d027acf4dea98d747637b94213ff1.zip chromium_src-2377cdee7b2d027acf4dea98d747637b94213ff1.tar.gz chromium_src-2377cdee7b2d027acf4dea98d747637b94213ff1.tar.bz2 |
Implement AES-CTR for NSS.
Implement AES-128-CTR.
BUG=87152
TEST=None
Review URL: http://codereview.chromium.org/7056026
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90425 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'crypto/encryptor_nss.cc')
-rw-r--r-- | crypto/encryptor_nss.cc | 167 |
1 files changed, 119 insertions, 48 deletions
diff --git a/crypto/encryptor_nss.cc b/crypto/encryptor_nss.cc index aaa6626..c53fc10 100644 --- a/crypto/encryptor_nss.cc +++ b/crypto/encryptor_nss.cc @@ -13,6 +13,25 @@ namespace crypto { +namespace { + +inline CK_MECHANISM_TYPE GetMechanism(Encryptor::Mode mode) { + switch (mode) { + case Encryptor::CBC: + return CKM_AES_CBC_PAD; + case Encryptor::CTR: + // AES-CTR encryption uses ECB encryptor as a building block since + // NSS doesn't support CTR encryption mode. + return CKM_AES_ECB; + default: + NOTREACHED() << "Unsupported mode of operation"; + break; + } + return static_cast<CK_MECHANISM_TYPE>(-1); +} + +} // namespace + Encryptor::Encryptor() : key_(NULL), mode_(CBC) { @@ -24,101 +43,153 @@ Encryptor::~Encryptor() { bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { DCHECK(key); - DCHECK_EQ(CBC, mode); + DCHECK(CBC == mode || CTR == mode) << "Unsupported mode of operation"; key_ = key; mode_ = mode; - if (iv.size() != AES_BLOCK_SIZE) + if (mode == CBC && iv.size() != AES_BLOCK_SIZE) return false; - slot_.reset(PK11_GetBestSlot(CKM_AES_CBC_PAD, NULL)); + slot_.reset(PK11_GetBestSlot(GetMechanism(mode), NULL)); if (!slot_.get()) return false; - SECItem iv_item; - iv_item.type = siBuffer; - iv_item.data = reinterpret_cast<unsigned char*>( - const_cast<char *>(iv.data())); - iv_item.len = iv.size(); + switch (mode) { + case CBC: + SECItem iv_item; + iv_item.type = siBuffer; + iv_item.data = reinterpret_cast<unsigned char*>( + const_cast<char *>(iv.data())); + iv_item.len = iv.size(); + + param_.reset(PK11_ParamFromIV(GetMechanism(mode), &iv_item)); + break; + case CTR: + param_.reset(PK11_ParamFromIV(GetMechanism(mode), NULL)); + break; + } - param_.reset(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item)); if (!param_.get()) return false; - return true; } bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { - ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, + ScopedPK11Context context(PK11_CreateContextBySymKey(GetMechanism(mode_), CKA_ENCRYPT, key_->key(), param_.get())); if (!context.get()) return false; - size_t ciphertext_len = plaintext.size() + AES_BLOCK_SIZE; - std::vector<unsigned char> buffer(ciphertext_len); + if (mode_ == CTR) + return CryptCTR(context.get(), plaintext, ciphertext); + else + return Crypt(context.get(), plaintext, ciphertext); +} + +bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { + if (ciphertext.empty()) + return false; + + ScopedPK11Context context(PK11_CreateContextBySymKey( + GetMechanism(mode_), (mode_ == CTR ? CKA_ENCRYPT : CKA_DECRYPT), + key_->key(), param_.get())); + if (!context.get()) + return false; + + if (mode_ == CTR) + return CryptCTR(context.get(), ciphertext, plaintext); + else + return Crypt(context.get(), ciphertext, plaintext); +} + +bool Encryptor::Crypt(PK11Context* context, const std::string& input, + std::string* output) { + size_t output_len = input.size() + AES_BLOCK_SIZE; + CHECK(output_len > input.size()) << "Output size overflow"; + + output->resize(output_len); + uint8* output_data = + reinterpret_cast<uint8*>(const_cast<char*>(output->data())); + + int input_len = input.size(); + uint8* input_data = + reinterpret_cast<uint8*>(const_cast<char*>(input.data())); int op_len; - SECStatus rv = PK11_CipherOp(context.get(), - &buffer[0], + SECStatus rv = PK11_CipherOp(context, + output_data, &op_len, - ciphertext_len, - reinterpret_cast<unsigned char*>( - const_cast<char*>(plaintext.data())), - plaintext.size()); - if (SECSuccess != rv) + output_len, + input_data, + input_len); + + if (SECSuccess != rv) { + output->clear(); return false; + } unsigned int digest_len; - rv = PK11_DigestFinal(context.get(), - &buffer[op_len], + rv = PK11_DigestFinal(context, + output_data + op_len, &digest_len, - ciphertext_len - op_len); - if (SECSuccess != rv) + output_len - op_len); + if (SECSuccess != rv) { + output->clear(); return false; + } - ciphertext->assign(reinterpret_cast<char *>(&buffer[0]), - op_len + digest_len); + output->resize(op_len + digest_len); return true; } -bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { - if (ciphertext.empty()) +bool Encryptor::CryptCTR(PK11Context* context, const std::string& input, + std::string* output) { + if (!counter_.get()) { + LOG(ERROR) << "Counter value not set in CTR mode."; return false; - - ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, - CKA_DECRYPT, - key_->key(), - param_.get())); - if (!context.get()) + } + + size_t output_len = ((input.size() + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * + AES_BLOCK_SIZE; + CHECK(output_len >= input.size()) << "Output size overflow"; + output->resize(output_len); + uint8* output_data = + reinterpret_cast<uint8*>(const_cast<char*>(output->data())); + + size_t mask_len; + bool ret = GenerateCounterMask(input.size(), output_data, &mask_len); + if (!ret) return false; - size_t plaintext_len = ciphertext.size(); - std::vector<unsigned char> buffer(plaintext_len); - + CHECK_EQ(mask_len, output_len); int op_len; - SECStatus rv = PK11_CipherOp(context.get(), - &buffer[0], + SECStatus rv = PK11_CipherOp(context, + output_data, &op_len, - plaintext_len, - reinterpret_cast<unsigned char*>( - const_cast<char*>(ciphertext.data())), - ciphertext.size()); + output_len, + output_data, + mask_len); if (SECSuccess != rv) return false; + CHECK(op_len == static_cast<int>(mask_len)); unsigned int digest_len; - rv = PK11_DigestFinal(context.get(), - &buffer[op_len], + rv = PK11_DigestFinal(context, + NULL, &digest_len, - plaintext_len - op_len); + 0); if (SECSuccess != rv) return false; + CHECK(!digest_len); - plaintext->assign(reinterpret_cast<char *>(&buffer[0]), - op_len + digest_len); + // Use |output_data| to mask |input|. + MaskMessage( + reinterpret_cast<uint8*>(const_cast<char*>(input.data())), + input.length(), output_data, output_data); + output->resize(input.length()); return true; } |