You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
3.9 KiB
108 lines
3.9 KiB
// 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 <random>
|
|
#include <utility>
|
|
|
|
#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<uint8_t>(owned_data_);
|
|
}
|
|
|
|
EncryptedFrame::~EncryptedFrame() = default;
|
|
|
|
EncryptedFrame::EncryptedFrame(EncryptedFrame&& other) noexcept
|
|
: EncodedFrame(static_cast<EncodedFrame&&>(other)),
|
|
owned_data_(std::move(other.owned_data_)) {
|
|
data = absl::Span<uint8_t>(owned_data_);
|
|
other.data = absl::Span<uint8_t>{};
|
|
}
|
|
|
|
EncryptedFrame& EncryptedFrame::operator=(EncryptedFrame&& other) {
|
|
this->EncodedFrame::operator=(static_cast<EncodedFrame&&>(other));
|
|
owned_data_ = std::move(other.owned_data_);
|
|
data = absl::Span<uint8_t>(owned_data_);
|
|
other.data = absl::Span<uint8_t>{};
|
|
return *this;
|
|
}
|
|
|
|
FrameCrypto::FrameCrypto(const std::array<uint8_t, 16>& aes_key,
|
|
const std::array<uint8_t, 16>& 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*>(&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<uint8_t>(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<uint8_t>(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<const uint8_t> in,
|
|
absl::Span<uint8_t> 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<uint8_t, 16> aes_nonce{/* zero initialized */};
|
|
static_assert(AES_BLOCK_SIZE == sizeof(aes_nonce),
|
|
"AES_BLOCK_SIZE is not 16 bytes.");
|
|
WriteBigEndian<uint32_t>(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<uint8_t, 16> 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
|