RSA Padding Schemes

/

Intro

Padding scheme is with RSA encrypt operations to prepend or append plaintext payloads in order to disable certain attacks which weaken the security of key. This is needed wen plaintext size is less then key size (modulus).

From what I see the most common padding schemes are:

  • RSASSA-PKCS1-V1_5 – Signature padding scheme (not for encryption)
  • RSA-PSS – Padding scheme for signatures
  • OEAP – Padding scheme for encryption

I’m not going into actual details of how these schemes work nor I’m familiar with all the details of it.
What I”m interested is how to utilize the mentioned encryption/singing operations with popular cryptography libraries and use cases which they offer. All these systems are standard’s based but should nevertheless be checked if they are still valid at the time of use since new understandings of them may prove that they have newly discovered vulnerabilities.

Signing with RSASSA-PKCS1-V1_5

OpenSSL 1.0

    RSA *rsa = RSA_new();
    BIGNUM *bn = BN_new();
    BN_set_word(bn, RSA_F4);

    int ret = RSA_generate_key_ex(rsa, 2048, bn, NULL);
    assert(0 != ret);

    SHA256_CTX *sha_ctx = (SHA256_CTX *) alloca(sizeof(SHA256_CTX));
    memset(sha_ctx, 0, sizeof(SHA256_CTX));
    assert(NULL != sha_ctx);
    SHA256_Init(sha_ctx);

    const char *msg_to_sign = "Hello, World";
    const size_t len = strlen(msg_to_sign);

    SHA256_Update(sha_ctx, msg_to_sign, len);
    unsigned char *digest = (unsigned char *) alloca(SHA256_DIGEST_LENGTH);
    memset(digest, 0, SHA256_DIGEST_LENGTH);
    SHA256_Final(digest, sha_ctx);

    int rsa_size = RSA_size(rsa);
    unsigned char *sigret = (unsigned char *) alloca(rsa_size);
    unsigned int siglen = 0;
    memset(sigret, 0, rsa_size);

    assert(1 == RSA_sign(NID_RSA_SHA3_256, digest, SHA256_DIGEST_LENGTH, sigret, &siglen, rsa));

    FILE *f = fopen("/tmp/signature.raw", "wc");
    assert(NULL != f);
    assert(siglen == fwrite(sigret, 1, siglen, f));
    fclose(f);

    RSA_free(rsa);

Signing with RSASSA-PSS

OpenSSL 1.0

    RSA *rsa = RSA_new();
    BIGNUM *bn = BN_new();
    BN_set_word(bn, RSA_F4);

    int ret = RSA_generate_key_ex(rsa, 2048, bn, NULL);
    assert(0 != ret);

    SHA256_CTX *sha_ctx = (SHA256_CTX *) alloca(sizeof(SHA256_CTX));
    memset(sha_ctx, 0, sizeof(SHA256_CTX));
    assert(NULL != sha_ctx);
    SHA256_Init(sha_ctx);

    const char *msg_to_sign = "Hello, World";
    const size_t len = strlen(msg_to_sign);

    SHA256_Update(sha_ctx, msg_to_sign, len);
    unsigned char *digest = (unsigned char *) alloca(SHA256_DIGEST_LENGTH);
    memset(digest, 0, SHA256_DIGEST_LENGTH);
    SHA256_Final(digest, sha_ctx);

    int rsa_size = RSA_size(rsa);
    unsigned char *sigret = (unsigned char *) alloca(rsa_size);
    unsigned int siglen = 0;
    memset(sigret, 0, rsa_size);

    assert(1 == RSA_sign(NID_rsassaPss, digest, SHA256_DIGEST_LENGTH, sigret, &siglen, rsa));
    assert(siglen == rsa_size);

    FILE *f = fopen("/tmp/signature.raw", "wc");
    assert(NULL != f);
    assert(siglen == fwrite(sigret, 1, siglen, f));
    fclose(f);

    assert(1 == RSA_verify(NID_rsassaPss, digest, SHA256_DIGEST_LENGTH, sigret, siglen, rsa));

    RSA_free(rsa);

Bouncy Castle

val (_, public, private) = getRsaKey()
val salt = ByteArray(256 / 8);
val ba = "Hello, World!".encodeToByteArray()
SecureRandom().nextBytes(salt)

val signature = PSSSigner(RSABlindedEngine(), SHA256Digest(), salt).let {
    it.init(true, private)
    it.update(ba, 0, ba.size)
    it.generateSignature()
}

assertTrue(PSSSigner(RSABlindedEngine(), SHA256Digest(), salt).let {
    it.init(false, public)
    it.update(ba, 0, ba.size)
    it.verifySignature(signature)
})

Leave a Reply

Your email address will not be published. Required fields are marked *