Skip to content
Merged
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
166 changes: 158 additions & 8 deletions include/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#include <openssl/ssl.h>
#include <openssl/x509.h>

#ifdef OPENSSL_IS_BORINGSSL
#include <openssl/aead.h>
#endif

#include <stdint.h>
#include <cstddef>
#include <cstdio>
Expand All @@ -21,6 +25,7 @@
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>

#if NCRYPTO_DEVELOPMENT_CHECKS
Expand Down Expand Up @@ -257,6 +262,8 @@ class ECKeyPointer;
class Dsa;
class Rsa;
class Ec;
class Aead;
class AeadCtxPointer;

struct StackOfXASN1Deleter {
void operator()(STACK_OF(ASN1_OBJECT) * p) const {
Expand Down Expand Up @@ -311,7 +318,25 @@ DataPointer xofHashDigest(const Buffer<const unsigned char>& data,
const EVP_MD* md,
size_t length);

class Cipher final {
template <typename T>
class ModeMixin {
public:
std::string_view getModeLabel() const;

bool isGcmMode() const { return self().getMode() == EVP_CIPH_GCM_MODE; }
bool isWrapMode() const { return self().getMode() == EVP_CIPH_WRAP_MODE; }
bool isCtrMode() const { return self().getMode() == EVP_CIPH_CTR_MODE; }
bool isCcmMode() const { return self().getMode() == EVP_CIPH_CCM_MODE; }
bool isOcbMode() const { return self().getMode() == EVP_CIPH_OCB_MODE; }
bool isStreamMode() const {
return self().getMode() == EVP_CIPH_STREAM_CIPHER;
}

private:
const T& self() const { return static_cast<const T&>(*this); }
};

class Cipher final : public ModeMixin<Cipher> {
public:
static constexpr size_t MAX_KEY_LENGTH = EVP_MAX_KEY_LENGTH;
static constexpr size_t MAX_IV_LENGTH = EVP_MAX_IV_LENGTH;
Expand Down Expand Up @@ -344,15 +369,9 @@ class Cipher final {
int getIvLength() const;
int getKeyLength() const;
int getBlockSize() const;
std::string_view getModeLabel() const;

const char* getName() const;

bool isGcmMode() const;
bool isWrapMode() const;
bool isCtrMode() const;
bool isCcmMode() const;
bool isOcbMode() const;
bool isStreamMode() const;
bool isChaCha20Poly1305() const;

bool isSupportedAuthenticatedMode() const;
Expand Down Expand Up @@ -1734,6 +1753,137 @@ class KEM final {

#endif // OPENSSL_VERSION_MAJOR >= 3

// ============================================================================
// AEAD (Authenticated Encryption with Associated Data)
// Note that the underlying EVP_AEAD interface is specific to BoringSSL. AEAD
// primitives are accessed through the Cipher class instead, if using OpenSSL.

#ifdef OPENSSL_IS_BORINGSSL
class Aead final : public ModeMixin<Aead> {
private:
// BoringSSL does not keep a list of AEADs, so we need to maintain our own.
struct AeadInfo {
std::string name;
int mode;
int nid = 0; // Note: BoringSSL only defines NIDs for some AEADs
};

public:
Aead() = default;
Aead(const AeadInfo* info, const EVP_AEAD* aead) : info_(info), aead_(aead) {}
Aead(const Aead&) = default;
Aead& operator=(const Aead&) = default;
NCRYPTO_DISALLOW_MOVE(Aead)

inline const EVP_AEAD* get() const { return aead_; }
inline operator const EVP_AEAD*() const { return aead_; }
inline operator bool() const { return aead_ != nullptr; }

int getMode() const;
int getNonceLength() const;
int getKeyLength() const;
int getBlockSize() const;
int getMaxOverhead() const;
int getMaxTagLength() const;
std::string_view getName() const;

static const Aead FromName(std::string_view name);

// TODO(npaun): BoringSSL does not define NIDs for all AEADs.
// This method is included only for implementing getCipherInfo and can't be
// used to construct an Aead instance.
int getNid() const;
// static const AEAD FromNid(int nid);

static const Aead FromCtx(std::string_view name, const AeadCtxPointer& ctx);

using AeadNameCallback = std::function<void(std::string_view name)>;

// Iterates the known ciphers if the underlying implementation
// is able to do so.
static void ForEach(AeadNameCallback callback);

// Utilities to get various AEADs by type.

static const Aead EMPTY;
static const Aead AES_128_GCM;
static const Aead AES_192_GCM;
static const Aead AES_256_GCM;
static const Aead CHACHA20_POLY1305;
static const Aead XCHACHA20_POLY1305;
static const Aead AES_128_CTR_HMAC_SHA256;
static const Aead AES_256_CTR_HMAC_SHA256;
static const Aead AES_128_GCM_SIV;
static const Aead AES_256_GCM_SIV;
static const Aead AES_128_GCM_RANDNONCE;
static const Aead AES_256_GCM_RANDNONCE;
static const Aead AES_128_CCM_BLUETOOTH;
static const Aead AES_128_CCM_BLUETOOTH_8;
static const Aead AES_128_CCM_MATTER;
static const Aead AES_128_EAX;
static const Aead AES_256_EAX;

private:
const EVP_AEAD* aead_ = nullptr;
const AeadInfo* info_ = nullptr;

using AeadConstructor = const EVP_AEAD* (*)();
static const std::unordered_map<AeadConstructor, AeadInfo> aeadIndex;
static const Aead FromConstructor(AeadConstructor construct);
};

class AeadCtxPointer final {
public:
static AeadCtxPointer New(
const Aead& aead,
bool encrypt,
const unsigned char* key = nullptr,
size_t keyLen = 0,
size_t tagLen = EVP_AEAD_DEFAULT_TAG_LENGTH /* = 0 */);

AeadCtxPointer() = default;
explicit AeadCtxPointer(EVP_AEAD_CTX* ctx);
AeadCtxPointer(AeadCtxPointer&& other) noexcept;
AeadCtxPointer& operator=(AeadCtxPointer&& other) noexcept;
NCRYPTO_DISALLOW_COPY(AeadCtxPointer)
~AeadCtxPointer();

inline bool operator==(std::nullptr_t) const noexcept {
return ctx_ == nullptr;
}
inline operator bool() const { return ctx_ != nullptr; }
inline EVP_AEAD_CTX* get() const { return ctx_.get(); }
inline operator EVP_AEAD_CTX*() const { return ctx_.get(); }
void reset(EVP_AEAD_CTX* ctx = nullptr);
EVP_AEAD_CTX* release();

bool init(const Aead& aead,
bool encrypt,
const unsigned char* key = nullptr,
size_t keyLen = 0,
size_t tagLen = EVP_AEAD_DEFAULT_TAG_LENGTH /* = 0 */);

// TODO(npaun): BoringSSL does not define NIDs for all AEADs.
// Decide if we will even implement this method.
// int getNid() const;

bool encrypt(const Buffer<const unsigned char>& in,
Buffer<unsigned char>& out,
Buffer<unsigned char>& tag,
const Buffer<const unsigned char>& nonce,
const Buffer<const unsigned char>& aad);

bool decrypt(const Buffer<const unsigned char>& in,
Buffer<unsigned char>& out,
const Buffer<const unsigned char>& tag,
const Buffer<const unsigned char>& nonce,
const Buffer<const unsigned char>& aad);

private:
DeleteFnPtr<EVP_AEAD_CTX, EVP_AEAD_CTX_free> ctx_;
};
#endif

// ============================================================================
// Version metadata
#define NCRYPTO_VERSION "0.0.1"
Expand Down
Loading