Skip to content

Commit bf9673b

Browse files
committed
src: move more key related stuff to ncrypto
1 parent d881fcb commit bf9673b

File tree

7 files changed

+463
-375
lines changed

7 files changed

+463
-375
lines changed

deps/ncrypto/ncrypto.cc

Lines changed: 226 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ std::optional<std::string> CryptoErrorList::pop_front() {
7979

8080
// ============================================================================
8181
DataPointer DataPointer::Alloc(size_t len) {
82-
return DataPointer(OPENSSL_malloc(len), len);
82+
return DataPointer(OPENSSL_zalloc(len), len);
8383
}
8484

8585
DataPointer::DataPointer(void* data, size_t length)
@@ -1427,6 +1427,33 @@ DataPointer pbkdf2(const EVP_MD* md,
14271427

14281428
// ============================================================================
14291429

1430+
EVPKeyPointer::PrivateKeyEncodingConfig::PrivateKeyEncodingConfig(
1431+
const PrivateKeyEncodingConfig& other)
1432+
: PrivateKeyEncodingConfig(other.output_key_object, other.format, other.type) {
1433+
cipher = other.cipher;
1434+
if (other.passphrase.has_value()) {
1435+
auto& otherPassphrase = other.passphrase.value();
1436+
auto newPassphrase = DataPointer::Alloc(otherPassphrase.size());
1437+
memcpy(newPassphrase.get(), otherPassphrase.get(), otherPassphrase.size());
1438+
passphrase = std::move(newPassphrase);
1439+
}
1440+
}
1441+
1442+
EVPKeyPointer::AsymmetricKeyEncodingConfig::AsymmetricKeyEncodingConfig(
1443+
bool output_key_object,
1444+
PKFormatType format,
1445+
PKEncodingType type)
1446+
: output_key_object(output_key_object),
1447+
format(format),
1448+
type(type) {}
1449+
1450+
EVPKeyPointer::PrivateKeyEncodingConfig& EVPKeyPointer::PrivateKeyEncodingConfig::operator=(
1451+
const PrivateKeyEncodingConfig& other) {
1452+
if (this == &other) return *this;
1453+
this->~PrivateKeyEncodingConfig();
1454+
return *new (this) PrivateKeyEncodingConfig(other);
1455+
}
1456+
14301457
EVPKeyPointer EVPKeyPointer::New() {
14311458
return EVPKeyPointer(EVP_PKEY_new());
14321459
}
@@ -1660,41 +1687,61 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKeyPEM(
16601687
}
16611688

16621689
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
1663-
PKFormatType format,
1664-
PKEncodingType encoding,
1690+
const PublicKeyEncodingConfig& config,
16651691
const Buffer<const unsigned char>& buffer) {
1666-
if (format == PKFormatType::PEM) {
1692+
if (config.format == PKFormatType::PEM) {
16671693
return TryParsePublicKeyPEM(buffer);
16681694
}
16691695

1670-
if (format != PKFormatType::DER) {
1696+
if (config.format != PKFormatType::DER) {
16711697
return ParseKeyResult(PKParseError::FAILED);
16721698
}
16731699

16741700
const unsigned char* start = buffer.data;
16751701

16761702
EVP_PKEY* key = nullptr;
16771703

1678-
if (encoding == PKEncodingType::PKCS1 &&
1704+
if (config.type == PKEncodingType::PKCS1 &&
16791705
(key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) {
16801706
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
16811707
}
16821708

1683-
if (encoding == PKEncodingType::SPKI &&
1709+
if (config.type == PKEncodingType::SPKI &&
16841710
(key = d2i_PUBKEY(nullptr, &start, buffer.len))) {
16851711
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key));
16861712
}
16871713

16881714
return ParseKeyResult(PKParseError::FAILED);
16891715
}
16901716

1717+
namespace {
1718+
Buffer<char> GetPassphrase(const EVPKeyPointer::PrivateKeyEncodingConfig& config) {
1719+
Buffer<char> pass {
1720+
// OpenSSL will not actually dereference this pointer, so it can be any
1721+
// non-null pointer. We cannot assert that directly, which is why we
1722+
// intentionally use a pointer that will likely cause a segmentation fault
1723+
// when dereferenced.
1724+
.data = reinterpret_cast<char*>(-1),
1725+
.len = 0,
1726+
};
1727+
if (config.passphrase.has_value()) {
1728+
auto& passphrase = config.passphrase.value();
1729+
// The pass.data can't be a nullptr, even if the len is zero or else
1730+
// openssl will prompt for a password and we really don't want that.
1731+
if (passphrase.get() != nullptr) {
1732+
pass.data = static_cast<char*>(passphrase.get());
1733+
}
1734+
pass.len = passphrase.size();
1735+
}
1736+
return pass;
1737+
}
1738+
} // namespace
1739+
16911740
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
1692-
PKFormatType format,
1693-
PKEncodingType encoding,
1694-
std::optional<Buffer<char>> maybe_passphrase,
1741+
const PrivateKeyEncodingConfig& config,
16951742
const Buffer<const unsigned char>& buffer) {
16961743

1697-
static auto keyOrError = [&](EVPKeyPointer pkey, bool had_passphrase = false) {
1744+
static constexpr auto keyOrError = [](EVPKeyPointer pkey, bool had_passphrase = false) {
16981745
if (int err = ERR_peek_error()) {
16991746
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
17001747
ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ &&
@@ -1707,24 +1754,23 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
17071754
return ParseKeyResult(std::move(pkey));
17081755
};
17091756

1710-
Buffer<char>* passphrase = nullptr;
1711-
if (maybe_passphrase.has_value()) {
1712-
passphrase = &maybe_passphrase.value();
1713-
}
17141757

17151758
auto bio = BIOPointer::New(buffer);
17161759
if (!bio) return ParseKeyResult(PKParseError::FAILED);
17171760

1718-
if (format == PKFormatType::PEM) {
1719-
auto key = PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback, passphrase);
1720-
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
1761+
auto passphrase = GetPassphrase(config);
1762+
1763+
if (config.format == PKFormatType::PEM) {
1764+
auto key = PEM_read_bio_PrivateKey(bio.get(), nullptr, PasswordCallback,
1765+
config.passphrase.has_value() ? &passphrase : nullptr);
1766+
return keyOrError(EVPKeyPointer(key), config.passphrase.has_value());
17211767
}
17221768

1723-
if (format != PKFormatType::DER) {
1769+
if (config.format != PKFormatType::DER) {
17241770
return ParseKeyResult(PKParseError::FAILED);
17251771
}
17261772

1727-
switch (encoding) {
1773+
switch (config.type) {
17281774
case PKEncodingType::PKCS1: {
17291775
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
17301776
return keyOrError(EVPKeyPointer(key));
@@ -1734,8 +1780,8 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
17341780
auto key = d2i_PKCS8PrivateKey_bio(bio.get(),
17351781
nullptr,
17361782
PasswordCallback,
1737-
passphrase);
1738-
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
1783+
config.passphrase.has_value() ? &passphrase : nullptr);
1784+
return keyOrError(EVPKeyPointer(key), config.passphrase.has_value());
17391785
}
17401786

17411787
PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
@@ -1754,4 +1800,162 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
17541800
};
17551801
}
17561802

1803+
Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
1804+
const PrivateKeyEncodingConfig& config) const {
1805+
if (config.format == PKFormatType::JWK) {
1806+
return Result<BIOPointer, bool>(false);
1807+
}
1808+
1809+
auto bio = BIOPointer::NewMem();
1810+
if (!bio) {
1811+
return Result<BIOPointer, bool>(false);
1812+
}
1813+
1814+
auto passphrase = GetPassphrase(config);
1815+
MarkPopErrorOnReturn mark_pop_error_on_return;
1816+
bool err;
1817+
1818+
switch (config.type) {
1819+
case PKEncodingType::PKCS1: {
1820+
// PKCS1 is only permitted for RSA keys.
1821+
if (id() != EVP_PKEY_RSA) return Result<BIOPointer, bool>(false);
1822+
1823+
#if OPENSSL_VERSION_MAJOR >= 3
1824+
const RSA* rsa = EVP_PKEY_get0_RSA(get());
1825+
#else
1826+
RSA* rsa = EVP_PKEY_get0_RSA(get());
1827+
#endif
1828+
switch (config.format) {
1829+
case PKFormatType::PEM: {
1830+
err = PEM_write_bio_RSAPrivateKey(bio.get(), rsa, config.cipher,
1831+
reinterpret_cast<unsigned char*>(passphrase.data),
1832+
passphrase.len, nullptr, nullptr) != 1;
1833+
break;
1834+
}
1835+
case PKFormatType::DER: {
1836+
// Encoding PKCS1 as DER. This variation does not permit encryption.
1837+
err = i2d_RSAPrivateKey_bio(bio.get(), rsa) != 1;
1838+
break;
1839+
}
1840+
default: {
1841+
// Should never get here.
1842+
return Result<BIOPointer, bool>(false);
1843+
}
1844+
}
1845+
break;
1846+
}
1847+
case PKEncodingType::PKCS8: {
1848+
switch (config.format) {
1849+
case PKFormatType::PEM: {
1850+
// Encode PKCS#8 as PEM.
1851+
err = PEM_write_bio_PKCS8PrivateKey(
1852+
bio.get(), get(),
1853+
config.cipher,
1854+
passphrase.data,
1855+
passphrase.len,
1856+
nullptr, nullptr) != 1;
1857+
break;
1858+
}
1859+
case PKFormatType::DER: {
1860+
err = i2d_PKCS8PrivateKey_bio(
1861+
bio.get(), get(),
1862+
config.cipher,
1863+
passphrase.data,
1864+
passphrase.len,
1865+
nullptr, nullptr) != 1;
1866+
break;
1867+
}
1868+
default: {
1869+
// Should never get here.
1870+
return Result<BIOPointer, bool>(false);
1871+
}
1872+
}
1873+
break;
1874+
}
1875+
case PKEncodingType::SEC1: {
1876+
// SEC1 is only permitted for EC keys
1877+
if (id() != EVP_PKEY_EC) return Result<BIOPointer, bool>(false);
1878+
1879+
const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get());
1880+
switch (config.format) {
1881+
case PKFormatType::PEM: {
1882+
err = PEM_write_bio_ECPrivateKey(bio.get(),
1883+
ec,
1884+
config.cipher,
1885+
reinterpret_cast<unsigned char*>(passphrase.data),
1886+
passphrase.len,
1887+
nullptr,
1888+
nullptr) != 1;
1889+
break;
1890+
}
1891+
case PKFormatType::DER: {
1892+
// Encoding SEC1 as DER. This variation does not permit encryption.
1893+
err = i2d_ECPrivateKey_bio(bio.get(), ec) != 1;
1894+
break;
1895+
}
1896+
default: {
1897+
// Should never get here.
1898+
return Result<BIOPointer, bool>(false);
1899+
}
1900+
}
1901+
break;
1902+
}
1903+
default: {
1904+
// Not a valid private key encoding
1905+
return Result<BIOPointer, bool>(false);
1906+
}
1907+
}
1908+
1909+
if (err) {
1910+
// Failed to encode the private key.
1911+
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
1912+
}
1913+
1914+
return bio;
1915+
}
1916+
1917+
Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
1918+
const ncrypto::EVPKeyPointer::PublicKeyEncodingConfig& config) const {
1919+
auto bio = BIOPointer::NewMem();
1920+
if (!bio) return Result<BIOPointer, bool>(false);
1921+
1922+
MarkPopErrorOnReturn mark_pop_error_on_return;
1923+
1924+
if (config.type == ncrypto::EVPKeyPointer::PKEncodingType::PKCS1) {
1925+
// PKCS#1 is only valid for RSA keys.
1926+
#if OPENSSL_VERSION_MAJOR >= 3
1927+
const RSA* rsa = EVP_PKEY_get0_RSA(get());
1928+
#else
1929+
RSA* rsa = EVP_PKEY_get0_RSA(get());
1930+
#endif
1931+
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
1932+
// Encode PKCS#1 as PEM.
1933+
if (PEM_write_bio_RSAPublicKey(bio.get(), rsa) != 1) {
1934+
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
1935+
}
1936+
return bio;
1937+
}
1938+
1939+
// Encode PKCS#1 as DER.
1940+
if (i2d_RSAPublicKey_bio(bio.get(), rsa) != 1) {
1941+
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
1942+
}
1943+
return bio;
1944+
}
1945+
1946+
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
1947+
// Encode SPKI as PEM.
1948+
if (PEM_write_bio_PUBKEY(bio.get(), get()) != 1) {
1949+
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
1950+
}
1951+
return bio;
1952+
}
1953+
1954+
// Encode SPKI as DER.
1955+
if (i2d_PUBKEY_bio(bio.get(), get()) != 1) {
1956+
return Result<BIOPointer, bool>(false, mark_pop_error_on_return.peekError());
1957+
}
1958+
return bio;
1959+
}
1960+
17571961
} // namespace ncrypto

