Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

request to support RSA OAEP padding #70

Closed
mouse07410 opened this issue Feb 28, 2016 · 9 comments
Closed

request to support RSA OAEP padding #70

mouse07410 opened this issue Feb 28, 2016 · 9 comments
Assignees
Milestone

Comments

@mouse07410
Copy link
Contributor

The title says it all: it would be great if libp11 (p11_rsa.c and p11_ops.c in particular) could support RSA_PKCS1_OAEP_PADDING in addition to RSA_PKCS1_PADDING.

@mtrojnar mtrojnar added this to the 0.4.x milestone Feb 29, 2016
@mtrojnar mtrojnar self-assigned this Feb 29, 2016
@mouse07410
Copy link
Contributor Author

Once 0e84a1e gets merged into https://github.com/OpenSC/libp11, this issue can be closed, IMHO.

(In fact, it does even more than I originally asked, by adding PSS signatures.)

@mtrojnar
Copy link
Member

mtrojnar commented Mar 4, 2016

This is great news. I wrote some initial code, but I haven't tested it at all. Could you share the results of your tests? Specifically, whether the data encrypted with a private key on the device can be successfully decrypted with the corresponding public key, and whether the data encrypted with a public key can be successfully decrypted on the device with the corresponding private key.

@mouse07410
Copy link
Contributor Author

This is great news

Thank you!

Could you share the results of your tests?

I will post them here.

My only concern at this point is - I don't fully understand how, e.g., RSA-PSS is done when the token itself only does "plain" raw RSA. But I'll run some more tests and will post the results here. So far they look encouraging, to say the least.

@mouse07410
Copy link
Contributor Author

I don't have the signature testing code yet, will do later.

Here's the encryption/decryption code excerpt (in both cases the asymmetric key is on the token :), with most of the checks culled out to save space. Please let me know if this is what you expect, and/or if you'd like something else.

    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config("/opt/local/etc/openssl/openssl.cnf");

    engine = ENGINE_by_id("pkcs11");
    ENGINE_init(engine);

    ENGINE_ctrl_cmd(engine, "MODULE_PATH", 0,
            "/Library/OpenSC/lib/opensc-pkcs11.dylib", NULL, 1) );

    fprintf(stderr, "We expect an RSA token for this demo...\n");
    /* Use Key Management key */
    inkey = "pkcs11:object=KEY%20MAN%20pubkey;object-type=public";
    keyform = FORMAT_ENGINE;
    key_type = KEY_PUBKEY;
    pkey_op = EVP_PKEY_OP_ENCRYPT;

    /* Initialize context */
    ctx = init_ctx(&keysize, inkey, keyform, key_type, NULL, EVP_PKEY_OP_ENCRYPT, engine);
    /* Set RSA padding */
    rv = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);

    buf_inlen = (size_t) 32;
    buf_in = OPENSSL_malloc(buf_inlen);
    for (int i = 0; i < buf_inlen; i++)
        if (((i/16)%2) == 0)
            buf_in[i] = ((i % 16) + 1);
        else
            buf_in[i] = (16 - (i % 16));

    /* Print test string in hex */
    printf("Input buffer:\n"); print_hex(buf_in, buf_inlen); printf("\n");

    rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen, buf_in, (size_t)buf_inlen);
    printf("encrypt: allocating %1lu bytes (rv=%1d)\n", buf_outlen, rv);
    buf_out = OPENSSL_malloc(buf_outlen);
    rv = do_keyop(ctx, pkey_op, buf_out, (size_t *)&buf_outlen, buf_in, (size_t)buf_inlen);
    if (rv <= 0) {
        BIO_printf(bio_err, "encrypt: Public Key operation error\n");
        ERR_print_errors(bio_err);
        goto end;
    } else {
        printf("RSA encryption succeeded (rv=%1d len=%1lu):\n", rv, buf_outlen);
        /* Output result in hex */
        print_hex(buf_out, buf_outlen);
        printf("\n");
    }
.........
    inkey = "pkcs11:object=KEY%20MAN%20key;object-type=private";
    key_type = KEY_PRIVKEY;
    pkey_op = EVP_PKEY_OP_DECRYPT;

    ctx = init_ctx(&keysize, inkey, keyform, key_type, NULL, EVP_PKEY_OP_DECRYPT, engine);
    rv=EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
    rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&new_inbuf_len, buf_out, (size_t)buf_outlen);
    printf("decrypt: allocating %1lu bytes (rv=%d)\n", new_inbuf_len, rv);
    new_inbuf = OPENSSL_malloc(new_inbuf_len); /* allocate what we were told */
        /* Decrypt encrypted buffer with the private key */
    rv = do_keyop(ctx, pkey_op, new_inbuf, (size_t *)&new_inbuf_len, buf_out, (size_t)buf_outlen);
    if (rv <= 0) {
        fprintf(stderr, "decrypt: Public key error!! rv=%d\n", rv);
        BIO_printf(bio_err, "decrypt: Public Key operation error\n");
        ERR_print_errors(bio_err);
        goto end;
    } else {
        printf("RSA decryption succeeded (rv=%1d len=%1lu):\n", rv, new_inbuf_len);
        /* Output result in hex, to show it is the same as original */
        print_hex(new_inbuf, new_inbuf_len);
        printf("\n");
    }

