Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: shift more crypto impl details to ncrypto #54028

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
460 changes: 437 additions & 23 deletions deps/ncrypto/ncrypto.cc

Large diffs are not rendered by default.

98 changes: 83 additions & 15 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#pragma once

#include <cstddef>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "openssl/bn.h"
#include <openssl/x509.h>
#include <openssl/dh.h>
Expand Down Expand Up @@ -82,6 +82,15 @@ namespace ncrypto {
void* operator new(size_t) = delete; \
void operator delete(void*) = delete;

[[noreturn]] inline void unreachable() {
#ifdef __GNUC__
__builtin_unreachable();
#elif defined(_MSC_VER)
__assume(false);
#else
#endif
}

// ============================================================================
// Error handling utilities

Expand Down Expand Up @@ -190,30 +199,92 @@ using SSLPointer = DeleteFnPtr<SSL, SSL_free>;
using SSLSessionPointer = DeleteFnPtr<SSL_SESSION, SSL_SESSION_free>;
using X509Pointer = DeleteFnPtr<X509, X509_free>;

// An unowned, unmanaged pointer to a buffer of data.
template <typename T>
struct Buffer {
T* data = nullptr;
size_t len = 0;
};

// A managed pointer to a buffer of data. When destroyed the underlying
// buffer will be freed.
class DataPointer final {
public:
static DataPointer Alloc(size_t len);

DataPointer() = default;
explicit DataPointer(void* data, size_t len);
explicit DataPointer(const Buffer<void>& buffer);
DataPointer(DataPointer&& other) noexcept;
DataPointer& operator=(DataPointer&& other) noexcept;
NCRYPTO_DISALLOW_COPY(DataPointer)
~DataPointer();

inline bool operator==(std::nullptr_t) noexcept { return data_ == nullptr; }
inline operator bool() const { return data_ != nullptr; }
inline void* get() const noexcept { return data_; }
inline size_t size() const noexcept { return len_; }
void reset(void* data = nullptr, size_t len = 0);
void reset(const Buffer<void>& buffer);

// Releases ownership of the underlying data buffer. It is the caller's
// responsibility to ensure the buffer is appropriately freed.
Buffer<void> release();

// Returns a Buffer struct that is a view of the underlying data.
inline operator const Buffer<void>() const {
return {
.data = data_,
.len = len_,
};
}

private:
void* data_ = nullptr;
size_t len_ = 0;
};

class BignumPointer final {
public:
BignumPointer() = default;
explicit BignumPointer(BIGNUM* bignum);
explicit BignumPointer(const unsigned char* data, size_t len);
BignumPointer(BignumPointer&& other) noexcept;
BignumPointer& operator=(BignumPointer&& other) noexcept;
NCRYPTO_DISALLOW_COPY(BignumPointer)
~BignumPointer();

bool operator==(const BignumPointer& other) noexcept;
bool operator==(const BIGNUM* other) noexcept;
inline bool operator==(std::nullptr_t) noexcept { return bn_ == nullptr; }
int operator<=>(const BignumPointer& other) const noexcept;
int operator<=>(const BIGNUM* other) const noexcept;
inline operator bool() const { return bn_ != nullptr; }
inline BIGNUM* get() const noexcept { return bn_.get(); }
void reset(BIGNUM* bn = nullptr);
void reset(const unsigned char* data, size_t len);
BIGNUM* release();

size_t byteLength();
bool isZero() const;
bool isOne() const;

std::vector<uint8_t> encode();
std::vector<uint8_t> encodePadded(size_t size);
bool setWord(unsigned long w);
unsigned long getWord() const;

static std::vector<uint8_t> encode(const BIGNUM* bn);
static std::vector<uint8_t> encodePadded(const BIGNUM* bn, size_t size);
size_t byteLength() const;

DataPointer toHex() const;
DataPointer encode() const;
DataPointer encodePadded(size_t size) const;
size_t encodeInto(unsigned char* out) const;
size_t encodePaddedInto(unsigned char* out, size_t size) const;

static BignumPointer New();
static BignumPointer NewSecure();
static DataPointer Encode(const BIGNUM* bn);
static DataPointer EncodePadded(const BIGNUM* bn, size_t size);
static size_t EncodePaddedInto(const BIGNUM* bn, unsigned char* out, size_t size);
static int GetBitCount(const BIGNUM* bn);
static int GetByteCount(const BIGNUM* bn);
static unsigned long GetWord(const BIGNUM* bn);
static const BIGNUM* One();

private:
DeleteFnPtr<BIGNUM, BN_clear_free> bn_;
Expand Down Expand Up @@ -269,12 +340,6 @@ bool testFipsEnabled();
// ============================================================================
// Various utilities

template <typename T>
struct Buffer {
T* data = nullptr;
size_t len = 0;
};

bool CSPRNG(void* buffer, size_t length) NCRYPTO_MUST_USE_RESULT;

// This callback is used to avoid the default passphrase callback in OpenSSL
Expand All @@ -286,6 +351,9 @@ int NoPasswordCallback(char* buf, int size, int rwflag, void* u);

int PasswordCallback(char* buf, int size, int rwflag, void* u);

bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext);
bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext);

