2.57
xs_openssl.h
1/* copyright (c) 2022 - 2024 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(mem);
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 */