Here's the public key used:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq5v0HJL9I96d34LrULo3
fjp+o+xd/YJSoNeWaLUh6U5PF7/8WCWvJRMFpgshYuydfUqI9gWCgelabYQf9HgJ
QogKTpSZ8yfJ7KLLgdGPUzJdVUS15f+jWW6FxVenGEDUIqEROgOOZ20iE8UkDYJ8
nX5YauqCFkjiWGQ4W8x+5Idm4A9hMoaDcm3OrbWpIOwprMI77bipybWI0Xx+x4Ph
Nfbi9bzHwDPFEUSH4fCIdnr803ihycak9GvSVG38US2OUpUqIvH1yQby71AQk+e3
IWdSYoUtsix1qH0SIUVTxq4LWji7fdmU+DiJPmxwzyIopH291Ws0HJVuEJFONgTR
owIDAQAB
-----END PUBLIC KEY-----

Here's the output:

We expect an RSA token for this demo...
Input buffer:
01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 
10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 

encrypt: allocating 256 bytes (rv=1)
RSA encryption succeeded (rv=1 len=256):
03 9a de f4 9d d1 ec c1 96 d6 a9 96 1b 57 8b 4c 
d1 3b 41 65 f8 d0 67 3d 3e 01 a7 26 fe e8 26 ad 
17 dd cb 80 7d 0a f9 a4 e6 f6 db 77 57 0a 41 0c 
56 af 50 2a 68 31 7b a8 a8 f4 b0 8f 9d 53 38 ed 
5b c3 c6 cd 7a 5b ba a9 36 bf c7 e5 5b 6c 0f 95 
3a 2a d8 3d 99 f7 2a 15 c6 c1 e4 6d e3 74 5b eb 
1a 10 47 2f 42 b0 60 a1 80 f1 f5 ef 78 0b f3 df 
1c 20 51 9e 54 13 ef 0a 5c 00 8c 30 38 ff af 92 
0d 54 f7 d2 c6 0c 37 be 52 4f d2 c4 69 a0 59 ce 
40 55 a5 00 32 f2 80 ce 32 26 3c 31 ec 93 63 17 
9f e6 b3 18 e7 4e 72 43 3d 95 b0 c2 3d 44 f5 5a 
f5 7d 15 50 4b b5 7c f9 e9 0c 0a e9 8c 95 76 31 
d7 ce 11 bb 07 9d c3 8e 50 59 87 4b 78 0f dc 91 
06 cf 2a 6c d3 f8 79 8a 27 8d ec 8f 44 28 34 bf 
13 ee cd de 9f 44 05 4a ef ad e5 de 9e cf be 92 
22 e3 ed 2d f7 3e 8d 0f a0 81 9c 30 a1 91 98 9f 

PKCS#11 token PIN: xxxxxxxx

decrypt: allocating 256 bytes (rv=1)
RSA decryption succeeded (rv=1 len=32):
01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 
10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01

More output (another invocation):

We expect an RSA token for this demo...
Input buffer:
01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 
10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 

encrypt: allocating 256 bytes (rv=1)
RSA encryption succeeded (rv=1 len=256):
79 5e e6 89 9b dc 44 9a 5b 94 2c 18 84 21 74 c5 
a0 60 2c 75 44 d4 a2 79 7e 92 93 fe 2b 17 2c 0f 
de 35 0d 61 96 90 42 01 20 e5 6a 26 17 fc 8b 8b 
fe 87 74 ad 19 95 c5 69 e5 75 51 ef 9b bf c3 d6 
76 be 17 e7 84 e2 c0 4b 7d 12 76 2d 8b 1d 45 86 
25 1f c3 3f e3 78 f0 03 a1 93 2b ba f4 fe 1d a8 
51 f3 eb 34 ea 0c d9 5b ec c5 13 dd b4 9e 53 11 
e0 a3 81 a6 7e 24 af f9 42 7b 6d ea d6 62 5e 1d 
2c ea 5e e4 a9 98 f9 dc c7 fd a5 29 93 73 e3 ff 
d7 e9 e7 23 ea 4b 66 95 f0 68 1a 10 2c 68 a0 0d 
c8 fc 4d b0 d8 b2 da a1 e4 35 d0 1f 10 8c 1e b5 
03 78 07 5e 61 78 7f 42 07 33 d5 94 a5 e0 1e 41 
1e 3e 0a 91 19 b9 2e 9f 81 6e f7 9c 8a de 86 97 
b6 00 9f 32 e8 05 95 ac cf b6 67 8d 9e 0e a7 e3 
23 74 37 4e e4 95 8f ce 6d d0 72 3f 72 e3 f6 50 
1e 25 25 cd 67 37 55 c3 e3 3d c6 14 b2 fb 9f f8 

