mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-30 04:39:06 +00:00
LibCrypto+LibWeb: Check RSA keys validity on SubtleCrypto import_key
Some checks are pending
CI / Linux, x86_64, Fuzzers_CI, Clang (push) Waiting to run
CI / macOS, arm64, Sanitizer_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / Linux, x86_64, Fuzzers_CI, Clang (push) Waiting to run
CI / macOS, arm64, Sanitizer_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Fix various TODO by checking the validity of RSA keys when they are imported. Also add some internal tests since WPT doesn't seem to provide them.
This commit is contained in:
parent
1bf4e712ec
commit
4e747f525a
Notes:
github-actions[bot]
2025-06-25 00:23:02 +00:00
Author: https://github.com/devgianlu
Commit: 4e747f525a
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4950
Reviewed-by: https://github.com/ADKaster
Reviewed-by: https://github.com/alimpfard
Reviewed-by: https://github.com/konradekk
Reviewed-by: https://github.com/shannonbooth
5 changed files with 301 additions and 17 deletions
|
@ -172,7 +172,7 @@ ErrorOr<RSA::KeyPairType> RSA::generate_key_pair(size_t bits, UnsignedBigInteger
|
|||
OPENSSL_TRY(OSSL_PARAM_BLD_push_BN(params_bld, openssl_name, param##_bn.ptr())); \
|
||||
}
|
||||
|
||||
ErrorOr<OpenSSL_PKEY> RSA::public_key_to_openssl_pkey(PublicKeyType const& public_key)
|
||||
static ErrorOr<OpenSSL_PKEY> public_key_to_openssl_pkey(RSAPublicKey const& public_key)
|
||||
{
|
||||
auto ctx = TRY(OpenSSL_PKEY_CTX::wrap(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr)));
|
||||
|
||||
|
@ -193,7 +193,7 @@ ErrorOr<OpenSSL_PKEY> RSA::public_key_to_openssl_pkey(PublicKeyType const& publi
|
|||
return key;
|
||||
}
|
||||
|
||||
ErrorOr<OpenSSL_PKEY> RSA::private_key_to_openssl_pkey(PrivateKeyType const& private_key)
|
||||
static ErrorOr<OpenSSL_PKEY> private_key_to_openssl_pkey(RSAPrivateKey const& private_key)
|
||||
{
|
||||
auto ctx = TRY(OpenSSL_PKEY_CTX::wrap(EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr)));
|
||||
|
||||
|
@ -222,6 +222,125 @@ ErrorOr<OpenSSL_PKEY> RSA::private_key_to_openssl_pkey(PrivateKeyType const& pri
|
|||
|
||||
#undef OPENSSL_SET_KEY_PARAM_NOT_ZERO
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc3447.html#section-3.1
|
||||
ErrorOr<bool> RSAPublicKey::is_valid() const
|
||||
{
|
||||
// In a valid RSA public key, the RSA modulus n is a product of u
|
||||
// distinct odd primes r_i, i = 1, 2, ..., u, where u >= 2, and the RSA
|
||||
// public exponent e is an integer between 3 and n - 1 satisfying GCD(e,
|
||||
// \lambda(n)) = 1, where \lambda(n) = LCM(r_1 - 1, ..., r_u - 1).
|
||||
|
||||
if (!m_public_exponent.is_odd())
|
||||
return false;
|
||||
|
||||
if (m_public_exponent < 3 || m_public_exponent >= m_modulus)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc3447.html#section-3.2
|
||||
ErrorOr<bool> RSAPrivateKey::is_valid() const
|
||||
{
|
||||
if (!m_public_exponent.is_odd())
|
||||
return false;
|
||||
|
||||
if (m_public_exponent < 3 || m_public_exponent >= m_modulus)
|
||||
return false;
|
||||
|
||||
if (!m_prime_1.is_zero() && !m_prime_2.is_zero() && !m_exponent_1.is_zero() && !m_exponent_2.is_zero() && !m_coefficient.is_zero()) {
|
||||
// In a valid RSA private key with the second representation, the two
|
||||
// factors p and q are the first two prime factors of the RSA modulus n
|
||||
// (i.e., r_1 and r_2), the CRT exponents dP and dQ are positive
|
||||
// integers less than p and q respectively satisfying
|
||||
// e * dP == 1 (mod (p-1))
|
||||
// e * dQ == 1 (mod (q-1)) ,
|
||||
// and the CRT coefficient qInv is a positive integer less than p
|
||||
// satisfying
|
||||
// q * qInv == 1 (mod p).
|
||||
// If u > 2, the representation will include one or more triplets (r_i,
|
||||
// d_i, t_i), i = 3, ..., u. The factors r_i are the additional prime
|
||||
// factors of the RSA modulus n. Each CRT exponent d_i (i = 3, ..., u)
|
||||
// satisfies
|
||||
// e * d_i == 1 (mod (r_i - 1)).
|
||||
// Each CRT coefficient t_i (i = 3, ..., u) is a positive integer less
|
||||
// than r_i satisfying
|
||||
// R_i * t_i == 1 (mod r_i) ,
|
||||
// where R_i = r_1 * r_2 * ... * r_(i-1).
|
||||
|
||||
if (m_exponent_1 >= m_prime_1 || m_exponent_2 >= m_prime_2 || m_coefficient >= m_prime_1)
|
||||
return false;
|
||||
|
||||
if (m_prime_1.multiplied_by(m_prime_2) != m_modulus)
|
||||
return false;
|
||||
|
||||
auto tmp_bn = TRY(OpenSSL_BN::create());
|
||||
|
||||
auto e = TRY(unsigned_big_integer_to_openssl_bignum(m_public_exponent)),
|
||||
p = TRY(unsigned_big_integer_to_openssl_bignum(m_prime_1)),
|
||||
q = TRY(unsigned_big_integer_to_openssl_bignum(m_prime_2));
|
||||
|
||||
auto dp = TRY(unsigned_big_integer_to_openssl_bignum(m_exponent_1)),
|
||||
dq = TRY(unsigned_big_integer_to_openssl_bignum(m_exponent_2));
|
||||
|
||||
auto* bn_ctx = OPENSSL_TRY_PTR(BN_CTX_new());
|
||||
ScopeGuard const free_bn_ctx = [&] { BN_CTX_free(bn_ctx); };
|
||||
|
||||
auto p1 = TRY(OpenSSL_BN::create());
|
||||
OPENSSL_TRY(BN_sub(p1.ptr(), p.ptr(), BN_value_one()));
|
||||
|
||||
OPENSSL_TRY(BN_mod_mul(tmp_bn.ptr(), e.ptr(), dp.ptr(), p1.ptr(), bn_ctx));
|
||||
if (!BN_is_one(tmp_bn.ptr()))
|
||||
return false;
|
||||
|
||||
auto q1 = TRY(OpenSSL_BN::create());
|
||||
OPENSSL_TRY(BN_sub(q1.ptr(), q.ptr(), BN_value_one()));
|
||||
|
||||
OPENSSL_TRY(BN_mod_mul(tmp_bn.ptr(), e.ptr(), dq.ptr(), q1.ptr(), bn_ctx));
|
||||
if (!BN_is_one(tmp_bn.ptr()))
|
||||
return false;
|
||||
|
||||
auto q_inv = TRY(unsigned_big_integer_to_openssl_bignum(m_coefficient));
|
||||
OPENSSL_TRY(BN_mod_mul(tmp_bn.ptr(), q.ptr(), q_inv.ptr(), p.ptr(), bn_ctx));
|
||||
if (!BN_is_one(tmp_bn.ptr()))
|
||||
return false;
|
||||
|
||||
if (!m_private_exponent.is_zero()) {
|
||||
if (m_private_exponent >= m_modulus)
|
||||
return false;
|
||||
|
||||
auto lambda = TRY(m_prime_1.minus(1)).lcm(TRY(m_prime_2.minus(1)));
|
||||
auto lambda_bn = TRY(unsigned_big_integer_to_openssl_bignum(lambda));
|
||||
|
||||
auto d = TRY(unsigned_big_integer_to_openssl_bignum(m_private_exponent));
|
||||
|
||||
OPENSSL_TRY(BN_mod_mul(tmp_bn.ptr(), d.ptr(), e.ptr(), lambda_bn.ptr(), bn_ctx));
|
||||
if (!BN_is_one(tmp_bn.ptr()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_modulus.is_zero() && !m_private_exponent.is_zero()) {
|
||||
// In a valid RSA private key with the first representation, the RSA
|
||||
// modulus n is the same as in the corresponding RSA public key and is
|
||||
// the product of u distinct odd primes r_i, i = 1, 2, ..., u, where u
|
||||
// >= 2. The RSA private exponent d is a positive integer less than n
|
||||
// satisfying
|
||||
// e * d == 1 (mod \lambda(n)),
|
||||
// where e is the corresponding RSA public exponent and \lambda(n) is
|
||||
// defined as in Section 3.1.
|
||||
|
||||
if (m_private_exponent >= m_modulus)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ErrorOr<void> RSA::configure(OpenSSL_PKEY_CTX& ctx)
|
||||
{
|
||||
OPENSSL_TRY(EVP_PKEY_CTX_set_rsa_padding(ctx.ptr(), RSA_NO_PADDING));
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
UnsignedBigInteger const& public_exponent() const { return m_public_exponent; }
|
||||
size_t length() const { return m_length; }
|
||||
|
||||
ErrorOr<bool> is_valid() const;
|
||||
|
||||
ErrorOr<ByteBuffer> export_as_der() const
|
||||
{
|
||||
ASN1::Encoder encoder;
|
||||
|
@ -88,6 +90,8 @@ public:
|
|||
UnsignedBigInteger const& coefficient() const { return m_coefficient; }
|
||||
size_t length() const { return m_length; }
|
||||
|
||||
ErrorOr<bool> is_valid() const;
|
||||
|
||||
ErrorOr<ByteBuffer> export_as_der() const
|
||||
{
|
||||
if (m_prime_1.is_zero() || m_prime_2.is_zero()) {
|
||||
|
@ -196,9 +200,6 @@ public:
|
|||
|
||||
protected:
|
||||
virtual ErrorOr<void> configure(OpenSSL_PKEY_CTX& ctx);
|
||||
|
||||
static ErrorOr<OpenSSL_PKEY> public_key_to_openssl_pkey(PublicKeyType const& public_key);
|
||||
static ErrorOr<OpenSSL_PKEY> private_key_to_openssl_pkey(PrivateKeyType const& private_key);
|
||||
};
|
||||
|
||||
ErrorOr<EVP_MD const*> hash_kind_to_hash_type(Hash::HashKind hash_kind);
|
||||
|
|
|
@ -837,7 +837,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAOAEP::import_key(Web::Crypto::Algorit
|
|||
|
||||
// 6. If an error occurred while parsing, or it can be determined that publicKey is not
|
||||
// a valid public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the public key
|
||||
auto maybe_valid = public_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 7. Let key be a new CryptoKey that represents the RSA public key identified by publicKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
|
||||
|
@ -874,7 +878,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAOAEP::import_key(Web::Crypto::Algorit
|
|||
|
||||
// 6. If an error occurred while parsing, or if rsaPrivateKey is not
|
||||
// a valid RSA private key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the private key
|
||||
auto maybe_valid = rsa_private_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 7. Let key be a new CryptoKey that represents the RSA private key identified by rsaPrivateKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { rsa_private_key });
|
||||
|
@ -989,7 +997,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAOAEP::import_key(Web::Crypto::Algorit
|
|||
auto private_key = TRY(parse_jwk_rsa_private_key(realm, jwk));
|
||||
|
||||
// 3. If privateKey can be determined to not be a valid RSA private key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the private key
|
||||
auto maybe_valid = private_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing privateKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key });
|
||||
|
@ -1008,7 +1020,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAOAEP::import_key(Web::Crypto::Algorit
|
|||
auto public_key = TRY(parse_jwk_rsa_public_key(realm, jwk));
|
||||
|
||||
// 3. If publicKey can be determined to not be a valid RSA public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the public key
|
||||
auto maybe_valid = public_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing publicKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
|
||||
|
@ -1414,7 +1430,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAPSS::import_key(AlgorithmParams const
|
|||
|
||||
// 6. If an error occurred while parsing, or it can be determined that publicKey is not
|
||||
// a valid public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the public key
|
||||
auto maybe_valid = public_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 7. Let key be a new CryptoKey that represents the RSA public key identified by publicKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
|
||||
|
@ -1451,7 +1471,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAPSS::import_key(AlgorithmParams const
|
|||
|
||||
// 6. If an error occurred while parsing, or if rsaPrivateKey is not
|
||||
// a valid RSA private key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the private key
|
||||
auto maybe_valid = rsa_private_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 7. Let key be a new CryptoKey that represents the RSA private key identified by rsaPrivateKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { rsa_private_key });
|
||||
|
@ -1567,7 +1591,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAPSS::import_key(AlgorithmParams const
|
|||
|
||||
// FIXME: Spec error, it should say 'not to be a valid RSA private key'
|
||||
// 3. If privateKey can be determined to not be a valid RSA public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the private key
|
||||
auto maybe_valid = private_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing privateKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key });
|
||||
|
@ -1586,7 +1614,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSAPSS::import_key(AlgorithmParams const
|
|||
auto public_key = TRY(parse_jwk_rsa_public_key(realm, jwk));
|
||||
|
||||
// 3. If publicKey can be determined to not be a valid RSA public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the public key
|
||||
auto maybe_valid = public_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing publicKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
|
||||
|
@ -1987,7 +2019,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSASSAPKCS1::import_key(AlgorithmParams
|
|||
|
||||
// 6. If an error occurred while parsing, or it can be determined that publicKey is not
|
||||
// a valid public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the public key
|
||||
auto maybe_valid = public_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 7. Let key be a new CryptoKey that represents the RSA public key identified by publicKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
|
||||
|
@ -2024,7 +2060,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSASSAPKCS1::import_key(AlgorithmParams
|
|||
|
||||
// 6. If an error occurred while parsing, or if rsaPrivateKey is not
|
||||
// a valid RSA private key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the private key
|
||||
auto maybe_valid = rsa_private_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 7. Let key be a new CryptoKey that represents the RSA private key identified by rsaPrivateKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { rsa_private_key });
|
||||
|
@ -2138,7 +2178,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSASSAPKCS1::import_key(AlgorithmParams
|
|||
auto private_key = TRY(parse_jwk_rsa_private_key(realm, jwk));
|
||||
|
||||
// 3. If privateKey can be determined to not be a valid RSA private key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the private key
|
||||
auto maybe_valid = private_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing privateKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key });
|
||||
|
@ -2157,7 +2201,11 @@ WebIDL::ExceptionOr<GC::Ref<CryptoKey>> RSASSAPKCS1::import_key(AlgorithmParams
|
|||
auto public_key = TRY(parse_jwk_rsa_public_key(realm, jwk));
|
||||
|
||||
// 3. If publicKey can be determined to not be a valid RSA public key according to [RFC3447], then throw a DataError.
|
||||
// FIXME: Validate the public key
|
||||
auto maybe_valid = public_key.is_valid();
|
||||
if (maybe_valid.is_error())
|
||||
return WebIDL::DataError::create(m_realm, "Failed to verify key"_string);
|
||||
if (!maybe_valid.value())
|
||||
return WebIDL::DataError::create(m_realm, "Invalid key"_string);
|
||||
|
||||
// 4. Let key be a new CryptoKey representing publicKey.
|
||||
key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key });
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
RSASSA-PKCS1-v1_5 SHA-1 PRIV - jwk OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-1 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-1 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-256 PRIV - jwk OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-256 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-256 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-384 PRIV - jwk OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-384 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-384 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-512 PRIV - jwk OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-512 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSASSA-PKCS1-v1_5 SHA-512 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-1 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-PSS SHA-1 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-1 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-256 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-PSS SHA-256 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-256 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-384 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-PSS SHA-384 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-384 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-512 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-PSS SHA-512 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-PSS SHA-512 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-1 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-1 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-1 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-256 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-256 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-256 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-384 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-384 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-384 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-512 PRIV - jwk OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-512 PRIV - pkcs8 OK: DataError: Invalid key
|
||||
RSA-OAEP SHA-512 PRIV - pkcs8 OK: DataError: Invalid key
|
|
@ -0,0 +1,80 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
function b64ToBn(b64) {
|
||||
const bin = atob(b64.replace(/-/g, '+').replace(/_/g, '/'));
|
||||
const hex = bin.split('').map((ch) => ch.charCodeAt(0).toString(16).padStart(2, '0')).join('');
|
||||
return [BigInt('0x' + hex), bin.length];
|
||||
}
|
||||
|
||||
function bnToB64(bn, len) {
|
||||
const hex = bn.toString(16);
|
||||
const bin = hex.match(/.{1,2}/g).map((byte) => String.fromCharCode(parseInt(byte, 16))).join('');
|
||||
return btoa(bin.padStart(len, '\x00')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
function corruptJwk(key) {
|
||||
const [bn, len] = b64ToBn(key.d);
|
||||
|
||||
// Corrupt the private key
|
||||
return {
|
||||
...key,
|
||||
d: bnToB64(bn ^ 1n, len),
|
||||
};
|
||||
}
|
||||
|
||||
function corruptPkcs8Pub(privateKey) {
|
||||
// Corrupt the public key, which is encoded between 33 and 165 for the PKCS8
|
||||
const corrupted = new Uint8Array(privateKey);
|
||||
corrupted[128] ^= 0x01;
|
||||
return corrupted.buffer;
|
||||
}
|
||||
|
||||
function corruptPkcs8Priv(privateKey) {
|
||||
// Corrupt a piece of the private key, which is encoded at the end of the PKCS8
|
||||
const corrupted = new Uint8Array(privateKey);
|
||||
corrupted[corrupted.length - 20] ^= 0x01;
|
||||
return corrupted.buffer;
|
||||
}
|
||||
|
||||
asyncTest(async done => {
|
||||
for (const [name, genUsages, privUsages] of [
|
||||
["RSASSA-PKCS1-v1_5", ["sign", "verify"], ["sign"]],
|
||||
["RSA-PSS", ["sign", "verify"], ["sign"]],
|
||||
["RSA-OAEP", ["encrypt", "decrypt"], ["decrypt"]],
|
||||
]) {
|
||||
for (const hash of ["SHA-1", "SHA-256", "SHA-384", "SHA-512"]) {
|
||||
const keyPair = await window.crypto.subtle.generateKey({
|
||||
name: name,
|
||||
hash: hash,
|
||||
modulusLength: 1024,
|
||||
publicExponent: new Uint8Array([1, 0, 1]),
|
||||
}, true, genUsages);
|
||||
|
||||
// NOTE: We don't check the public key for RSA since it's harder to corrupt.
|
||||
|
||||
for (const [format, corruptFn] of [["jwk", corruptJwk], ["pkcs8", corruptPkcs8Priv], ["pkcs8", corruptPkcs8Pub]]) {
|
||||
const privateKey = await window.crypto.subtle.exportKey(format, keyPair.privateKey);
|
||||
|
||||
// Prove that the original key can be imported successfully
|
||||
await window.crypto.subtle.importKey(format, privateKey, {name: name, hash: hash,}, false, privUsages);
|
||||
|
||||
// Corrupt the private key and try to import it
|
||||
const corruptedPrivateKey = corruptFn(privateKey);
|
||||
|
||||
try {
|
||||
await window.crypto.subtle.importKey(format, corruptedPrivateKey, {
|
||||
name: name,
|
||||
hash: hash,
|
||||
}, false, privUsages);
|
||||
println(`${name.padEnd(5, ' ')} ${hash} PRIV - ${format.padEnd(5, ' ')} FAILED`);
|
||||
} catch (e) {
|
||||
println(`${name.padEnd(5, ' ')} ${hash} PRIV - ${format.padEnd(5, ' ')} OK: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue