Skip to content

Commit 942ad54

Browse files
jasnelltargos
authored andcommitted
src: move more key handling to ncrypto
PR-URL: #55108 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matthew Aitken <maitken033380023@gmail.com>
1 parent 61b9ed3 commit 942ad54

File tree

3 files changed

+254
-253
lines changed

3 files changed

+254
-253
lines changed

deps/ncrypto/ncrypto.cc

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ClearErrorOnReturn::~ClearErrorOnReturn() {
3333
ERR_clear_error();
3434
}
3535

36-
int ClearErrorOnReturn::peeKError() { return ERR_peek_error(); }
36+
int ClearErrorOnReturn::peekError() { return ERR_peek_error(); }
3737

3838
MarkPopErrorOnReturn::MarkPopErrorOnReturn(CryptoErrorList* errors) : errors_(errors) {
3939
ERR_set_mark();
@@ -1570,11 +1570,66 @@ EVPKeyPointer::ParseKeyResult TryParsePublicKeyInner(
15701570
return EVPKeyPointer::ParseKeyResult(std::move(pkey));
15711571
}
15721572

1573-
EVPKeyPointer::ParseKeyResult TryParsePublicKeyPEM(
1573+
constexpr bool IsASN1Sequence(const unsigned char* data, size_t size,
1574+
size_t* data_offset, size_t* data_size) {
1575+
if (size < 2 || data[0] != 0x30)
1576+
return false;
1577+
1578+
if (data[1] & 0x80) {
1579+
// Long form.
1580+
size_t n_bytes = data[1] & ~0x80;
1581+
if (n_bytes + 2 > size || n_bytes > sizeof(size_t))
1582+
return false;
1583+
size_t length = 0;
1584+
for (size_t i = 0; i < n_bytes; i++)
1585+
length = (length << 8) | data[i + 2];
1586+
*data_offset = 2 + n_bytes;
1587+
*data_size = std::min(size - 2 - n_bytes, length);
1588+
} else {
1589+
// Short form.
1590+
*data_offset = 2;
1591+
*data_size = std::min<size_t>(size - 2, data[1]);
1592+
}
1593+
1594+
return true;
1595+
}
1596+
1597+
constexpr bool IsEncryptedPrivateKeyInfo(const Buffer<const unsigned char>& buffer) {
1598+
// Both PrivateKeyInfo and EncryptedPrivateKeyInfo start with a SEQUENCE.
1599+
if (buffer.len == 0 || buffer.data == nullptr) return false;
1600+
size_t offset, len;
1601+
if (!IsASN1Sequence(buffer.data, buffer.len, &offset, &len))
1602+
return false;
1603+
1604+
// A PrivateKeyInfo sequence always starts with an integer whereas an
1605+
// EncryptedPrivateKeyInfo starts with an AlgorithmIdentifier.
1606+
return len >= 1 && buffer.data[offset] != 2;
1607+
}
1608+
1609+
} // namespace
1610+
1611+
bool EVPKeyPointer::IsRSAPrivateKey(const Buffer<const unsigned char>& buffer) {
1612+
// Both RSAPrivateKey and RSAPublicKey structures start with a SEQUENCE.
1613+
size_t offset, len;
1614+
if (!IsASN1Sequence(buffer.data, buffer.len, &offset, &len))
1615+
return false;
1616+
1617+
// An RSAPrivateKey sequence always starts with a single-byte integer whose
1618+
// value is either 0 or 1, whereas an RSAPublicKey starts with the modulus
1619+
// (which is the product of two primes and therefore at least 4), so we can
1620+
// decide the type of the structure based on the first three bytes of the
1621+
// sequence.
1622+
return len >= 3 &&
1623+
buffer.data[offset] == 2 &&
1624+
buffer.data[offset + 1] == 1 &&
1625+
!(buffer.data[offset + 2] & 0xfe);
1626+
}
1627+
1628+
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKeyPEM(
15741629
const Buffer<const unsigned char>& buffer) {
15751630
auto bp = BIOPointer::New(buffer.data, buffer.len);
15761631
if (!bp)
1577-
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::FAILED);
1632+
return ParseKeyResult(PKParseError::FAILED);
15781633

15791634
// Try parsing as SubjectPublicKeyInfo (SPKI) first.
15801635
if (auto ret = TryParsePublicKeyInner(bp, "PUBLIC KEY",
@@ -1601,9 +1656,8 @@ EVPKeyPointer::ParseKeyResult TryParsePublicKeyPEM(
16011656
return ret;
16021657
};
16031658

1604-
return EVPKeyPointer::ParseKeyResult(EVPKeyPointer::PKParseError::NOT_RECOGNIZED);
1659+
return ParseKeyResult(PKParseError::NOT_RECOGNIZED);
16051660
}
1606-
} // namespace
16071661

16081662
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
16091663
PKFormatType format,
@@ -1634,4 +1688,70 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey(
16341688
return ParseKeyResult(PKParseError::FAILED);
16351689
}
16361690

1691+
EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey(
1692+
PKFormatType format,
1693+
PKEncodingType encoding,
1694+
std::optional<Buffer<char>> maybe_passphrase,
1695+
const Buffer<const unsigned char>& buffer) {
1696+
1697+
static auto keyOrError = [&](EVPKeyPointer pkey, bool had_passphrase = false) {
1698+
if (int err = ERR_peek_error()) {
1699+
if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
1700+
ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ &&
1701+
!had_passphrase) {
1702+
return ParseKeyResult(PKParseError::NEED_PASSPHRASE);
1703+
}
1704+
return ParseKeyResult(PKParseError::FAILED, err);
1705+
}
1706+
if (!pkey) return ParseKeyResult(PKParseError::FAILED);
1707+
return ParseKeyResult(std::move(pkey));
1708+
};
1709+
1710+
Buffer<char>* passphrase = nullptr;
1711+
if (maybe_passphrase.has_value()) {
1712+
passphrase = &maybe_passphrase.value();
1713+
}
1714+
1715+
auto bio = BIOPointer::New(buffer);
1716+
if (!bio) return ParseKeyResult(PKParseError::FAILED);
1717+
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());
1721+
}
1722+
1723+
if (format != PKFormatType::DER) {
1724+
return ParseKeyResult(PKParseError::FAILED);
1725+
}
1726+
1727+
switch (encoding) {
1728+
case PKEncodingType::PKCS1: {
1729+
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
1730+
return keyOrError(EVPKeyPointer(key));
1731+
}
1732+
case PKEncodingType::PKCS8: {
1733+
if (IsEncryptedPrivateKeyInfo(buffer)) {
1734+
auto key = d2i_PKCS8PrivateKey_bio(bio.get(),
1735+
nullptr,
1736+
PasswordCallback,
1737+
passphrase);
1738+
return keyOrError(EVPKeyPointer(key), maybe_passphrase.has_value());
1739+
}
1740+
1741+
PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr));
1742+
if (!p8inf) {
1743+
return ParseKeyResult(PKParseError::FAILED, ERR_peek_error());
1744+
}
1745+
return keyOrError(EVPKeyPointer(EVP_PKCS82PKEY(p8inf.get())));
1746+
}
1747+
case PKEncodingType::SEC1: {
1748+
auto key = d2i_PrivateKey_bio(bio.get(), nullptr);
1749+
return keyOrError(EVPKeyPointer(key));
1750+
}
1751+
default: {
1752+
return ParseKeyResult(PKParseError::FAILED, ERR_peek_error());
1753+
}
1754+
};
1755+
}
1756+
16371757
} // namespace ncrypto

