GeezerVison is showing github.com — src: move hkdf, scrypto, pbkdf2 impl to ncrypto · nodejs/node@b89cd8d
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: deps/ncrypto/ncrypto.cc +132Lines changed: 132 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 4 | 4 | #include <openssl/dh.h> | 5 | 5 | #include <openssl/bn.h> | 6 | 6 | #include <openssl/evp.h> | | 7 | +#include <openssl/hmac.h> | 7 | 8 | #include <openssl/pkcs12.h> | 8 | 9 | #include <openssl/x509v3.h> | 9 | 10 | #if OPENSSL_VERSION_MAJOR >= 3 | @@ -1252,4 +1253,135 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, | 1252 | 1253 | return out; | 1253 | 1254 | } | 1254 | 1255 |
| | 1256 | +// ============================================================================ | | 1257 | +// KDF | | 1258 | + | | 1259 | +const EVP_MD* getDigestByName(const std::string_view name) { | | 1260 | + return EVP_get_digestbyname(name.data()); | | 1261 | +} | | 1262 | + | | 1263 | +bool checkHkdfLength(const EVP_MD* md, size_t length) { | | 1264 | + // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as | | 1265 | + // the output of the hash function. 255 is a hard limit because HKDF appends | | 1266 | + // an 8-bit counter to each HMAC'd message, starting at 1. | | 1267 | + static constexpr size_t kMaxDigestMultiplier = 255; | | 1268 | + size_t max_length = EVP_MD_size(md) * kMaxDigestMultiplier; | | 1269 | + if (length > max_length) return false; | | 1270 | + return true; | | 1271 | +} | | 1272 | + | | 1273 | +DataPointer hkdf(const EVP_MD* md, | | 1274 | + const Buffer<const unsigned char>& key, | | 1275 | + const Buffer<const unsigned char>& info, | | 1276 | + const Buffer<const unsigned char>& salt, | | 1277 | + size_t length) { | | 1278 | + ClearErrorOnReturn clearErrorOnReturn; | | 1279 | + | | 1280 | + if (!checkHkdfLength(md, length) || | | 1281 | + info.len > INT_MAX || | | 1282 | + salt.len > INT_MAX) { | | 1283 | + return {}; | | 1284 | + } | | 1285 | + | | 1286 | + EVPKeyCtxPointer ctx = | | 1287 | + EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); | | 1288 | + if (!ctx || | | 1289 | + !EVP_PKEY_derive_init(ctx.get()) || | | 1290 | + !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md) || | | 1291 | + !EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), info.data, info.len)) { | | 1292 | + return {}; | | 1293 | + } | | 1294 | + | | 1295 | + std::string_view actual_salt; | | 1296 | + static const char default_salt[EVP_MAX_MD_SIZE] = {0}; | | 1297 | + if (salt.len > 0) { | | 1298 | + actual_salt = {reinterpret_cast<const char*>(salt.data), salt.len}; | | 1299 | + } else { | | 1300 | + actual_salt = {default_salt, static_cast<unsigned>(EVP_MD_size(md))}; | | 1301 | + } | | 1302 | + | | 1303 | + // We do not use EVP_PKEY_HKDF_MODE_EXTRACT_AND_EXPAND because and instead | | 1304 | + // implement the extraction step ourselves because EVP_PKEY_derive does not | | 1305 | + // handle zero-length keys, which are required for Web Crypto. | | 1306 | + // TODO: Once OpenSSL 1.1.1 support is dropped completely, and once BoringSSL | | 1307 | + // is confirmed to support it, wen can hopefully drop this and use EVP_KDF | | 1308 | + // directly which does support zero length keys. | | 1309 | + unsigned char pseudorandom_key[EVP_MAX_MD_SIZE]; | | 1310 | + unsigned pseudorandom_key_len = sizeof(pseudorandom_key); | | 1311 | + | | 1312 | + if (HMAC(md, | | 1313 | + actual_salt.data(), | | 1314 | + actual_salt.size(), | | 1315 | + key.data, | | 1316 | + key.len, | | 1317 | + pseudorandom_key, | | 1318 | + &pseudorandom_key_len) == nullptr) { | | 1319 | + return {}; | | 1320 | + } | | 1321 | + if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(), EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) || | | 1322 | + !EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pseudorandom_key, pseudorandom_key_len)) { | | 1323 | + return {}; | | 1324 | + } | | 1325 | + | | 1326 | + auto buf = DataPointer::Alloc(length); | | 1327 | + if (!buf) return {}; | | 1328 | + | | 1329 | + if (EVP_PKEY_derive(ctx.get(), static_cast<unsigned char*>(buf.get()), &length) <= 0) { | | 1330 | + return {}; | | 1331 | + } | | 1332 | + | | 1333 | + return buf; | | 1334 | +} | | 1335 | + | | 1336 | +bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem) { | | 1337 | + return EVP_PBE_scrypt(nullptr, 0, nullptr, 0, N, r, p, maxmem, nullptr, 0) == 1; | | 1338 | +} | | 1339 | + | | 1340 | +DataPointer scrypt(const Buffer<const char>& pass, | | 1341 | + const Buffer<const unsigned char>& salt, | | 1342 | + uint64_t N, | | 1343 | + uint64_t r, | | 1344 | + uint64_t p, | | 1345 | + uint64_t maxmem, | | 1346 | + size_t length) { | | 1347 | + ClearErrorOnReturn clearErrorOnReturn; | | 1348 | + | | 1349 | + if (pass.len > INT_MAX || | | 1350 | + salt.len > INT_MAX) { | | 1351 | + return {}; | | 1352 | + } | | 1353 | + | | 1354 | + auto dp = DataPointer::Alloc(length); | | 1355 | + if (dp && EVP_PBE_scrypt( | | 1356 | + pass.data, pass.len, salt.data, salt.len, N, r, p, maxmem, | | 1357 | + reinterpret_cast<unsigned char*>(dp.get()), length)) { | | 1358 | + return dp; | | 1359 | + } | | 1360 | + | | 1361 | + return {}; | | 1362 | +} | | 1363 | + | | 1364 | +DataPointer pbkdf2(const EVP_MD* md, | | 1365 | + const Buffer<const char>& pass, | | 1366 | + const Buffer<const unsigned char>& salt, | | 1367 | + uint32_t iterations, | | 1368 | + size_t length) { | | 1369 | + ClearErrorOnReturn clearErrorOnReturn; | | 1370 | + | | 1371 | + if (pass.len > INT_MAX || | | 1372 | + salt.len > INT_MAX || | | 1373 | + length > INT_MAX) { | | 1374 | + return {}; | | 1375 | + } | | 1376 | + | | 1377 | + auto dp = DataPointer::Alloc(length); | | 1378 | + if (dp && PKCS5_PBKDF2_HMAC(pass.data, pass.len, salt.data, salt.len, | | 1379 | + iterations, md, length, | | 1380 | + reinterpret_cast<unsigned char*>(dp.get()))) { | | 1381 | + return dp; | | 1382 | + } | | 1383 | + | | 1384 | + return {}; | | 1385 | +} | | 1386 | + | 1255 | 1387 | } // namespace ncrypto |
Collapse file Copy file name to clipboardExpand all lines: deps/ncrypto/ncrypto.h +32Lines changed: 32 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -588,6 +588,38 @@ BIOPointer ExportPublicKey(const char* input, size_t length); | 588 | 588 | // The caller takes ownership of the returned Buffer<char> | 589 | 589 | Buffer<char> ExportChallenge(const char* input, size_t length); | 590 | 590 |
| | 591 | +// ============================================================================ | | 592 | +// KDF | | 593 | + | | 594 | +const EVP_MD* getDigestByName(const std::string_view name); | | 595 | + | | 596 | +// Verify that the specified HKDF output length is valid for the given digest. | | 597 | +// The maximum length for HKDF output for a given digest is 255 times the | | 598 | +// hash size for the given digest algorithm. | | 599 | +bool checkHkdfLength(const EVP_MD* md, size_t length); | | 600 | + | | 601 | +DataPointer hkdf(const EVP_MD* md, | | 602 | + const Buffer<const unsigned char>& key, | | 603 | + const Buffer<const unsigned char>& info, | | 604 | + const Buffer<const unsigned char>& salt, | | 605 | + size_t length); | | 606 | + | | 607 | +bool checkScryptParams(uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem); | | 608 | + | | 609 | +DataPointer scrypt(const Buffer<const char>& pass, | | 610 | + const Buffer<const unsigned char>& salt, | | 611 | + uint64_t N, | | 612 | + uint64_t r, | | 613 | + uint64_t p, | | 614 | + uint64_t maxmem, | | 615 | + size_t length); | | 616 | + | | 617 | +DataPointer pbkdf2(const EVP_MD* md, | | 618 | + const Buffer<const char>& pass, | | 619 | + const Buffer<const unsigned char>& salt, | | 620 | + uint32_t iterations, | | 621 | + size_t length); | | 622 | + | 591 | 623 | // ============================================================================ | 592 | 624 | // Version metadata | 593 | 625 | #define NCRYPTO_VERSION "0.0.1" | |
Collapse file Copy file name to clipboardExpand all lines: src/crypto/crypto_hkdf.cc +20-51Lines changed: 20 additions & 51 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -56,7 +56,7 @@ Maybe<bool> HKDFTraits::AdditionalConfig( | 56 | 56 | CHECK(args[offset + 4]->IsUint32()); // Length | 57 | 57 |
| 58 | 58 | Utf8Value hash(env->isolate(), args[offset]); | 59 | | - params->digest = EVP_get_digestbyname(*hash); | | 59 | + params->digest = ncrypto::getDigestByName(hash.ToStringView()); | 60 | 60 | if (params->digest == nullptr) { | 61 | 61 | THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash); | 62 | 62 | return Nothing<bool>(); | @@ -90,9 +90,7 @@ Maybe<bool> HKDFTraits::AdditionalConfig( | 90 | 90 | // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as the | 91 | 91 | // output of the hash function. 255 is a hard limit because HKDF appends an | 92 | 92 | // 8-bit counter to each HMAC'd message, starting at 1. | 93 | | - constexpr size_t kMaxDigestMultiplier = 255; | 94 | | - size_t max_length = EVP_MD_size(params->digest) * kMaxDigestMultiplier; | 95 | | - if (params->length > max_length) { | | 93 | + if (!ncrypto::checkHkdfLength(params->digest, params->length)) { | 96 | 94 | THROW_ERR_CRYPTO_INVALID_KEYLEN(env); | 97 | 95 | return Nothing<bool>(); | 98 | 96 | } | @@ -104,53 +102,24 @@ bool HKDFTraits::DeriveBits( | 104 | 102 | Environment* env, | 105 | 103 | const HKDFConfig& params, | 106 | 104 | ByteSource* out) { | 107 | | - EVPKeyCtxPointer ctx = | 108 | | - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); | 109 | | - if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || | 110 | | - !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), params.digest) || | 111 | | - !EVP_PKEY_CTX_add1_hkdf_info( | 112 | | - ctx.get(), params.info.data<unsigned char>(), params.info.size())) { | 113 | | - return false; | 114 | | - } | 115 | | - | 116 | | - // TODO(panva): Once support for OpenSSL 1.1.1 is dropped the whole | 117 | | - // of HKDFTraits::DeriveBits can be refactored to use | 118 | | - // EVP_KDF which does handle zero length key. | 119 | | - | 120 | | - std::string_view salt; | 121 | | - if (params.salt.size() != 0) { | 122 | | - salt = {params.salt.data<char>(), params.salt.size()}; | 123 | | - } else { | 124 | | - static const char default_salt[EVP_MAX_MD_SIZE] = {0}; | 125 | | - salt = {default_salt, static_cast<unsigned>(EVP_MD_size(params.digest))}; | 126 | | - } | 127 | | - | 128 | | - // We do not use EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND and instead implement | 129 | | - // the extraction step ourselves because EVP_PKEY_derive does not handle | 130 | | - // zero-length keys, which are required for Web Crypto. | 131 | | - unsigned char pseudorandom_key[EVP_MAX_MD_SIZE]; | 132 | | - unsigned int prk_len = sizeof(pseudorandom_key); | 133 | | - if (HMAC( | 134 | | - params.digest, | 135 | | - salt.data(), | 136 | | - salt.size(), | 137 | | - reinterpret_cast<const unsigned char*>(params.key->GetSymmetricKey()), | 138 | | - params.key->GetSymmetricKeySize(), | 139 | | - pseudorandom_key, | 140 | | - &prk_len) == nullptr) { | 141 | | - return false; | 142 | | - } | 143 | | - if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(), EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) || | 144 | | - !EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pseudorandom_key, prk_len)) { | 145 | | - return false; | 146 | | - } | 147 | | - | 148 | | - size_t length = params.length; | 149 | | - ByteSource::Builder buf(length); | 150 | | - if (EVP_PKEY_derive(ctx.get(), buf.data<unsigned char>(), &length) <= 0) | 151 | | - return false; | 152 | | - | 153 | | - *out = std::move(buf).release(); | | 105 | + auto dp = ncrypto::hkdf(params.digest, | | 106 | + ncrypto::Buffer<const unsigned char>{ | | 107 | + .data = reinterpret_cast<const unsigned char*>( | | 108 | + params.key->GetSymmetricKey()), | | 109 | + .len = params.key->GetSymmetricKeySize(), | | 110 | + }, | | 111 | + ncrypto::Buffer<const unsigned char>{ | | 112 | + .data = params.info.data<const unsigned char>(), | | 113 | + .len = params.info.size(), | | 114 | + }, | | 115 | + ncrypto::Buffer<const unsigned char>{ | | 116 | + .data = params.salt.data<const unsigned char>(), | | 117 | + .len = params.salt.size(), | | 118 | + }, | | 119 | + params.length); | | 120 | + if (!dp) return false; | | 121 | + | | 122 | + *out = ByteSource::Allocated(dp.release()); | 154 | 123 | return true; | 155 | 124 | } | 156 | 125 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/crypto/crypto_pbkdf2.cc +18-21Lines changed: 18 additions & 21 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -102,7 +102,7 @@ Maybe<bool> PBKDF2Traits::AdditionalConfig( | 102 | 102 | } | 103 | 103 |
| 104 | 104 | Utf8Value name(args.GetIsolate(), args[offset + 4]); | 105 | | - params->digest = EVP_get_digestbyname(*name); | | 105 | + params->digest = ncrypto::getDigestByName(name.ToStringView()); | 106 | 106 | if (params->digest == nullptr) { | 107 | 107 | THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name); | 108 | 108 | return Nothing<bool>(); | @@ -111,27 +111,24 @@ Maybe<bool> PBKDF2Traits::AdditionalConfig( | 111 | 111 | return Just(true); | 112 | 112 | } | 113 | 113 |
| 114 | | -bool PBKDF2Traits::DeriveBits( | 115 | | - Environment* env, | 116 | | - const PBKDF2Config& params, | 117 | | - ByteSource* out) { | 118 | | - ByteSource::Builder buf(params.length); | 119 | | - | | 114 | +bool PBKDF2Traits::DeriveBits(Environment* env, | | 115 | + const PBKDF2Config& params, | | 116 | + ByteSource* out) { | 120 | 117 | // Both pass and salt may be zero length here. | 121 | | - // The generated bytes are stored in buf, which is | 122 | | - // assigned to out on success. | 123 | | - | 124 | | - if (PKCS5_PBKDF2_HMAC(params.pass.data<char>(), | 125 | | - params.pass.size(), | 126 | | - params.salt.data<unsigned char>(), | 127 | | - params.salt.size(), | 128 | | - params.iterations, | 129 | | - params.digest, | 130 | | - params.length, | 131 | | - buf.data<unsigned char>()) <= 0) { | 132 | | - return false; | 133 | | - } | 134 | | - *out = std::move(buf).release(); | | 118 | + auto dp = ncrypto::pbkdf2(params.digest, | | 119 | + ncrypto::Buffer<const char>{ | | 120 | + .data = params.pass.data<const char>(), | | 121 | + .len = params.pass.size(), | | 122 | + }, | | 123 | + ncrypto::Buffer<const unsigned char>{ | | 124 | + .data = params.salt.data<unsigned char>(), | | 125 | + .len = params.salt.size(), | | 126 | + }, | | 127 | + params.iterations, | | 128 | + params.length); | | 129 | + | | 130 | + if (!dp) return false; | | 131 | + *out = ByteSource::Allocated(dp.release()); | 135 | 132 | return true; | 136 | 133 | } | 137 | 134 |
| |
|