@@ -80,7 +80,7 @@ std::optional<std::string> CryptoErrorList::pop_front() { |
80 | 80 |
|
81 | 81 | // ============================================================================ |
82 | 82 | DataPointer DataPointer::Alloc(size_t len) { |
83 | | - return DataPointer(OPENSSL_malloc(len), len); |
| 83 | + return DataPointer(OPENSSL_zalloc(len), len); |
84 | 84 | } |
85 | 85 |
|
86 | 86 | DataPointer::DataPointer(void* data, size_t length) |
@@ -1428,6 +1428,33 @@ DataPointer pbkdf2(const EVP_MD* md, |
1428 | 1428 |
|
1429 | 1429 | // ============================================================================ |
1430 | 1430 |
|
| 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 | + |
1431 | 1458 | EVPKeyPointer EVPKeyPointer::New() { |
1432 | 1459 | return EVPKeyPointer(EVP_PKEY_new()); |
1433 | 1460 | } |
@@ -1661,41 +1688,61 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKeyPEM( |
1661 | 1688 | } |
1662 | 1689 |
|
1663 | 1690 | EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePublicKey( |
1664 | | - PKFormatType format, |
1665 | | - PKEncodingType encoding, |
| 1691 | + const PublicKeyEncodingConfig& config, |
1666 | 1692 | const Buffer<const unsigned char>& buffer) { |
1667 | | - if (format == PKFormatType::PEM) { |
| 1693 | + if (config.format == PKFormatType::PEM) { |
1668 | 1694 | return TryParsePublicKeyPEM(buffer); |
1669 | 1695 | } |
1670 | 1696 |
|
1671 | | - if (format != PKFormatType::DER) { |
| 1697 | + if (config.format != PKFormatType::DER) { |
1672 | 1698 | return ParseKeyResult(PKParseError::FAILED); |
1673 | 1699 | } |
1674 | 1700 |
|
1675 | 1701 | const unsigned char* start = buffer.data; |
1676 | 1702 |
|
1677 | 1703 | EVP_PKEY* key = nullptr; |
1678 | 1704 |
|
1679 | | - if (encoding == PKEncodingType::PKCS1 && |
| 1705 | + if (config.type == PKEncodingType::PKCS1 && |
1680 | 1706 | (key = d2i_PublicKey(EVP_PKEY_RSA, nullptr, &start, buffer.len))) { |
1681 | 1707 | return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key)); |
1682 | 1708 | } |
1683 | 1709 |
|
1684 | | - if (encoding == PKEncodingType::SPKI && |
| 1710 | + if (config.type == PKEncodingType::SPKI && |
1685 | 1711 | (key = d2i_PUBKEY(nullptr, &start, buffer.len))) { |
1686 | 1712 | return EVPKeyPointer::ParseKeyResult(EVPKeyPointer(key)); |
1687 | 1713 | } |
1688 | 1714 |
|
1689 | 1715 | return ParseKeyResult(PKParseError::FAILED); |
1690 | 1716 | } |
1691 | 1717 |
|
| 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 | + |
1692 | 1741 | EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey( |
1693 | | - PKFormatType format, |
1694 | | - PKEncodingType encoding, |
1695 | | - std::optional<Buffer<char>> maybe_passphrase, |
| 1742 | + const PrivateKeyEncodingConfig& config, |
1696 | 1743 | const Buffer<const unsigned char>& buffer) { |
1697 | 1744 |
|
1698 | | - static auto keyOrError = [&](EVPKeyPointer pkey, bool had_passphrase = false) { |
| 1745 | + static constexpr auto keyOrError = [](EVPKeyPointer pkey, bool had_passphrase = false) { |
1699 | 1746 | if (int err = ERR_peek_error()) { |
1700 | 1747 | if (ERR_GET_LIB(err) == ERR_LIB_PEM && |
1701 | 1748 | ERR_GET_REASON(err) == PEM_R_BAD_PASSWORD_READ && |
@@ -1708,24 +1755,23 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey( |
1708 | 1755 | return ParseKeyResult(std::move(pkey)); |
1709 | 1756 | }; |
1710 | 1757 |
|
1711 | | - Buffer<char>* passphrase = nullptr; |
1712 | | - if (maybe_passphrase.has_value()) { |
1713 | | - passphrase = &maybe_passphrase.value(); |
1714 | | - } |
1715 | 1758 |
|
1716 | 1759 | auto bio = BIOPointer::New(buffer); |
1717 | 1760 | if (!bio) return ParseKeyResult(PKParseError::FAILED); |
1718 | 1761 |
|
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()); |
1722 | 1768 | } |
1723 | 1769 |
|
1724 | | - if (format != PKFormatType::DER) { |
| 1770 | + if (config.format != PKFormatType::DER) { |
1725 | 1771 | return ParseKeyResult(PKParseError::FAILED); |
1726 | 1772 | } |
1727 | 1773 |
|
1728 | | - switch (encoding) { |
| 1774 | + switch (config.type) { |
1729 | 1775 | case PKEncodingType::PKCS1: { |
1730 | 1776 | auto key = d2i_PrivateKey_bio(bio.get(), nullptr); |
1731 | 1777 | return keyOrError(EVPKeyPointer(key)); |
@@ -1735,8 +1781,8 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey( |
1735 | 1781 | auto key = d2i_PKCS8PrivateKey_bio(bio.get(), |
1736 | 1782 | nullptr, |
1737 | 1783 | 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()); |
1740 | 1786 | } |
1741 | 1787 |
|
1742 | 1788 | PKCS8Pointer p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), nullptr)); |
@@ -1755,4 +1801,166 @@ EVPKeyPointer::ParseKeyResult EVPKeyPointer::TryParsePrivateKey( |
1755 | 1801 | }; |
1756 | 1802 | } |
1757 | 1803 |
|
| 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 | + |
1758 | 1966 | } // namespace ncrypto |