// ============================================================================
// SPKAC

Expand Down
25 changes: 10 additions & 15 deletions src/crypto/crypto_aes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,7 @@ BignumPointer GetCounter(const AESCipherConfig& params) {

if (remainder == 0) {
unsigned int byte_length = params.length / CHAR_BIT;
return BignumPointer(BN_bin2bn(
data + params.iv.size() - byte_length,
byte_length,
nullptr));
return BignumPointer(data + params.iv.size() - byte_length, byte_length);
}

unsigned int byte_length =
Expand All @@ -199,7 +196,7 @@ BignumPointer GetCounter(const AESCipherConfig& params) {
data + params.iv.size());
counter[0] &= ~(0xFF << remainder);

return BignumPointer(BN_bin2bn(counter.data(), counter.size(), nullptr));
return BignumPointer(counter.data(), counter.size());
}

std::vector<unsigned char> BlockWithZeroedCounter(
Expand Down Expand Up @@ -269,23 +266,22 @@ WebCryptoCipherStatus AES_CTR_Cipher(
const AESCipherConfig& params,
const ByteSource& in,
ByteSource* out) {
BignumPointer num_counters(BN_new());
if (!BN_lshift(num_counters.get(), BN_value_one(), params.length))
auto num_counters = BignumPointer::New();
if (!BN_lshift(num_counters.get(), BignumPointer::One(), params.length))
return WebCryptoCipherStatus::FAILED;

BignumPointer current_counter = GetCounter(params);

BignumPointer num_output(BN_new());
auto num_output = BignumPointer::New();

if (!BN_set_word(num_output.get(), CeilDiv(in.size(), kAesBlockSize)))
if (!num_output.setWord(CeilDiv(in.size(), kAesBlockSize)))
return WebCryptoCipherStatus::FAILED;

// Just like in chromium's implementation, if the counter will
// be incremented more than there are counter values, we fail.
if (BN_cmp(num_output.get(), num_counters.get()) > 0)
return WebCryptoCipherStatus::FAILED;
if (num_output > num_counters) return WebCryptoCipherStatus::FAILED;

BignumPointer remaining_until_reset(BN_new());
auto remaining_until_reset = BignumPointer::New();
if (!BN_sub(remaining_until_reset.get(),
num_counters.get(),
current_counter.get())) {
Expand All @@ -298,7 +294,7 @@ WebCryptoCipherStatus AES_CTR_Cipher(
// Also just like in chromium's implementation, if we can process
// the input without wrapping the counter, we'll do it as a single
// call here. If we can't, we'll fallback to the a two-step approach
if (BN_cmp(remaining_until_reset.get(), num_output.get()) >= 0) {
if (remaining_until_reset >= num_output) {
auto status = AES_CTR_Cipher2(key_data,
cipher_mode,
params,
Expand All @@ -309,8 +305,7 @@ WebCryptoCipherStatus AES_CTR_Cipher(
return status;
}

BN_ULONG blocks_part1 = BN_get_word(remaining_until_reset.get());
BN_ULONG input_size_part1 = blocks_part1 * kAesBlockSize;
BN_ULONG input_size_part1 = remaining_until_reset.getWord() * kAesBlockSize;

// Encrypt the first part...
auto status =
Expand Down
Loading
Loading