snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

xs_openssl.h (7185B)


      1 /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */
      2 
      3 #ifndef _XS_OPENSSL_H
      4 
      5 #define _XS_OPENSSL_H
      6 
      7 xs_str *_xs_digest(const xs_val *input, int size, const char *digest, int as_hex);
      8 xs_str *_xs_md5_buf(xs_str *buffer, const char *input, ...);
      9 xs_str *_xs_md5_buf_bin(xs_str *buffer, const void *input, int size);
     10 
     11 #ifndef _XS_MD5_H
     12 #define xs_md5_hex(input, size)       _xs_digest(input, size, "md5",    1)
     13 #define xs_md5(...)                   _xs_md5_buf((char[MD5_HEX_SIZE]){ 0 }, __VA_ARGS__, NULL)
     14 #define xs_md5_arr(arr)               _xs_md5_buf_bin((char[MD5_HEX_SIZE]){ 0 }, arr, sizeof(arr))
     15 #endif /* XS_MD5_H */
     16 
     17 #ifndef _XS_BASE64_H
     18 xs_str *xs_base64_enc(const xs_val *data, int sz);
     19 xs_val *xs_base64_dec(const xs_str *data, int *size);
     20 #endif /* XS_BASE64_H */
     21 
     22 #define xs_sha1_hex(input, size)      _xs_digest(input, size, "sha1",   1)
     23 #define xs_sha256_hex(input, size)    _xs_digest(input, size, "sha256", 1)
     24 #define xs_sha256_base64(input, size) _xs_digest(input, size, "sha256", 0)
     25 
     26 xs_dict *xs_evp_genkey(int bits);
     27 xs_str *xs_evp_sign(const char *secret, const char *mem, int size);
     28 int xs_evp_verify(const char *pubkey, const char *mem, int size, const char *b64sig);
     29 
     30 
     31 #ifdef XS_IMPLEMENTATION
     32 
     33 #include "openssl/rsa.h"
     34 #include "openssl/pem.h"
     35 #include "openssl/evp.h"
     36 
     37 
     38 #ifndef _XS_BASE64_H
     39 
     40 xs_str *xs_base64_enc(const xs_val *data, int sz)
     41 /* encodes data to base64 */
     42 {
     43     BIO *mem, *b64;
     44     BUF_MEM *bptr;
     45  
     46     b64 = BIO_new(BIO_f_base64());
     47     mem = BIO_new(BIO_s_mem());
     48     b64 = BIO_push(b64, mem);
     49 
     50     BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
     51 
     52     BIO_write(b64, data, sz);
     53     BIO_flush(b64);
     54     BIO_get_mem_ptr(b64, &bptr);
     55 
     56     int n = bptr->length;
     57     xs_str *s = xs_realloc(NULL, _xs_blk_size(n + 1));
     58 
     59     memcpy(s, bptr->data, n);
     60     s[n] = '\0';
     61 
     62     BIO_free_all(b64);
     63 
     64     return s;
     65 }
     66 
     67 
     68 xs_val *xs_base64_dec(const xs_str *data, int *size)
     69 /* decodes data from base64 */
     70 {
     71     BIO *b64, *mem;
     72 
     73     *size = strlen(data);
     74 
     75     b64 = BIO_new(BIO_f_base64());
     76     mem = BIO_new_mem_buf(data, *size);
     77     b64 = BIO_push(b64, mem);
     78 
     79     BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
     80 
     81     /* alloc a very big buffer */
     82     xs_str *s = xs_realloc(NULL, *size);
     83 
     84     *size = BIO_read(b64, s, *size);
     85 
     86     /* adjust to current size */
     87     s = xs_realloc(s, _xs_blk_size(*size + 1));
     88     s[*size] = '\0';
     89 
     90     BIO_free_all(b64);
     91 
     92     return s;
     93 }
     94 
     95 #endif /* _XS_BASE64_H */
     96 
     97 xs_str *_xs_md5_buf_bin(xs_str *buffer, const void *input, int size)
     98 {
     99     const EVP_MD *md;
    100 
    101     if ((md = EVP_get_digestbyname("md5")) == NULL)
    102         return NULL;
    103 
    104     unsigned char output[EVP_MAX_MD_SIZE];
    105     unsigned int out_size;
    106     EVP_MD_CTX *mdctx;
    107 
    108     mdctx = EVP_MD_CTX_new();
    109     EVP_DigestInit_ex(mdctx, md, NULL);
    110     EVP_DigestUpdate(mdctx, input, size);
    111     EVP_DigestFinal_ex(mdctx, output, &out_size);
    112     EVP_MD_CTX_free(mdctx);
    113 
    114     return xs_hex_enc_buf((const char *)output, out_size, buffer);
    115 }
    116 
    117 xs_str *_xs_md5_buf(xs_str *buffer, const char *input, ...)
    118 /* generic function for generating and encoding digests */
    119 {
    120     const EVP_MD *md;
    121     va_list args;
    122 
    123     if ((md = EVP_get_digestbyname("md5")) == NULL)
    124         return NULL;
    125 
    126     unsigned char output[EVP_MAX_MD_SIZE];
    127     unsigned int out_size;
    128     EVP_MD_CTX *mdctx;
    129 
    130     mdctx = EVP_MD_CTX_new();
    131     EVP_DigestInit_ex(mdctx, md, NULL);
    132     va_start(args, input);
    133     while (input) {
    134         EVP_DigestUpdate(mdctx, input, strlen(input));
    135         input = va_arg(args, const char *);
    136     }
    137     va_end(args);
    138     EVP_DigestFinal_ex(mdctx, output, &out_size);
    139     EVP_MD_CTX_free(mdctx);
    140 
    141     return xs_hex_enc_buf((const char *)output, out_size, buffer);
    142 }
    143 
    144 
    145 xs_str *_xs_digest(const xs_val *input, int size, const char *digest, int as_hex)
    146 /* generic function for generating and encoding digests */
    147 {
    148     const EVP_MD *md;
    149 
    150     if ((md = EVP_get_digestbyname(digest)) == NULL)
    151         return NULL;
    152 
    153     unsigned char output[1024];
    154     unsigned int out_size;
    155     EVP_MD_CTX *mdctx;
    156 
    157     mdctx = EVP_MD_CTX_new();
    158     EVP_DigestInit_ex(mdctx, md, NULL);
    159     EVP_DigestUpdate(mdctx, input, size);
    160     EVP_DigestFinal_ex(mdctx, output, &out_size);
    161     EVP_MD_CTX_free(mdctx);
    162 
    163     return as_hex ? xs_hex_enc   ((char *)output, out_size) :
    164                     xs_base64_enc((char *)output, out_size);
    165 }
    166 
    167 
    168 xs_dict *xs_evp_genkey(int bits)
    169 /* generates an RSA keypair using the EVP interface */
    170 {
    171     xs_dict *keypair = NULL;
    172     EVP_PKEY_CTX *ctx;
    173     EVP_PKEY *pkey = NULL;
    174 
    175     if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL)
    176         goto end;
    177 
    178     if (EVP_PKEY_keygen_init(ctx) <= 0 ||
    179         EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0 ||
    180         EVP_PKEY_keygen(ctx, &pkey) <= 0)
    181         goto end;
    182 
    183     BIO *bs = BIO_new(BIO_s_mem());
    184     BIO *bp = BIO_new(BIO_s_mem());
    185     BUF_MEM *sptr;
    186     BUF_MEM *pptr;
    187 
    188     PEM_write_bio_PrivateKey(bs, pkey, NULL, NULL, 0, 0, NULL);
    189     BIO_get_mem_ptr(bs, &sptr);
    190 
    191     PEM_write_bio_PUBKEY(bp, pkey);
    192     BIO_get_mem_ptr(bp, &pptr);
    193 
    194     keypair = xs_dict_new();
    195 
    196     keypair = xs_dict_append(keypair, "secret", sptr->data);
    197     keypair = xs_dict_append(keypair, "public", pptr->data);
    198 
    199     BIO_free(bs);
    200     BIO_free(bp);
    201 
    202 end:
    203     return keypair;
    204 }
    205 
    206 
    207 xs_str *xs_evp_sign(const char *secret, const char *mem, int size)
    208 /* signs a memory block (secret is in PEM format) */
    209 {
    210     xs_str *signature = NULL;
    211     BIO *b;
    212     unsigned char *sig;
    213     unsigned int sig_len;
    214     EVP_PKEY *pkey;
    215     EVP_MD_CTX *mdctx;
    216     const EVP_MD *md;
    217 
    218     /* un-PEM the key */
    219     b = BIO_new_mem_buf(secret, strlen(secret));
    220     pkey = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
    221 
    222     /* I've learnt all these magical incantations by watching
    223        the Python module code and the OpenSSL manual pages */
    224     /* Well, "learnt" may be an overstatement */
    225 
    226     md = EVP_get_digestbyname("sha256");
    227 
    228     mdctx = EVP_MD_CTX_new();
    229 
    230     sig_len = EVP_PKEY_size(pkey);
    231     sig = xs_realloc(NULL, sig_len);
    232 
    233     EVP_SignInit(mdctx, md);
    234     EVP_SignUpdate(mdctx, mem, size);
    235 
    236     if (EVP_SignFinal(mdctx, sig, &sig_len, pkey) == 1)
    237         signature = xs_base64_enc((char *)sig, sig_len);
    238 
    239     EVP_MD_CTX_free(mdctx);
    240     EVP_PKEY_free(pkey);
    241     BIO_free(b);
    242     xs_free(sig);
    243 
    244     return signature;
    245 }
    246 
    247 
    248 int xs_evp_verify(const char *pubkey, const char *mem, int size, const char *b64sig)
    249 /* verifies a base64 block, returns non-zero on ok */
    250 {
    251     int r = 0;
    252     BIO *b;
    253     EVP_PKEY *pkey;
    254     EVP_MD_CTX *mdctx;
    255     const EVP_MD *md;
    256 
    257     /* un-PEM the key */
    258     b = BIO_new_mem_buf(pubkey, strlen(pubkey));
    259     pkey = PEM_read_bio_PUBKEY(b, NULL, NULL, NULL);
    260 
    261     md = EVP_get_digestbyname("sha256");
    262     mdctx = EVP_MD_CTX_new();
    263 
    264     if (pkey != NULL) {
    265         xs *sig = NULL;
    266         int s_size;
    267 
    268         /* de-base64 */
    269         sig = xs_base64_dec(b64sig,  &s_size);
    270 
    271         if (sig != NULL) {
    272             EVP_VerifyInit(mdctx, md);
    273             EVP_VerifyUpdate(mdctx, mem, size);
    274 
    275             r = EVP_VerifyFinal(mdctx, (unsigned char *)sig, s_size, pkey);
    276         }
    277     }
    278 
    279     EVP_MD_CTX_free(mdctx);
    280     EVP_PKEY_free(pkey);
    281     BIO_free(b);
    282 
    283     return r;
    284 }
    285 
    286 
    287 #endif /* XS_IMPLEMENTATION */
    288 
    289 #endif /* _XS_OPENSSL_H */