PKCS#11 token PIN: xxxxxxxx

decrypt: allocating 256 bytes (rv=1)
RSA decryption succeeded (rv=1 len=32):
01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 
10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01

More:

We expect an RSA token for this demo...
Input buffer:
01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 
10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 

encrypt: allocating 256 bytes (rv=1)
RSA encryption succeeded (rv=1 len=256):
97 7a cc fc 7d 05 1e 8a e1 0e 07 c1 17 fa 37 c2 
2a af e0 3e 4e 0d c7 2d d9 5b 7d 27 c9 db 8c b9 
f1 97 0d c4 18 55 78 9c 68 27 ff ed b5 69 ed 26 
70 be 2d c7 e6 db 5e 22 30 fd 37 94 82 0c 16 3b 
09 ab 88 fe e8 d7 62 bd 1e c5 aa c4 91 7d 01 cb 
23 33 a5 90 58 4a ac 7b 86 99 d4 d7 80 b7 58 ab 
8c c2 ae 9a af cd fe 18 af fd 9c 77 5b 20 ba aa 
a0 4a a6 35 6e 8e af 14 e2 0f 70 a7 e2 2c ba b6 
76 be 14 b3 80 b3 54 c2 f2 ea 55 85 6e 37 61 12 
5f 54 a0 8f 3e ed b5 b1 6a 6e 52 7f 41 63 f3 e6 
2a 00 9d f7 11 6f c2 bc 55 4f bd e0 db 5f 42 41 
6a e9 aa 1c 17 8a fb 96 14 bc 4e 05 b5 ce 5f 17 
1c f2 d5 fa c2 a9 72 29 54 62 4f cd d7 4b 48 3a 
e3 5b dd 33 94 de b1 76 2c b3 a6 79 96 b2 b4 1a 
c1 fa ea 39 b7 9e 44 38 72 34 3a 56 e8 43 c4 74 
8f bb f8 a6 d4 8a 3b 8e cf ab e9 b9 56 04 d7 ec 

PKCS#11 token PIN: xxxxxxxx

decrypt: allocating 256 bytes (rv=1)
RSA decryption succeeded (rv=1 len=32):
01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 
10 0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01

@mtrojnar
Copy link
Member

mtrojnar commented Mar 7, 2016

Great. Thank you very much for testing it. I guess it is okay to have public keys "on the token", because libp11 just feeds them into OpenSSL anyway, instead of executing public key operations via PKCS#11.

My only concern at this point is - I don't fully understand how, e.g., RSA-PSS is done when the token itself only does "plain" raw RSA.

Each PKCS#11 module is not really just an interface to hardware, but rather a self-contained cryptographic library. Only some sensitive primitives (PRNG or private key computation) have to be performed on the actual hardware. For RSA-PSS signatures this boils down to the RSASP1 primitive, which is also used in the basic PKCS#1 v1.5 signature. The remaining data conversion primitives can technically be implemented in the PKCS#11 (software) module rather than on the (hardware) device.

Correct me if I'm wrong, but the OpenSC PKCS#11 module does not seem to implement RSA-OAEP nor RSA-PSS:

$ pkcs11-tool -IM
Cryptoki version 2.20
Manufacturer     OpenSC (www.opensc-project.org)
Library          Smart card PKCS#11 API (ver 0.0)
Using slot 1 with a present token (0x1)
Supported mechanisms:
  SHA-1, digest
  SHA256, digest
  SHA384, digest
  SHA512, digest
  MD5, digest
  RIPEMD160, digest
  GOSTR3411, digest
  ECDSA, keySize={192,320}, hw, sign, other flags=0x1d00000
  ECDSA-SHA1, keySize={192,320}, hw, sign, other flags=0x1d00000
  ECDH1-COFACTOR-DERIVE, keySize={192,320}, hw, derive, other flags=0x1d00000
  ECDH1-DERIVE, keySize={192,320}, hw, derive, other flags=0x1d00000
  ECDSA-KEY-PAIR-GEN, keySize={192,320}, hw, generate_key_pair, other flags=0x1d00000
  RSA-X-509, keySize={1024,2048}, hw, decrypt, sign, verify
  RSA-PKCS, keySize={1024,2048}, hw, decrypt, sign, verify
  SHA1-RSA-PKCS, keySize={1024,2048}, sign, verify
  SHA256-RSA-PKCS, keySize={1024,2048}, sign, verify
  SHA384-RSA-PKCS, keySize={1024,2048}, sign, verify
  SHA512-RSA-PKCS, keySize={1024,2048}, sign, verify
  MD5-RSA-PKCS, keySize={1024,2048}, sign, verify
  RIPEMD160-RSA-PKCS, keySize={1024,2048}, sign, verify
  RSA-PKCS-KEY-PAIR-GEN, keySize={1024,2048}, generate_key_pair

