Hi all,
I can see a "RAW RSA" encoding method for signatures both implemented in mbed TLS and PSA API and I'm wondering about the motivations and rational of this variant. Could you tell more about it, please ? Below details.
Thierry
It was introduced since 2017 in mbed TLS in this sha1 ID : fdf38030de70b95a77205f17d65591f05e74be08 ("Outsource code for generating PKCS1 v1.5 encoding"). As far as I know, standard does not specifically mention it but RFC 8017 ($9.2) specifies EMSA-PKCS1-v1_5 as a deterministic encoding method (no randomness) with a buffer that is built as an encoded message EM = 0x00 || 0x01 || PS || 0x00 || T, where PS being padding bytes,
T being a DER encoding of a digestInfo, where digestInfo contains : - the hash function (so called AlgorithmIdentifier applied on the message) - and the hash value (so called digest H = = Hash(M))
PSA defines the RAW RSA in its API. It is said a signature algorithm, without hashing. In this raw RSA PKCS#1 v1.5, the hash parameter to psa_sign_hash is computed "externally". It is a DER encoding, “ready-to-use" hash, so called raw data in PSA and mbed TLS.
In the RSA PKCS#1 v1.5, the hash parameter to psa_sign_hash is H (computed with the hash function as indicated in the standard).
mbed TLS implementation manages RAW data in rsa_rsassa_pkcs1_v15_encode(), it copies raw data as they are given and pads accordingly.
Even if from a security perspective, this variant does not seem to introduce vulnerabilities, there is no information on the used hash algorithm inside the signature itself, therefore making mandatory for the verifier to know exactly which algorithm is used, and therefore less trivial in term of device interoperability.
Hi Thierry,
The PKCS#1 v1.5 signature generation process can be divided into four steps: 1. hash the data; 2. encode the hash into a DER structure; 3. pad the DER structure; 4. interpret the padded string as a number and apply the private key. mbedtls_rsa_rsassa_pkcs1_v15_sign(md_alg=0) and psa_sign_hash(alg=PSA_ALG_RSA_PKCS1V15_SIGN_RAW) implement steps 3–4 only. mbedtls_rsa_rsassa_pkcs1_v15_sign(md_alg≠0) and psa_sign_hash(alg=PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg)) implement steps 2–4. psa_sign_message(alg=PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg)) implement steps 1–4 combined.
The main reason for having an interface for steps 3–4 is that this is what you need for TLS versions up to 1.1, which hash the concatenation of the MD5 and the SHA1 of the handshake transcript. (TLS 1.2 and 1.3 switched to proper PKCS#1v1.5 with a configurable hash.) This is presumably why it was introduced in XySSL (the functionality has been there for a very long time, the commit you cite merely refactored it). It is the main reason why it's present in the PSA API: a driving goal of PSA Crypto 1.0 was to have all the features necessary for Mbed TLS's X.509 and TLS libraries (we did end up deferring one thing: Diffie-Hellman with non-predefined groups).
The selection of cryptographic mechanism in the PSA API aims to be a reasonable compromise between several things, including security (thus avoiding hard-to-use or dubious mechanisms) and practical usage (which sometimes requires hard-to-use or dubious mechanism). So for example is has CBC (vulnerable to padding oracle attacks, but still very popular), ECB (which shouldn't be used as ECB, but allows applications to implement a custom block cipher mode), SHA-1 (for the sake of legacy protocols), raw PKCS#1v1.5 signature (again, legacy protocols), even PKCS#1v1.5 encryption (again, legacy protocols, including TLS up to 1.2), but not raw RSA (really tricky and no compelling use case).
In terms of security, as far as I'm aware, the analyses of the security of RSA-PKCS#1v1.5 only depend on the fact that the input to the private key operation is the concatenation of a constant string with an early nonzero octet and the hash of the message to sign. So as long as the input to PSA_ALG_RSA_PKCS1V15_SIGN_RAW is a padded hash, this is as secure as any PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg). There is some added risk if the same public key is used for verification in a way that allows multiple hashes, since this scheme becomes at least as weak as the weakest hash. But it's good hygiene to restrict a key to a single suite of algorithms anyway, including the hash. The PSA API guides the user towards restricting a key to a single hash, although it does allow multiple hashes (via the PSA_ALG_ANY_HASH wildcard) for practical reasons (many communication protocols, including TLS, select hash algorithms independently of the public-key method).
In terms of interoperability, you should stick to standard protocols if at all possible. They will either mandate a specific hash (e.g. TLS 1.0/1.1 and MD5+SHA1) or provide a way to select hashes (e.g. TLS 1.2, TLS 1.3). Incidentally, ECDSA, which is a popular alternative to RSA, does not encode the hash algorithm.
Best regards,
mbed-tls@lists.trustedfirmware.org