deps/ncrypto/ncrypto.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class ClearErrorOnReturn final {
148148
NCRYPTO_DISALLOW_COPY_AND_MOVE(ClearErrorOnReturn)
149149
NCRYPTO_DISALLOW_NEW_DELETE()
150150

151-
int peeKError();
151+
int peekError();
152152

153153
private:
154154
CryptoErrorList* errors_;
@@ -178,9 +178,13 @@ template <typename T, typename E>
178178
struct Result final {
179179
const bool has_value;
180180
T value;
181-
std::optional<E> error;
181+
std::optional<E> error = std::nullopt;
182+
std::optional<int> openssl_error = std::nullopt;
182183
Result(T&& value) : has_value(true), value(std::move(value)) {}
183-
Result(E&& error) : has_value(false), error(std::move(error)) {}
184+
Result(E&& error, std::optional<int> openssl_error = std::nullopt)
185+
: has_value(false),
186+
error(std::move(error)),
187+
openssl_error(std::move(openssl_error)) {}
184188
inline operator bool() const { return has_value; }
185189
};
186190

@@ -277,6 +281,11 @@ class BIOPointer final {
277281
static BIOPointer NewFile(std::string_view filename, std::string_view mode);
278282
static BIOPointer NewFp(FILE* fd, int flags);
279283

284+
template <typename T>
285+
static BIOPointer New(const Buffer<T>& buf) {
286+
return New(buf.data, buf.len);
287+
}
288+
280289
BIOPointer() = default;
281290
BIOPointer(std::nullptr_t) : bio_(nullptr) {}
282291
explicit BIOPointer(BIO* bio);
@@ -398,6 +407,15 @@ class EVPKeyPointer final {
398407
PKEncodingType encoding,
399408
const Buffer<const unsigned char>& buffer);
400409

410+
static ParseKeyResult TryParsePublicKeyPEM(
411+
const Buffer<const unsigned char>& buffer);
412+
413+
static ParseKeyResult TryParsePrivateKey(
414+
PKFormatType format,
415+
PKEncodingType encoding,
416+
std::optional<Buffer<char>> passphrase,
417+
const Buffer<const unsigned char>& buffer);
418+
401419
EVPKeyPointer() = default;
402420
explicit EVPKeyPointer(EVP_PKEY* pkey);
403421
EVPKeyPointer(EVPKeyPointer&& other) noexcept;
@@ -428,6 +446,8 @@ class EVPKeyPointer final {
428446

429447
EVPKeyCtxPointer newCtx() const;
430448

449+
static bool IsRSAPrivateKey(const Buffer<const unsigned char>& buffer);
450+
431451
private:
432452
DeleteFnPtr<EVP_PKEY, EVP_PKEY_free> pkey_;
433453
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy