Skip to content

Commit 3477b6b

Browse files
jasnellruyadorno
authored andcommitted
src: move more key related stuff to ncrypto
PR-URL: #55368 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 456b023 commit 3477b6b

File tree

7 files changed

+467
-375
lines changed

7 files changed

+467
-375
lines changed

deps/ncrypto/ncrypto.cc

+230-22
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ std::optional<std::string> CryptoErrorList::pop_front() {
8080

8181
// ============================================================================
8282
DataPointer DataPointer::Alloc(size_t len) {
83-
return DataPointer(OPENSSL_malloc(len), len);
83+
return DataPointer(OPENSSL_zalloc(len), len);
8484
}
8585

8686
DataPointer::DataPointer(void* data, size_t length)
@@ -1428,6 +1428,33 @@ DataPointer pbkdf2(const EVP_MD* md,
14281428

14291429
// ============================================================================
14301430

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

16631690
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
1664-
PKFormatType format,
1665-
PKEncodingType encoding,
1691+
const PublicKeyEncodingConfig& config,
16661692
const Buffer<const unsigned char>& buffer) {
1667-
if (format == PKFormatType::PEM) {
1693+
if (config.format == PKFormatType::PEM) {
16681694
return TryParsePublicKeyPEM(buffer);
16691695
}
16701696

1671-
if (format != PKFormatType::DER) {
1697+
if (config.format != PKFormatType::DER) {
16721698
return ParseKeyResult(PKParseError::FAILED);
16731699
}
16741700

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

16771703
EVP_PKEY* key = nullptr;
16781704

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

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

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

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

1698-
static auto keyOrError = [&](EVPKeyPointer pkey, bool had_passphrase = false) {
1745+
static constexpr auto keyOrError = [](EVPKeyPointer pkey, bool had_passphrase = false) {
16991746
if (int err = ERR_peek_error()) {
17001747
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
17011748
ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ &&
@@ -1708,24 +1755,23 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
17081755
return ParseKeyResult(std::move(pkey));
17091756
};
17101757

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

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

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

1724-
if (format != PKFormatType::DER) {
1770+
if (config.format != PKFormatType::DER) {
17251771
return ParseKeyResult(PKParseError::FAILED);
17261772
}
17271773

1728-
switch (encoding) {
1774+
switch (config.type) {
17291775
case PKEncodingType::PKCS1: {
17301776
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
17311777
return keyOrError(EVPKeyPointer(key));
@@ -1735,8 +1781,8 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
17351781
auto key = d2i_PKCS8PrivateKey_bio(bio.get(),
17361782
nullptr,
17371783
PasswordCallback,
1738-
passphrase);
1739-
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
1784+
config.passphrase.has_value() ? &passphrase : nullptr);
1785+
return keyOrError(EVPKeyPointer(key), config.passphrase.has_value());
17401786
}
17411787

17421788
PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
@@ -1755,4 +1801,166 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
17551801
};
17561802
}
17571803

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

deps/ncrypto/ncrypto.h

+28-8
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)