diff --git a/js/ccf-app/src/global.ts b/js/ccf-app/src/global.ts index 33db097ba2fe..c47b037cedf7 100644 --- a/js/ccf-app/src/global.ts +++ b/js/ccf-app/src/global.ts @@ -231,7 +231,7 @@ export interface CryptoKeyPair { publicKey: string; } -export type AlgorithmName = "RSASSA-PKCS1-v1_5" | "ECDSA" | "EdDSA" | "HMAC"; +export type AlgorithmName = "RSASSA-PSS" | "ECDSA" | "EdDSA" | "HMAC"; export type DigestAlgorithm = "SHA-256" | "SHA-384" | "SHA-512"; @@ -239,7 +239,7 @@ export interface SigningAlgorithm { name: AlgorithmName; /** - * Digest algorithm. It's necessary for "RSASSA-PKCS1-v1_5", "ECDSA", and "HMAC" + * Digest algorithm. It's necessary for "RSASSA-PSS", "ECDSA", and "HMAC" */ hash?: DigestAlgorithm; } diff --git a/js/ccf-app/src/polyfill.ts b/js/ccf-app/src/polyfill.ts index 949ca1e2fdd2..0e1b62149dba 100644 --- a/js/ccf-app/src/polyfill.ts +++ b/js/ccf-app/src/polyfill.ts @@ -142,8 +142,8 @@ class CCFPolyfill implements CCF { let padding = undefined; const privKey = jscrypto.createPrivateKey(key); if (privKey.asymmetricKeyType == "rsa") { - if (algorithm.name === "RSASSA-PKCS1-v1_5") { - padding = jscrypto.constants.RSA_PKCS1_PADDING; + if (algorithm.name === "RSASSA-PSS") { + padding = jscrypto.constants.RSA_PKCS1_PSS_PADDING; } else { throw new Error("incompatible signing algorithm for given key type"); } @@ -168,6 +168,7 @@ class CCFPolyfill implements CCF { key: privKey, dsaEncoding: "ieee-p1363", padding: padding, + saltLength: jscrypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, }); }, verifySignature( @@ -179,8 +180,8 @@ class CCFPolyfill implements CCF { let padding = undefined; const pubKey = jscrypto.createPublicKey(key); if (pubKey.asymmetricKeyType == "rsa") { - if (algorithm.name === "RSASSA-PKCS1-v1_5") { - padding = jscrypto.constants.RSA_PKCS1_PADDING; + if (algorithm.name === "RSASSA-PSS") { + padding = jscrypto.constants.RSA_PKCS1_PSS_PADDING; } else { throw new Error("incompatible signing algorithm for given key type"); } @@ -211,6 +212,7 @@ class CCFPolyfill implements CCF { key: pubKey, dsaEncoding: "ieee-p1363", padding: padding, + saltLength: jscrypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, }, new Uint8Array(signature), ); diff --git a/js/ccf-app/test/polyfill.test.ts b/js/ccf-app/test/polyfill.test.ts index ef4ddf430325..b3b7362e31e3 100644 --- a/js/ccf-app/test/polyfill.test.ts +++ b/js/ccf-app/test/polyfill.test.ts @@ -167,7 +167,7 @@ describe("polyfill", function () { }); }); describe("sign", function () { - it("performs RSASSA-PKCS1-v1_5 sign correctly", function () { + it("performs RSASSA-PSS sign correctly", function () { const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", { modulusLength: 2048, publicKeyEncoding: { @@ -182,7 +182,7 @@ describe("polyfill", function () { const data = ccf.strToBuf("foo"); const signature = ccf.crypto.sign( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, privateKey, @@ -198,6 +198,8 @@ describe("polyfill", function () { { key: publicKey, dsaEncoding: "ieee-p1363", + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, }, new Uint8Array(signature), ), @@ -208,7 +210,7 @@ describe("polyfill", function () { assert.isTrue( ccf.crypto.verifySignature( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, publicKey, @@ -392,7 +394,7 @@ describe("polyfill", function () { }); }); describe("verifySignature", function () { - it("performs RSASSA-PKCS1-v1_5 validation correctly", function () { + it("performs RSASSA-PSS validation correctly", function () { const { cert, publicKey, privateKey } = generateSelfSignedCert(); const signer = crypto.createSign("sha256"); const data = ccf.strToBuf("foo"); @@ -400,12 +402,13 @@ describe("polyfill", function () { signer.end(); const signature = signer.sign({ key: crypto.createPrivateKey(privateKey), - padding: crypto.constants.RSA_PKCS1_PADDING, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, }); assert.isTrue( ccf.crypto.verifySignature( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, cert, @@ -416,7 +419,7 @@ describe("polyfill", function () { assert.isTrue( ccf.crypto.verifySignature( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, publicKey, @@ -427,7 +430,7 @@ describe("polyfill", function () { assert.isNotTrue( ccf.crypto.verifySignature( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, cert, @@ -494,7 +497,7 @@ describe("polyfill", function () { assert.throws(() => ccf.crypto.verifySignature( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, publicKey, @@ -543,7 +546,7 @@ describe("polyfill", function () { assert.throws(() => ccf.crypto.verifySignature( { - name: "RSASSA-PKCS1-v1_5", + name: "RSASSA-PSS", hash: "SHA-256", }, publicKey, diff --git a/src/crypto/openssl/key_pair.cpp b/src/crypto/openssl/key_pair.cpp index 7bbf3e7a3c2e..707cecf33348 100644 --- a/src/crypto/openssl/key_pair.cpp +++ b/src/crypto/openssl/key_pair.cpp @@ -202,6 +202,7 @@ namespace ccf::crypto { Unique_EVP_PKEY_CTX pctx(key); OpenSSL::CHECK1(EVP_PKEY_sign_init(pctx)); + OpenSSL::CHECK1(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)); OpenSSL::CHECK1(EVP_PKEY_sign(pctx, sig, sig_size, hash, hash_size)); return 0; } diff --git a/src/crypto/openssl/public_key.cpp b/src/crypto/openssl/public_key.cpp index 6e1afaefef2a..69b9a793dd9e 100644 --- a/src/crypto/openssl/public_key.cpp +++ b/src/crypto/openssl/public_key.cpp @@ -227,6 +227,8 @@ namespace ccf::crypto Unique_EVP_PKEY_CTX pctx(key); OpenSSL::CHECK1(EVP_PKEY_verify_init(pctx)); + CHECK1(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)); + if (md_type != MDType::NONE) { OpenSSL::CHECK1( diff --git a/src/crypto/openssl/rsa_key_pair.cpp b/src/crypto/openssl/rsa_key_pair.cpp index 4c31ddcbfd0f..34b5d424af79 100644 --- a/src/crypto/openssl/rsa_key_pair.cpp +++ b/src/crypto/openssl/rsa_key_pair.cpp @@ -211,6 +211,7 @@ namespace ccf::crypto auto hash = OpenSSLHashProvider().Hash(d.data(), d.size(), md_type); Unique_EVP_PKEY_CTX pctx(key); CHECK1(EVP_PKEY_sign_init(pctx)); + CHECK1(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)); CHECK1(EVP_PKEY_CTX_set_signature_md(pctx, get_md_type(md_type))); size_t olen = r.size(); CHECK1(EVP_PKEY_sign(pctx, r.data(), &olen, hash.data(), hash.size())); diff --git a/src/crypto/openssl/rsa_public_key.cpp b/src/crypto/openssl/rsa_public_key.cpp index e508b10e4454..b96f24fd2fa3 100644 --- a/src/crypto/openssl/rsa_public_key.cpp +++ b/src/crypto/openssl/rsa_public_key.cpp @@ -200,6 +200,7 @@ namespace ccf::crypto auto hash = OpenSSLHashProvider().Hash(contents, contents_size, md_type); Unique_EVP_PKEY_CTX pctx(key); CHECK1(EVP_PKEY_verify_init(pctx)); + CHECK1(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)); CHECK1(EVP_PKEY_CTX_set_signature_md(pctx, get_md_type(md_type))); return EVP_PKEY_verify( pctx, signature, signature_size, hash.data(), hash.size()) == 1; diff --git a/src/js/extensions/ccf/crypto.cpp b/src/js/extensions/ccf/crypto.cpp index abd1d935828e..1bec963fc447 100644 --- a/src/js/extensions/ccf/crypto.cpp +++ b/src/js/extensions/ccf/crypto.cpp @@ -884,7 +884,7 @@ namespace ccf::js::extensions sig_der, key_pair->get_curve_id()); return JS_NewArrayBufferCopy(ctx, sig.data(), sig.size()); } - else if (algo_name == "RSASSA-PKCS1-v1_5") + else if (algo_name == "RSASSA-PSS") { auto key_pair = ccf::crypto::make_rsa_key_pair(key); auto sig = key_pair->sign(contents, mdtype); @@ -900,8 +900,8 @@ namespace ccf::js::extensions { return JS_ThrowRangeError( ctx, - "Unsupported signing algorithm, supported: RSASSA-PKCS1-v1_5, " - "ECDSA, EdDSA, HMAC"); + "Unsupported signing algorithm, supported: RSASSA-PSS, ECDSA, " + "EdDSA, HMAC"); } } catch (const std::exception& ex) @@ -1010,12 +1010,12 @@ namespace ccf::js::extensions ctx, "Unsupported hash algorithm, supported: SHA-256"); } - if (algo_name != "RSASSA-PKCS1-v1_5" && algo_name != "ECDSA") + if (algo_name != "RSASSA-PSS" && algo_name != "ECDSA") { return JS_ThrowRangeError( ctx, - "Unsupported signing algorithm, supported: RSASSA-PKCS1-v1_5, " - "ECDSA, EdDSA"); + "Unsupported signing algorithm, supported: RSASSA-PSS, ECDSA, " + "EdDSA"); } std::vector sig(signature, signature + signature_size); diff --git a/tests/infra/crypto.py b/tests/infra/crypto.py index 6efb24ae267c..2aeac13d7202 100644 --- a/tests/infra/crypto.py +++ b/tests/infra/crypto.py @@ -213,8 +213,15 @@ def sign(algorithm: dict, key_pem: str, data: bytes) -> bytes: else: raise ValueError("Unsupported hash algorithm") if isinstance(key, rsa.RSAPrivateKey): - if algorithm["name"] == "RSASSA-PKCS1-v1_5": - return key.sign(data, padding.PKCS1v15(), hash_alg) + if algorithm["name"] == "RSASSA-PSS": + return key.sign( + data, + padding=padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH, + ), + algorithm=hash_alg, + ) else: raise ValueError("Unsupported signing algorithm") elif isinstance(key, ec.EllipticCurvePrivateKey): @@ -254,7 +261,15 @@ def verify_signature(algorithm: dict, signature: bytes, data: bytes, key_pub_pem if isinstance(key_pub, rsa.RSAPublicKey): if algorithm["hash"] != "SHA-256": raise ValueError("Unsupported hash algorithm") - key_pub.verify(signature, data, padding.PKCS1v15(), hashes.SHA256()) + key_pub.verify( + signature, + data, + padding.PSS( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + salt_length=padding.PSS.MAX_LENGTH, + ), + hashes.SHA256(), + ) elif isinstance(key_pub, ec.EllipticCurvePublicKey): if algorithm["hash"] != "SHA-256": raise ValueError("Unsupported hash algorithm") diff --git a/tests/npm_tests.py b/tests/npm_tests.py index 85cb37b5bab7..cf74d53e79e1 100644 --- a/tests/npm_tests.py +++ b/tests/npm_tests.py @@ -438,7 +438,7 @@ def test_npm_app(network, args): # Test RSA signing + verification key_priv_pem, key_pub_pem = infra.crypto.generate_rsa_keypair(2048) - algorithm = {"name": "RSASSA-PKCS1-v1_5", "hash": "SHA-256"} + algorithm = {"name": "RSASSA-PSS", "hash": "SHA-256"} data = rand_bytes(random.randint(2, 50)) r = c.post( "/app/sign", @@ -551,7 +551,7 @@ def test_npm_app(network, args): pass key_priv_pem, key_pub_pem = infra.crypto.generate_rsa_keypair(2048) - algorithm = {"name": "RSASSA-PKCS1-v1_5", "hash": "SHA-256"} + algorithm = {"name": "RSASSA-PSS", "hash": "SHA-256"} signature = infra.crypto.sign(algorithm, key_priv_pem, data) r = c.post( "/app/verifySignature",