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