~aritra1911/openssl_madness

69b93f510885a2c83a6eeb83867109f906dcbab2 — Aritra Sarkar 2 years ago b96aa0a
Finish porting to OpenSSL 3.0
3 files changed, 115 insertions(+), 27 deletions(-)

M README.md
M madness.c
M notes.txt
M README.md => README.md +7 -4
@@ 1,10 1,13 @@
# RSA Encryption / Decryption
> You'll need to generate your own key pair in order to use this. A sample pair already exists in this repo in PEM
> format. Check `notes.txt` for instructions regarding command line operation of openssl and generating a key pair
> through the same. I've tested mostly using RSA 4096, but feel free to test other key sizes as well.

This is a test repo for me to get familiar with openssl and specifically the RSA part of the openssl API.

> WARNING! Not compatible with OpenSSL versions < 3.0

## BYOK (Bring Your Own Keys)
You'll need to generate your own key pair in order to use this. A sample pair already exists in this repo in PEM
format. Check `notes.txt` for instructions regarding command line operation of openssl and generating a key pair
through the same. I've tested mostly using RSA 4096, but feel free to test other key sizes as well.

## Compile
You'll need a C compiler.
```sh

M madness.c => madness.c +99 -22
@@ 185,7 185,7 @@ int main(int argc, char* argv[]) {
                   EVP_PKEY_get_size(private_key));
        }

        if ( EVP_PKEY_get_id(public_key) != EVP_PKEY_RSA ) {
        if ( EVP_PKEY_get_id(private_key) != EVP_PKEY_RSA ) {
            fprintf(stderr, "Not a valid RSA private key!!!\n");
            fclose(fp);
            return EXIT_FAILURE;


@@ 244,33 244,38 @@ int rsa_encrypt(EVP_PKEY* public_key, FILE* infile, FILE* outfile, const int ver

    /* Get default RSA context from `public_key` */
    if ( !(ctx = EVP_PKEY_CTX_new(public_key, NULL)) ) {
        fprintf(stderr, " ERR : ");
        ERR_print_errors_fp(stderr);
        return -1;
    }

    /* Initialize the context for encryption */
    if ( EVP_PKEY_encrypt_init(ctx) <= 0 ) {
        fprintf(stderr, " ERR : ");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    /* Set padding type for context */
    if ( EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 ) {
        fprintf(stderr, " ERR : ");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    // Quoting from man page of RSA_PUBLIC_ENCRYPT(3):
    // "flen must not be more than RSA_size(rsa) - 42 for RSA_PKCS1_OAEP_PADDING"
    if ( !(buf = OPENSSL_malloc(key_size - 42)) ) {
        fprintf(stderr, "Failed to allocate memory for input buffer\n");
        fprintf(stderr, " ERR : Failed to allocate memory for input buffer\n");
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    // Encrypted block with padding will be `key_size' bytes long
    if ( !(encbuf = OPENSSL_malloc(key_size)) ) {
        fprintf(stderr, "Failed to allocate memory!\n");
        fprintf(stderr, " ERR : Failed to allocate memory for output buffer\n");
        OPENSSL_free(buf);
        EVP_PKEY_CTX_free(ctx);
        return -1;


@@ 290,7 295,9 @@ int rsa_encrypt(EVP_PKEY* public_key, FILE* infile, FILE* outfile, const int ver
        if ( EVP_PKEY_encrypt(ctx, encbuf, &len, buf, flen) <= 0 ) {
            fprintf(stderr, " ERR : ");
            ERR_print_errors_fp(stderr);
            free(buf);
            OPENSSL_free(buf);
            OPENSSL_free(encbuf);
            EVP_PKEY_CTX_free(ctx);
            return -1;
        }



@@ 310,15 317,56 @@ int rsa_encrypt(EVP_PKEY* public_key, FILE* infile, FILE* outfile, const int ver
}

int rsa_decrypt(EVP_PKEY* private_key, FILE* infile, FILE* outfile, const int verbose) {
    int len, key_size = EVP_PKEY_get_size(private_key);
    char *buf, *decbuf;
    size_t flen;
    int key_size = EVP_PKEY_get_size(private_key);
    size_t flen, len, declen;
    unsigned char *buf, *decbuf;
    EVP_PKEY_CTX* ctx;

    /* Get default RSA context from `private_key` */
    if ( !(ctx = EVP_PKEY_CTX_new(private_key, NULL)) ) {
        fprintf(stderr, " ERR : ");
        ERR_print_errors_fp(stderr);
        return -1;
    }

    /* Initialize the context for decryption */
    if ( EVP_PKEY_decrypt_init(ctx) <= 0 ) {
        fprintf(stderr, " ERR : ");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    /* Set padding type for context */
    if ( EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 ) {
        fprintf(stderr, " ERR : ");
        ERR_print_errors_fp(stderr);
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    // Encrypted blocks come in `key_size' bytes each
    buf = malloc(key_size);
    // Refer to this part of `rsa_encrypt()'.
    // Each block of `key_size' bytes will be decrypted to (`key_size' - 42) bytes.
    decbuf = malloc(key_size - 42);
    if ( !(buf = OPENSSL_malloc(key_size)) ) {
        fprintf(stderr, " ERR : Failed to allocate memory for input buffer\n");
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    /* Refer to this part of `rsa_encrypt()'.
     * Each block of `key_size' bytes will be decrypted to (`key_size' - 42) bytes.
     *
     * Despite the above comment, a little experimentation has revealed that
     * during determination of output buffer length (i.e. passing `out` = NULL
     * into `EVP_PKEY_decrypt()`, we get `len` = `key_size`. So here we assume
     * that'll be the case everytime. `declen` is set to keep track of the
     * number of bytes `malloc()`ed for `decbuf`. */
    declen = key_size;
    if ( !(decbuf = OPENSSL_malloc(declen)) ) {
        fprintf(stderr, " ERR : Failed to allocate memory for output buffer\n");
        OPENSSL_free(buf);
        EVP_PKEY_CTX_free(ctx);
        return -1;
    }

    do {
        // Read blocks of data, each `key_size' bytes long, from `infile' and decrypt them and write them out to


@@ 328,26 376,55 @@ int rsa_decrypt(EVP_PKEY* private_key, FILE* infile, FILE* outfile, const int ve
        // Quoting from man page of FREAD(3):
        // "If the end of the file is reached, the return value is a short item count (or zero)"
        // Hence, if 0 bytes were read, that's definitely an EOF, implying that we must stop
        if (!(flen = fread(buf, 1, key_size, infile))) break;
        if (verbose) printf("%lu bytes read for decryption\n", flen);
        if (!(flen = fread(buf, (size_t) 1, key_size, infile))) break;
        if (verbose) printf("INFO : %lu bytes read for decryption\n", flen);

        /* Determine length of required output buffer */
        if ( EVP_PKEY_decrypt(ctx, NULL, &len, buf, flen) <= 0 ) {
            fprintf(stderr, " ERR : ");
            ERR_print_errors_fp(stderr);
            OPENSSL_free(buf);
            OPENSSL_free(decbuf);
            EVP_PKEY_CTX_free(ctx);
            return -1;
        }

        if ( len > declen ) {
            /* If we find that our previous assumption of the size of `decbuf`
             * was terribly wrong, we `realloc()` adequate amount of memory for
             * `decbuf`, as determined by the previous successful call to
             * `EVP_PKEY_decrypt()` */
            if ( !(decbuf = OPENSSL_realloc(decbuf, len)) ) {
                fprintf(stderr, " ERR : Failed to reallocate memory"
                                " for output buffer\n");
                OPENSSL_free(buf);
                EVP_PKEY_CTX_free(ctx);
                return -1;
            }
            declen = len;
        }

        if ((len = RSA_private_decrypt(flen, (unsigned char*) buf, (unsigned char*) decbuf,
                                       private_key, RSA_PKCS1_OAEP_PADDING)) == -1) {
        /* Now decrypt `flen` bytes which should produce `len` bytes */
        if ( EVP_PKEY_decrypt(ctx, decbuf, &len, buf, flen) <= 0 ) {
            fprintf(stderr, " ERR : ");
            ERR_print_errors_fp(stderr);
            free(buf);
            free(decbuf);
            OPENSSL_free(buf);
            OPENSSL_free(decbuf);
            EVP_PKEY_CTX_free(ctx);
            return -1;
        }
        if (verbose) printf("%d bytes decrypted\n", len);

        if (verbose) printf("INFO : %lu bytes decrypted\n", flen);

        // Write it to outfile
        flen = fwrite(decbuf, 1, len, outfile);
        if (verbose) printf("%lu bytes written\n", flen);
        len = fwrite(decbuf, (size_t) 1, len, outfile);
        if (verbose) printf("INFO : %lu bytes written\n", len);

    } while (!feof(infile));

    free(buf);
    free(decbuf);
    OPENSSL_free(buf);
    OPENSSL_free(decbuf);
    EVP_PKEY_CTX_free(ctx);

    return 0;
}

M notes.txt => notes.txt +9 -1
@@ 31,4 31,12 @@ Hello, World!
#################

# Test whether encryption works or not.
$ fortune | ./madness -e -k pubkey.pem | /opt/openssl-3.0.0-beta1/bin/openssl pkeyutl -decrypt -inkey key.pem -pkeyopt rsa_padding_mode:oaep
$ fortune -s | ./madness -e -k pubkey.pem | /opt/openssl-3.0.0-beta1/bin/openssl pkeyutl -decrypt -inkey key.pem -pkeyopt rsa_padding_mode:oaep


# Test whether decryption works or not.
❯ fortune -s | /usr/local/build/openssl-3.0.0-beta1/bin/openssl pkeyutl -encrypt -inkey pubkey.pem -pubin -pkeyopt rsa_padding_mode:oaep | ./madness -dk key.pem

# OR : Use our own tool #

❯ fortune -l | ./madness -ek pubkey.pem | ./madness -dk key.pem