Search code examples
cpointersopensslbigdatapointer-arithmetic

c programming - working with BIGNUM in openssl


i am trying to add two big numbers using openssl library in c but i really don't know how to work with it.

The documentation of openssl - BN_add() is here https://www.openssl.org/docs/man3.0/man3/BN_add.html

//#include <openssl/bn.h>

//BN_add() adds a and b and places the result in r (r=a+b).
//r may be the same BIGNUM as a or b.

int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);

what is a & b expecting from me? an array of bytes? a string?

Anyone who worked with adding bignumbers in openssl and have a simple example?


Solution

  • To use BN_add you need three BIGNUM artifacts: the two operands you're trying to add and a place to store the result. The source of the operands can vary, depending on the nature of the source data and possible conversions therein. The result should be acquired with BN_new.

    A trivial example using two decimal digit strings appears below:

    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/bn.h>
    #include <openssl/crypto.h>
    
    int main()
    {
        const char n1[] = "12345678912345789123456789123456789";
        const char n2[] = "12345678912345789123456789123456789";
    
        BIGNUM *bn1 = NULL;
        BN_dec2bn(&bn1, n1);
    
        BIGNUM *bn2 = NULL;
        BN_dec2bn(&bn2, n2);
    
        BIGNUM *bn3 = BN_new();
        BN_add(bn3, bn1, bn2);
    
        char *n3 = BN_bn2dec(bn3);
        printf("%s\n%s\n%s\n", n1, n2, n3);
        OPENSSL_free(n3); // don't forget to free this.
        
        BN_free(bn1);
        BN_free(bn2);
        BN_free(bn3);
    
        return 0;
    }
    

    Output

    12345678912345789123456789123456789
    12345678912345789123456789123456789
    24691357824691578246913578246913578
    

    Note that we make sure to free everything we acquire, and that includes the conversion to decimal char string of the result, which per BN_bn2dec documentation, must be freed with OPENSSL_free.


    This isn't the only way to do this, and different methodologies depend highly on the source data. For example, we can modify the above program to convert two 256 bit numbers from big-endian byte octets into BIGNUM. To do that, we may use BN_bin2bn. The rest of the code, however, would look similar (save for some additional conversion buffers that must be freed).

    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/bn.h>
    #include <openssl/crypto.h>
    #include <openssl/rand.h>
    
    int main()
    {
        unsigned char n1[32];
        unsigned char n2[32];
    
        RAND_bytes(n1, sizeof n1);
        RAND_bytes(n2, sizeof n2);
    
        BIGNUM *bn1 = BN_bin2bn(n1, sizeof n1, NULL);
        BIGNUM *bn2 = BN_bin2bn(n2, sizeof n2, NULL);
    
        BIGNUM *bn3 = BN_new();
        BN_add(bn3, bn1, bn2);
    
        char *s1 = BN_bn2dec(bn1);
        char *s2 = BN_bn2dec(bn2);
        char *s3 = BN_bn2dec(bn3);
        printf("%s\n%s\n%s\n", s1, s2, s3);
        OPENSSL_free(s1);
        OPENSSL_free(s2);
        OPENSSL_free(s3);
        
        BN_free(bn1);
        BN_free(bn2);
        BN_free(bn3);
    
        return 0;
    }
    

    Output (varies, obviously)

    12848991999079618356122471791635779303297173135339588614513279277444395635360
    47516096440756489210553139954635811049213722081352926332729551875133172387567
    60365088439836107566675611746271590352510895216692514947242831152577568022927
    

    Finally, some operations utilize a context to perform operations. For the single-shot operation we're doing here, it wouldn't be particularly beneficial, but a great deal of cryptographic algorithm code performs many complicated operations on big numbers. Providing a temporary storage context to manage what would otherwise be a great many memory allocations and releases is what contexts are mainly for.

    Below you will see an example of generating two 128bit numbers as random blobs, then multiplying them together to produce the desired result.

    #include <stdio.h>
    #include <stdlib.h>
    #include <openssl/bn.h>
    #include <openssl/crypto.h>
    #include <openssl/rand.h>
    
    int main()
    {
        unsigned char n1[16];
        unsigned char n2[16];
    
        RAND_bytes(n1, sizeof n1);
        RAND_bytes(n2, sizeof n2);
    
        BIGNUM *bn1 = BN_bin2bn(n1, sizeof n1, NULL);
        BIGNUM *bn2 = BN_bin2bn(n2, sizeof n2, NULL);
        BIGNUM *bn3 = BN_new();
    
        // create context
        BN_CTX *ctx = BN_CTX_new();
        BN_mul(bn3, bn1, bn2, ctx);
        BN_CTX_free(ctx);
    
        char *s1 = BN_bn2dec(bn1);
        char *s2 = BN_bn2dec(bn2);
        char *s3 = BN_bn2dec(bn3);
        printf("%s\n%s\n%s\n", s1, s2, s3);
        OPENSSL_free(s1);
        OPENSSL_free(s2);
        OPENSSL_free(s3);
        
        BN_free(bn1);
        BN_free(bn2);
        BN_free(bn3);
    
        return 0;
    }
    

    Output (varies, obviously)

    132784775043065614238831600062417274250
    106732277254089889576391506780872414280
    14172421425018434896232647200508450669828492339359021022467696136374376290000
    

    There are a variety of web sites where you can verify the above product is accurate if you harbor any doubts; I realize long-handing that out is considerably more cumbersome than the simple addition operations we did prior.