| // Copyright 2020 The Cobalt Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "cobalt/subtlecrypto/crypto_impl.h" | |
| #include <openssl/cipher.h> | |
| #include <openssl/hmac.h> | |
| #include <openssl/sha.h> | |
| #include <algorithm> | |
| #include <map> | |
| #include "base/logging.h" | |
| namespace cobalt { | |
| namespace subtlecrypto { | |
| namespace { | |
| using EVP_Func = const EVP_MD *(*)(void); | |
| template <typename TCtx, size_t HashLen, EVP_Func evp_func, | |
| int (*fInit)(TCtx *), int (*fUpdate)(TCtx *, const void *, size_t), | |
| int (*fFinal)(unsigned char *, TCtx *)> | |
| class HashImpl : public Hash { | |
| TCtx ctx; | |
| public: | |
| HashImpl() { fInit(&ctx); } | |
| void Update(const ByteVector &data) override { | |
| if (!data.empty()) { | |
| fUpdate(&ctx, static_cast<const unsigned char *>(data.data()), | |
| data.size()); | |
| } | |
| } | |
| ByteVector Finish() override { | |
| ByteVector out(HashLen); | |
| fFinal(out.data(), &ctx); | |
| return out; | |
| } | |
| ByteVector CalculateHMAC(const ByteVector &data, | |
| const ByteVector &key) override { | |
| ByteVector ret(HashLen); | |
| unsigned int ret_len = static_cast<unsigned int>(ret.size()); | |
| auto result = HMAC(evp_func(), key.data(), key.size(), data.data(), | |
| data.size(), ret.data(), &ret_len); | |
| if (ret_len == HashLen && result) { | |
| return ret; | |
| } | |
| return {}; | |
| } | |
| }; | |
| using Sha1Hash = HashImpl<SHA_CTX, SHA_DIGEST_LENGTH, EVP_sha1, SHA1_Init, | |
| SHA1_Update, SHA1_Final>; | |
| using Sha256Hash = HashImpl<SHA256_CTX, SHA256_DIGEST_LENGTH, EVP_sha256, | |
| SHA256_Init, SHA256_Update, SHA256_Final>; | |
| using Sha384Hash = HashImpl<SHA512_CTX, SHA384_DIGEST_LENGTH, EVP_sha384, | |
| SHA384_Init, SHA384_Update, SHA384_Final>; | |
| using Sha512Hash = HashImpl<SHA512_CTX, SHA512_DIGEST_LENGTH, EVP_sha512, | |
| SHA512_Init, SHA512_Update, SHA512_Final>; | |
| using HashPtr = std::unique_ptr<Hash>; | |
| } // namespace | |
| std::unique_ptr<Hash> Hash::CreateByName(const std::string &name) { | |
| std::string tmp_name(name); | |
| transform(tmp_name.begin(), tmp_name.end(), tmp_name.begin(), ::toupper); | |
| if (tmp_name == "SHA-1") return std::make_unique<Sha1Hash>(); | |
| if (tmp_name == "SHA-256") return std::make_unique<Sha256Hash>(); | |
| if (tmp_name == "SHA-384") return std::make_unique<Sha384Hash>(); | |
| if (tmp_name == "SHA-512") return std::make_unique<Sha512Hash>(); | |
| return nullptr; | |
| } | |
| ByteVector CalculateAES_CTR(const ByteVector &data, const ByteVector &key, | |
| const ByteVector &iv) { | |
| EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); | |
| DCHECK(ctx); | |
| auto error = [&ctx](const char *msg) -> ByteVector { | |
| DLOG(ERROR) << msg; | |
| EVP_CIPHER_CTX_free(ctx); | |
| return {}; | |
| }; | |
| if (iv.size() != 16) { | |
| return error("Invalid initialization vector size, AES requires 128-bit IV"); | |
| } | |
| const char *algo = nullptr; | |
| switch (key.size()) { | |
| case 16: | |
| algo = "aes-128-ctr"; | |
| break; | |
| case 24: | |
| algo = "aes-192-ctr"; | |
| break; | |
| case 32: | |
| algo = "aes-256-ctr"; | |
| break; | |
| } | |
| if (!algo) { | |
| return error("Invalid key size, 128, 192 or 256 bits required"); | |
| } | |
| if (!EVP_EncryptInit_ex(ctx, EVP_get_cipherbyname(algo), NULL, key.data(), | |
| iv.data())) { | |
| return error("EVP_EncryptInit_ex failed"); | |
| } | |
| int buf_len = 0, out_len = 0; | |
| ByteVector out(data.size() + 1); | |
| if (!EVP_EncryptUpdate(ctx, out.data(), &buf_len, data.data(), | |
| static_cast<int>(data.size()))) { | |
| return error("EncryptUpdate failed"); | |
| } | |
| out_len += buf_len; | |
| if (!EVP_EncryptFinal_ex(ctx, &out[buf_len], &buf_len)) { | |
| return error("EVP_EncryptFinal_ex failed"); | |
| } | |
| out_len += buf_len; | |
| out.resize(out_len); | |
| EVP_CIPHER_CTX_free(ctx); | |
| return out; | |
| } | |
| } // namespace subtlecrypto | |
| } // namespace cobalt |