Skip to content

Commit 50b68a6

Browse files
authored
Implement wrapper for EVP_AEAD interface (#15)
1 parent ad09963 commit 50b68a6

File tree

3 files changed

+540
-68
lines changed

3 files changed

+540
-68
lines changed

include/ncrypto.h

Lines changed: 158 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#include <openssl/ssl.h>
1313
#include <openssl/x509.h>
1414

15+
#ifdef OPENSSL_IS_BORINGSSL
16+
#include <openssl/aead.h>
17+
#endif
18+
1519
#include <stdint.h>
1620
#include <cstddef>
1721
#include <cstdio>
@@ -21,6 +25,7 @@
2125
#include <optional>
2226
#include <string>
2327
#include <string_view>
28+
#include <unordered_map>
2429
#include <utility>
2530

2631
#if NCRYPTO_DEVELOPMENT_CHECKS
@@ -257,6 +262,8 @@ class ECKeyPointer;
257262
class Dsa;
258263
class Rsa;
259264
class Ec;
265+
class Aead;
266+
class AeadCtxPointer;
260267

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

314-
class Cipher final {
321+
template <typename T>
322+
class ModeMixin {
323+
public:
324+
std::string_view getModeLabel() const;
325+
326+
bool isGcmMode() const { return self().getMode() == EVP_CIPH_GCM_MODE; }
327+
bool isWrapMode() const { return self().getMode() == EVP_CIPH_WRAP_MODE; }
328+
bool isCtrMode() const { return self().getMode() == EVP_CIPH_CTR_MODE; }
329+
bool isCcmMode() const { return self().getMode() == EVP_CIPH_CCM_MODE; }
330+
bool isOcbMode() const { return self().getMode() == EVP_CIPH_OCB_MODE; }
331+
bool isStreamMode() const {
332+
return self().getMode() == EVP_CIPH_STREAM_CIPHER;
333+
}
334+
335+
private:
336+
const T& self() const { return static_cast<const T&>(*this); }
337+
};
338+
339+
class Cipher final : public ModeMixin<Cipher> {
315340
public:
316341
static constexpr size_t MAX_KEY_LENGTH = EVP_MAX_KEY_LENGTH;
317342
static constexpr size_t MAX_IV_LENGTH = EVP_MAX_IV_LENGTH;
@@ -344,15 +369,9 @@ class Cipher final {
344369
int getIvLength() const;
345370
int getKeyLength() const;
346371
int getBlockSize() const;
347-
std::string_view getModeLabel() const;
372+
348373
const char* getName() const;
349374

350-
bool isGcmMode() const;
351-
bool isWrapMode() const;
352-
bool isCtrMode() const;
353-
bool isCcmMode() const;
354-
bool isOcbMode() const;
355-
bool isStreamMode() const;
356375
bool isChaCha20Poly1305() const;
357376

358377
bool isSupportedAuthenticatedMode() const;
@@ -1734,6 +1753,137 @@ class KEM final {
17341753

17351754
#endif // OPENSSL_VERSION_MAJOR >= 3
17361755

1756+
// ============================================================================
1757+
// AEAD (Authenticated Encryption with Associated Data)
1758+
// Note that the underlying EVP_AEAD interface is specific to BoringSSL. AEAD
1759+
// primitives are accessed through the Cipher class instead, if using OpenSSL.
1760+
1761+
#ifdef OPENSSL_IS_BORINGSSL
1762+
class Aead final : public ModeMixin<Aead> {
1763+
private:
1764+
// BoringSSL does not keep a list of AEADs, so we need to maintain our own.
1765+
struct AeadInfo {
1766+
std::string name;
1767+
int mode;
1768+
int nid = 0; // Note: BoringSSL only defines NIDs for some AEADs
1769+
};
1770+
1771+
public:
1772+
Aead() = default;
1773+
Aead(const AeadInfo* info, const EVP_AEAD* aead) : info_(info), aead_(aead) {}
1774+
Aead(const Aead&) = default;
1775+
Aead& operator=(const Aead&) = default;
1776+
NCRYPTO_DISALLOW_MOVE(Aead)
1777+
1778+
inline const EVP_AEAD* get() const { return aead_; }
1779+
inline operator const EVP_AEAD*() const { return aead_; }
1780+
inline operator bool() const { return aead_ != nullptr; }
1781+
1782+
int getMode() const;
1783+
int getNonceLength() const;
1784+
int getKeyLength() const;
1785+
int getBlockSize() const;
1786+
int getMaxOverhead() const;
1787+
int getMaxTagLength() const;
1788+
std::string_view getName() const;
1789+
1790+
static const Aead FromName(std::string_view name);
1791+
1792+
// TODO(npaun): BoringSSL does not define NIDs for all AEADs.
1793+
// This method is included only for implementing getCipherInfo and can't be
1794+
// used to construct an Aead instance.
1795+
int getNid() const;
1796+
// static const AEAD FromNid(int nid);
1797+
1798+
static const Aead FromCtx(std::string_view name, const AeadCtxPointer& ctx);
1799+
1800+
using AeadNameCallback = std::function<void(std::string_view name)>;
1801+
1802+
// Iterates the known ciphers if the underlying implementation
1803+
// is able to do so.
1804+
static void ForEach(AeadNameCallback callback);
1805+
1806+
// Utilities to get various AEADs by type.
1807+
1808+
static const Aead EMPTY;
1809+
static const Aead AES_128_GCM;
1810+
static const Aead AES_192_GCM;
1811+
static const Aead AES_256_GCM;
1812+
static const Aead CHACHA20_POLY1305;
1813+
static const Aead XCHACHA20_POLY1305;
1814+
static const Aead AES_128_CTR_HMAC_SHA256;
1815+
static const Aead AES_256_CTR_HMAC_SHA256;
1816+
static const Aead AES_128_GCM_SIV;
1817+
static const Aead AES_256_GCM_SIV;
1818+
static const Aead AES_128_GCM_RANDNONCE;
1819+
static const Aead AES_256_GCM_RANDNONCE;
1820+
static const Aead AES_128_CCM_BLUETOOTH;
1821+
static const Aead AES_128_CCM_BLUETOOTH_8;
1822+
static const Aead AES_128_CCM_MATTER;
1823+
static const Aead AES_128_EAX;
1824+
static const Aead AES_256_EAX;
1825+
1826+
private:
1827+
const EVP_AEAD* aead_ = nullptr;
1828+
const AeadInfo* info_ = nullptr;
1829+
1830+
using AeadConstructor = const EVP_AEAD* (*)();
1831+
static const std::unordered_map<AeadConstructor, AeadInfo> aeadIndex;
1832+
static const Aead FromConstructor(AeadConstructor construct);
1833+
};
1834+
1835+
class AeadCtxPointer final {
1836+
public:
1837+
static AeadCtxPointer New(
1838+
const Aead& aead,
1839+
bool encrypt,
1840+
const unsigned char* key = nullptr,
1841+
size_t keyLen = 0,
1842+
size_t tagLen = EVP_AEAD_DEFAULT_TAG_LENGTH /* = 0 */);
1843+
1844+
AeadCtxPointer() = default;
1845+
explicit AeadCtxPointer(EVP_AEAD_CTX* ctx);
1846+
AeadCtxPointer(AeadCtxPointer&& other) noexcept;
1847+
AeadCtxPointer& operator=(AeadCtxPointer&& other) noexcept;
1848+
NCRYPTO_DISALLOW_COPY(AeadCtxPointer)
1849+
~AeadCtxPointer();
1850+
1851+
inline bool operator==(std::nullptr_t) const noexcept {
1852+
return ctx_ == nullptr;
1853+
}
1854+
inline operator bool() const { return ctx_ != nullptr; }
1855+
inline EVP_AEAD_CTX* get() const { return ctx_.get(); }
1856+
inline operator EVP_AEAD_CTX*() const { return ctx_.get(); }
1857+
void reset(EVP_AEAD_CTX* ctx = nullptr);
1858+
EVP_AEAD_CTX* release();
1859+
1860+
bool init(const Aead& aead,
1861+
bool encrypt,
1862+
const unsigned char* key = nullptr,
1863+
size_t keyLen = 0,
1864+
size_t tagLen = EVP_AEAD_DEFAULT_TAG_LENGTH /* = 0 */);
1865+
1866+
// TODO(npaun): BoringSSL does not define NIDs for all AEADs.
1867+
// Decide if we will even implement this method.
1868+
// int getNid() const;
1869+
1870+
bool encrypt(const Buffer<const unsigned char>& in,
1871+
Buffer<unsigned char>& out,
1872+
Buffer<unsigned char>& tag,
1873+
const Buffer<const unsigned char>& nonce,
1874+
const Buffer<const unsigned char>& aad);
1875+
1876+
bool decrypt(const Buffer<const unsigned char>& in,
1877+
Buffer<unsigned char>& out,
1878+
const Buffer<const unsigned char>& tag,
1879+
const Buffer<const unsigned char>& nonce,
1880+
const Buffer<const unsigned char>& aad);
1881+
1882+
private:
1883+
DeleteFnPtr<EVP_AEAD_CTX, EVP_AEAD_CTX_free> ctx_;
1884+
};
1885+
#endif
1886+
17371887
// ============================================================================
17381888
// Version metadata
17391889
#define NCRYPTO_VERSION "0.0.1"

0 commit comments

Comments
 (0)