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 */