deps/ncrypto/ncrypto.h

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -386,13 +386,13 @@ class EVPKeyPointer final {
386386
// SubjectPublicKeyInfo according to X.509.
387387
SPKI,
388388
// ECPrivateKey according to SEC1.
389-
SEC1
389+
SEC1,
390390
};
391391

392392
enum class PKFormatType {
393393
DER,
394394
PEM,
395-
JWK
395+
JWK,
396396
};
397397

398398
enum class PKParseError {
@@ -402,18 +402,36 @@ class EVPKeyPointer final {
402402
};
403403
using ParseKeyResult = Result<EVPKeyPointer, PKParseError>;
404404

405+
struct AsymmetricKeyEncodingConfig {
406+
bool output_key_object = false;
407+
PKFormatType format = PKFormatType::DER;
408+
PKEncodingType type = PKEncodingType::PKCS8;
409+
AsymmetricKeyEncodingConfig() = default;
410+
AsymmetricKeyEncodingConfig(bool output_key_object, PKFormatType format, PKEncodingType type);
411+
AsymmetricKeyEncodingConfig(const AsymmetricKeyEncodingConfig&) = default;
412+
AsymmetricKeyEncodingConfig& operator=(const AsymmetricKeyEncodingConfig&) = default;
413+
};
414+
using PublicKeyEncodingConfig = AsymmetricKeyEncodingConfig;
415+
416+
struct PrivateKeyEncodingConfig: public AsymmetricKeyEncodingConfig {
417+
const EVP_CIPHER* cipher = nullptr;
418+
std::optional<DataPointer> passphrase = std::nullopt;
419+
PrivateKeyEncodingConfig() = default;
420+
PrivateKeyEncodingConfig(bool output_key_object, PKFormatType format, PKEncodingType type)
421+
: AsymmetricKeyEncodingConfig(output_key_object, format, type) {}
422+
PrivateKeyEncodingConfig(const PrivateKeyEncodingConfig&);
423+
PrivateKeyEncodingConfig& operator=(const PrivateKeyEncodingConfig&);
424+
};
425+
405426
static ParseKeyResult TryParsePublicKey(
406-
PKFormatType format,
407-
PKEncodingType encoding,
427+
const PublicKeyEncodingConfig& config,
408428
const Buffer<const unsigned char>& buffer);
409429

410430
static ParseKeyResult TryParsePublicKeyPEM(
411431
const Buffer<const unsigned char>& buffer);
412432

413433
static ParseKeyResult TryParsePrivateKey(
414-
PKFormatType format,
415-
PKEncodingType encoding,
416-
std::optional<Buffer<char>> passphrase,
434+
const PrivateKeyEncodingConfig& config,
417435
const Buffer<const unsigned char>& buffer);
418436

419437
EVPKeyPointer() = default;
@@ -441,9 +459,11 @@ class EVPKeyPointer final {
441459
size_t rawPrivateKeySize() const;
442460
DataPointer rawPublicKey() const;
443461
DataPointer rawPrivateKey() const;
444-
445462
BIOPointer derPublicKey() const;
446463

464+
Result<BIOPointer, bool> writePrivateKey(const PrivateKeyEncodingConfig& config) const;
465+
Result<BIOPointer, bool> writePublicKey(const PublicKeyEncodingConfig& config) const;
466+
447467
EVPKeyCtxPointer newCtx() const;
448468

449469
static bool IsRSAPrivateKey(const Buffer<const unsigned char>& buffer);

0 commit comments

Comments
 (0)