// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cast/streaming/frame_crypto.h" #include #include #include "openssl/crypto.h" #include "openssl/err.h" #include "openssl/rand.h" #include "util/big_endian.h" #include "util/crypto/openssl_util.h" #include "util/crypto/random_bytes.h" namespace openscreen { namespace cast { EncryptedFrame::EncryptedFrame() { data = absl::Span(owned_data_); } EncryptedFrame::~EncryptedFrame() = default; EncryptedFrame::EncryptedFrame(EncryptedFrame&& other) noexcept : EncodedFrame(static_cast(other)), owned_data_(std::move(other.owned_data_)) { data = absl::Span(owned_data_); other.data = absl::Span{}; } EncryptedFrame& EncryptedFrame::operator=(EncryptedFrame&& other) { this->EncodedFrame::operator=(static_cast(other)); owned_data_ = std::move(other.owned_data_); data = absl::Span(owned_data_); other.data = absl::Span{}; return *this; } FrameCrypto::FrameCrypto(const std::array& aes_key, const std::array& cast_iv_mask) : aes_key_{}, cast_iv_mask_(cast_iv_mask) { // Ensure that the library has been initialized. CRYPTO_library_init() may be // safely called multiple times during the life of a process. CRYPTO_library_init(); // Initialize the 244-byte AES_KEY struct once, here at construction time. The // const_cast<> is reasonable as this is a one-time-ctor-initialized value // that will remain constant from here onward. const int return_code = AES_set_encrypt_key( aes_key.data(), aes_key.size() * 8, const_cast(&aes_key_)); if (return_code != 0) { ClearOpenSSLERRStack(CURRENT_LOCATION); OSP_LOG_FATAL << "Failure when setting encryption key; unsafe to continue."; OSP_NOTREACHED(); } } FrameCrypto::~FrameCrypto() = default; EncryptedFrame FrameCrypto::Encrypt(const EncodedFrame& encoded_frame) const { EncryptedFrame result; encoded_frame.CopyMetadataTo(&result); result.owned_data_.resize(encoded_frame.data.size()); result.data = absl::Span(result.owned_data_); EncryptCommon(encoded_frame.frame_id, encoded_frame.data, result.data); return result; } void FrameCrypto::Decrypt(const EncryptedFrame& encrypted_frame, EncodedFrame* encoded_frame) const { encrypted_frame.CopyMetadataTo(encoded_frame); // AES-CTC is symmetric. Thus, decryption back to the plaintext is the same as // encrypting the ciphertext; and both are the same size. if (encrypted_frame.data.size() < encoded_frame->data.size()) { encoded_frame->data = absl::Span(encoded_frame->data.data(), encrypted_frame.data.size()); } EncryptCommon(encrypted_frame.frame_id, encrypted_frame.data, encoded_frame->data); } void FrameCrypto::EncryptCommon(FrameId frame_id, absl::Span in, absl::Span out) const { OSP_DCHECK(!frame_id.is_null()); OSP_DCHECK_EQ(in.size(), out.size()); // Compute the AES nonce for Cast Streaming payload encryption, which is based // on the |frame_id|. std::array aes_nonce{/* zero initialized */}; static_assert(AES_BLOCK_SIZE == sizeof(aes_nonce), "AES_BLOCK_SIZE is not 16 bytes."); WriteBigEndian(frame_id.lower_32_bits(), aes_nonce.data() + 8); for (size_t i = 0; i < aes_nonce.size(); ++i) { aes_nonce[i] ^= cast_iv_mask_[i]; } std::array ecount_buf{/* zero initialized */}; unsigned int block_offset = 0; AES_ctr128_encrypt(in.data(), out.data(), in.size(), &aes_key_, aes_nonce.data(), ecount_buf.data(), &block_offset); } } // namespace cast } // namespace openscreen