What is your output of pkcs11-tool -IM?

@mouse07410
Copy link
Contributor Author

Thank you very much for testing it.

My pleasure - thank you for adding this capability (and so quickly too).

I guess it is okay to have public keys "on the token"...

It's more than "okay" - it's a necessity for cases like "encrypted filesystem". Work-arounds could be possible, but way too cumbersome. So thank God that public keys "on the token" work fine.

For RSA-PSS signatures ... ... remaining data conversion primitives can technically be
implemented in the PKCS#11 (software) module rather than on the (hardware) device.

I just wasn't sure that was the case, not knowing myself where exactly in the code that implementation lives.

but the OpenSC PKCS#11 module does not seem to implement RSA-OAEP nor RSA-PSS

Correct, as far as I know:

$ pkcs11-tool -IM
Cryptoki version 2.20
Manufacturer     OpenSC (www.opensc-project.org)
Library          Smart card PKCS#11 API (ver 0.0)
Using slot 1 with a present token (0x1)
Supported mechanisms:
  SHA-1, digest
  SHA256, digest
  SHA384, digest
  SHA512, digest
  MD5, digest
  RIPEMD160, digest
  GOSTR3411, digest
  ECDSA, keySize={256,384}, hw, sign, other flags=0x1800000
  ECDH1-COFACTOR-DERIVE, keySize={256,384}, hw, derive, other flags=0x1800000
  ECDH1-DERIVE, keySize={256,384}, hw, derive, other flags=0x1800000
  RSA-X-509, keySize={1024,3072}, hw, decrypt, sign, verify
  RSA-PKCS, keySize={1024,3072}, hw, decrypt, sign, verify
  SHA1-RSA-PKCS, keySize={1024,3072}, sign, verify
  SHA256-RSA-PKCS, keySize={1024,3072}, sign, verify
  SHA384-RSA-PKCS, keySize={1024,3072}, sign, verify
  SHA512-RSA-PKCS, keySize={1024,3072}, sign, verify
  MD5-RSA-PKCS, keySize={1024,3072}, sign, verify
  RIPEMD160-RSA-PKCS, keySize={1024,3072}, sign, verify
$ 

@mtrojnar
Copy link
Member

mtrojnar commented Mar 7, 2016

I dug deeper into the guts of OpenSSL, and it doesn't really use OAEP provided by the engine. Instead, it applies its own OAEP padding, and only uses the engine for raw RSA. My commit has also enabled raw RSA support in the pkcs11_private_decrypt() method, which apparently made it working for you. 😄

From crypto/rsa/rsa_pmeth.c:

static int pkey_rsa_decrypt(EVP_PKEY_CTX *ctx,
                            unsigned char *out, size_t *outlen,
                            const unsigned char *in, size_t inlen)
{
    int ret;
    RSA_PKEY_CTX *rctx = ctx->data;
    if (rctx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
        int i;
        if (!setup_tbuf(rctx, ctx))
            return -1;
        ret = RSA_private_decrypt(inlen, in, rctx->tbuf,
                                  ctx->pkey->pkey.rsa, RSA_NO_PADDING);
        if (ret <= 0)
            return ret;
        for (i = 0; i < ret; i++) {
            if (rctx->tbuf[i])
                break;
        }
        ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, ret, rctx->tbuf + i,
                                                ret - i, ret,
                                                rctx->oaep_label,
                                                rctx->oaep_labellen,
                                                rctx->md, rctx->mgf1md);
    } else
        ret = RSA_private_decrypt(inlen, in, out, ctx->pkey->pkey.rsa,
                                  rctx->pad_mode);
    if (ret < 0)
        return ret;
    *outlen = ret;
    return 1;
}

RSA_private_decrypt() is a wrapper that executes the method provided by the engine.

@mouse07410
Copy link
Contributor Author

I dug deeper into the guts of OpenSSL, and it doesn't really use OAEP provided by the engine.
Instead, it applies its own OAEP padding, and only uses the engine for raw RSA

Perfect. That's what I expected, more or less.

I'll make more extensive cross-library tests (creating something in BouncyCastle or such, and decrypting using my OpenSSL stint, and/or vs versa). But I think what has been already demonstrated is sufficient.

Thank you again!

@mtrojnar
Copy link
Member

mtrojnar commented Mar 8, 2016

Since this issue was addressed with simple enabling raw RSA support in the pkcs11_private_decrypt() method, the tests you already performed are more than enough. Thank you again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants