diff options
Diffstat (limited to 'server/resty/evp.lua')
-rw-r--r-- | server/resty/evp.lua | 804 |
1 files changed, 0 insertions, 804 deletions
diff --git a/server/resty/evp.lua b/server/resty/evp.lua deleted file mode 100644 index 584ff5a..0000000 --- a/server/resty/evp.lua +++ /dev/null @@ -1,804 +0,0 @@ -local ffi = require "ffi" -local ffi_copy = ffi.copy -local ffi_gc = ffi.gc -local ffi_new = ffi.new -local ffi_string = ffi.string -local ffi_cast = ffi.cast -local _C = ffi.C - -local _M = { _VERSION = "0.2.3" } - -local ngx = ngx - - -local CONST = { - SHA256_DIGEST = "SHA256", - SHA512_DIGEST = "SHA512", - -- ref : https://github.com/openssl/openssl/blob/master/include/openssl/rsa.h - RSA_PKCS1_PADDING = 1, - RSA_SSLV23_PADDING = 2, - RSA_NO_PADDING = 3, - RSA_PKCS1_OAEP_PADDING = 4, - RSA_X931_PADDING = 5, - RSA_PKCS1_PSS_PADDING = 6, - -- ref : https://github.com/openssl/openssl/blob/master/include/openssl/evp.h - NID_rsaEncryption = 6, - EVP_PKEY_RSA = 6, - EVP_PKEY_ALG_CTRL = 0x1000, - EVP_PKEY_CTRL_RSA_PADDING = 0x1000 + 1, - - EVP_PKEY_OP_TYPE_CRYPT = 768, - EVP_PKEY_CTRL_RSA_OAEP_MD = 0x1000 + 9 -} -_M.CONST = CONST - - --- Reference: https://wiki.openssl.org/index.php/EVP_Signing_and_Verifying -ffi.cdef[[ -// Error handling -unsigned long ERR_get_error(void); -const char * ERR_reason_error_string(unsigned long e); - -// Basic IO -typedef struct bio_st BIO; -typedef struct bio_method_st BIO_METHOD; -BIO_METHOD *BIO_s_mem(void); -BIO * BIO_new(BIO_METHOD *type); -int BIO_puts(BIO *bp,const char *buf); -void BIO_vfree(BIO *a); -int BIO_write(BIO *b, const void *buf, int len); - -// RSA -typedef struct rsa_st RSA; -int RSA_size(const RSA *rsa); -void RSA_free(RSA *rsa); -typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); -RSA * PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb, - void *u); -RSA * PEM_read_bio_RSAPublicKey(BIO *bp, RSA **rsa, pem_password_cb *cb, - void *u); - -// EC_KEY -typedef struct ec_key_st EC_KEY; -void EC_KEY_free(EC_KEY *key); -EC_KEY * PEM_read_bio_ECPrivateKey(BIO *bp, EC_KEY **key, pem_password_cb *cb, - void *u); -EC_KEY * PEM_read_bio_ECPublicKey(BIO *bp, EC_KEY **key, pem_password_cb *cb, - void *u); -// EVP PKEY -typedef struct evp_pkey_st EVP_PKEY; -typedef struct engine_st ENGINE; -EVP_PKEY *EVP_PKEY_new(void); -int EVP_PKEY_set1_RSA(EVP_PKEY *pkey,RSA *key); -int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey,EC_KEY *key); -EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, - const unsigned char *key, int keylen); -void EVP_PKEY_free(EVP_PKEY *key); -int i2d_RSA(RSA *a, unsigned char **out); - -// Additional typedef of ECC operations (DER/RAW sig conversion) -typedef struct bignum_st BIGNUM; -BIGNUM *BN_new(void); -void BN_free(BIGNUM *a); -int BN_num_bits(const BIGNUM *a); -int BN_bn2bin(const BIGNUM *a, unsigned char *to); -BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); -char *BN_bn2hex(const BIGNUM *a); - - -typedef struct ECDSA_SIG_st { - BIGNUM *r; - BIGNUM *s;} ECDSA_SIG; -ECDSA_SIG* ECDSA_SIG_new(void); -int i2d_ECDSA_SIG(const ECDSA_SIG *sig, unsigned char **pp); -ECDSA_SIG* d2i_ECDSA_SIG(ECDSA_SIG **sig, unsigned char **pp, -long len); -void ECDSA_SIG_free(ECDSA_SIG *sig); - -typedef struct ecgroup_st EC_GROUP; - -EC_GROUP *EC_KEY_get0_group(const EC_KEY *key); -EC_KEY *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey); -int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order, void *ctx); - - -// PUBKEY -EVP_PKEY *PEM_read_bio_PUBKEY(BIO *bp, EVP_PKEY **x, - pem_password_cb *cb, void *u); - -// X509 -typedef struct x509_st X509; -X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u); -EVP_PKEY * X509_get_pubkey(X509 *x); -void X509_free(X509 *a); -void EVP_PKEY_free(EVP_PKEY *key); -int i2d_X509(X509 *a, unsigned char **out); -X509 *d2i_X509_bio(BIO *bp, X509 **x); - -// X509 store -typedef struct x509_store_st X509_STORE; -typedef struct X509_crl_st X509_CRL; -X509_STORE *X509_STORE_new(void ); -int X509_STORE_add_cert(X509_STORE *ctx, X509 *x); - // Use this if we want to load the certs directly from a variables -int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x); -int X509_STORE_load_locations (X509_STORE *ctx, - const char *file, const char *dir); -void X509_STORE_free(X509_STORE *v); - -// X509 store context -typedef struct x509_store_ctx_st X509_STORE_CTX; -X509_STORE_CTX *X509_STORE_CTX_new(void); -int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, - X509 *x509, void *chain); -int X509_verify_cert(X509_STORE_CTX *ctx); -void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx); -int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); -const char *X509_verify_cert_error_string(long n); -void X509_STORE_CTX_free(X509_STORE_CTX *ctx); - -// EVP Sign/Verify -typedef struct env_md_ctx_st EVP_MD_CTX; -typedef struct env_md_st EVP_MD; -typedef struct evp_pkey_ctx_st EVP_PKEY_CTX; -const EVP_MD *EVP_get_digestbyname(const char *name); - -//OpenSSL 1.0 -EVP_MD_CTX *EVP_MD_CTX_create(void); -void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); - -//OpenSSL 1.1 -EVP_MD_CTX *EVP_MD_CTX_new(void); -void EVP_MD_CTX_free(EVP_MD_CTX *ctx); - -int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); -int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, - const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); -int EVP_DigestUpdate(EVP_MD_CTX *ctx,const void *d, - size_t cnt); -int EVP_DigestSignFinal(EVP_MD_CTX *ctx, - unsigned char *sigret, size_t *siglen); - -int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, - const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); -int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, - unsigned char *sig, size_t siglen); - -// Fingerprints -int X509_digest(const X509 *data,const EVP_MD *type, - unsigned char *md, unsigned int *len); - -//EVP encrypt decrypt -EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); -void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx); - -int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, - int cmd, int p1, void *p2); - -int EVP_PKEY_size(EVP_PKEY *pkey); - -int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx); -int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, - unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen); - -int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx); -int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, - unsigned char *out, size_t *outlen, - const unsigned char *in, size_t inlen); - - -]] - - -local function _err(ret) - -- The openssl error queue can have multiple items, print them all separated by ': ' - local errs = {} - local code = _C.ERR_get_error() - while code ~= 0 do - table.insert(errs, 1, ffi_string(_C.ERR_reason_error_string(code))) - code = _C.ERR_get_error() - end - - if #errs == 0 then - return ret, "Zero error code (null arguments?)" - end - return ret, table.concat(errs, ": ") -end - -local ctx_new, ctx_free -local openssl11, e = pcall(function () - local ctx = _C.EVP_MD_CTX_new() - _C.EVP_MD_CTX_free(ctx) -end) - -ngx.log(ngx.DEBUG, "openssl11=", openssl11, " err=", e) - -if openssl11 then - ctx_new = function () - return _C.EVP_MD_CTX_new() - end - ctx_free = function (ctx) - ffi_gc(ctx, _C.EVP_MD_CTX_free) - end -else - ctx_new = function () - local ctx = _C.EVP_MD_CTX_create() - return ctx - end - ctx_free = function (ctx) - ffi_gc(ctx, _C.EVP_MD_CTX_destroy) - end -end - -local function _new_key(self, opts) - local bio = _C.BIO_new(_C.BIO_s_mem()) - ffi_gc(bio, _C.BIO_vfree) - if _C.BIO_puts(bio, opts.pem_private_key) < 0 then - return _err() - end - - local pass - if opts.password then - local plen = #opts.password - pass = ffi_new("unsigned char[?]", plen + 1) - ffi_copy(pass, opts.password, plen) - end - - local key = nil - if self.algo == "RSA" then - key = _C.PEM_read_bio_RSAPrivateKey(bio, nil, nil, pass) - ffi_gc(key, _C.RSA_free) - elseif self.algo == "ECDSA" then - key = _C.PEM_read_bio_ECPrivateKey(bio, nil, nil, pass) - ffi_gc(key, _C.EC_KEY_free) - end - - if not key then - return _err() - end - - local evp_pkey = _C.EVP_PKEY_new() - if evp_pkey == nil then - return _err() - end - - ffi_gc(evp_pkey, _C.EVP_PKEY_free) - if self.algo == "RSA" then - if _C.EVP_PKEY_set1_RSA(evp_pkey, key) ~= 1 then - return _err() - end - elseif self.algo == "ECDSA" then - if _C.EVP_PKEY_set1_EC_KEY(evp_pkey, key) ~= 1 then - return _err() - end - end - - self.evp_pkey = evp_pkey - return self, nil -end - -local function _create_evp_ctx(self, encrypt) - self.ctx = _C.EVP_PKEY_CTX_new(self.evp_pkey, nil) - if self.ctx == nil then - return _err() - end - - ffi_gc(self.ctx, _C.EVP_PKEY_CTX_free) - - local md = _C.EVP_get_digestbyname(self.digest_alg) - if ffi_cast("void *", md) == nil then - return nil, "Unknown message digest" - end - - if encrypt then - if _C.EVP_PKEY_encrypt_init(self.ctx) <= 0 then - return _err() - end - else - if _C.EVP_PKEY_decrypt_init(self.ctx) <= 0 then - return _err() - end - end - - if _C.EVP_PKEY_CTX_ctrl(self.ctx, CONST.EVP_PKEY_RSA, -1, CONST.EVP_PKEY_CTRL_RSA_PADDING, - self.padding, nil) <= 0 then - return _err() - end - - if self.padding == CONST.RSA_PKCS1_OAEP_PADDING then - if _C.EVP_PKEY_CTX_ctrl(self.ctx, CONST.EVP_PKEY_RSA, CONST.EVP_PKEY_OP_TYPE_CRYPT, - CONST.EVP_PKEY_CTRL_RSA_OAEP_MD, 0, ffi_cast("void *", md)) <= 0 then - return _err() - end - end - - return self.ctx -end - -local RSASigner = {algo="RSA"} -_M.RSASigner = RSASigner - ---- Create a new RSASigner --- @param pem_private_key A private key string in PEM format --- @param password password for the private key (if required) --- @returns RSASigner, err_string -function RSASigner.new(self, pem_private_key, password) - return _new_key ( - self, - { - pem_private_key = pem_private_key, - password = password - } - ) -end - - ---- Sign a message --- @param message The message to sign --- @param digest_name The digest format to use (e.g., "SHA256") --- @returns signature, error_string -function RSASigner.sign(self, message, digest_name) - local buf = ffi_new("unsigned char[?]", 1024) - local len = ffi_new("size_t[1]", 1024) - - local ctx = ctx_new() - if ctx == nil then - return _err() - end - ctx_free(ctx) - - local md = _C.EVP_get_digestbyname(digest_name) - if md == nil then - return _err() - end - - if _C.EVP_DigestInit_ex(ctx, md, nil) ~= 1 then - return _err() - end - - local ret = _C.EVP_DigestSignInit(ctx, nil, md, nil, self.evp_pkey) - if ret ~= 1 then - return _err() - end - if _C.EVP_DigestUpdate(ctx, message, #message) ~= 1 then - return _err() - end - if _C.EVP_DigestSignFinal(ctx, buf, len) ~= 1 then - return _err() - end - return ffi_string(buf, len[0]), nil -end - - -local ECSigner = {algo="ECDSA"} -_M.ECSigner = ECSigner - ---- Create a new ECSigner --- @param pem_private_key A private key string in PEM format --- @param password password for the private key (if required) --- @returns ECSigner, err_string -function ECSigner.new(self, pem_private_key, password) - return RSASigner.new(self, pem_private_key, password) -end - ---- Sign a message with ECDSA --- @param message The message to sign --- @param digest_name The digest format to use (e.g., "SHA256") --- @returns signature, error_string -function ECSigner.sign(self, message, digest_name) - return RSASigner.sign(self, message, digest_name) -end - ---- Converts a ASN.1 DER signature to RAW r,s --- @param signature The ASN.1 DER signature --- @returns signature, error_string -function ECSigner.get_raw_sig(self, signature) - if not signature then - return nil, "Must pass a signature to convert" - end - local sig_ptr = ffi_new("unsigned char *[1]") - local sig_bin = ffi_new("unsigned char [?]", #signature) - ffi_copy(sig_bin, signature, #signature) - - sig_ptr[0] = sig_bin - local sig = _C.d2i_ECDSA_SIG(nil, sig_ptr, #signature) - ffi_gc(sig, _C.ECDSA_SIG_free) - - local rbytes = math.floor((_C.BN_num_bits(sig.r)+7)/8) - local sbytes = math.floor((_C.BN_num_bits(sig.s)+7)/8) - - -- Ensure we copy the BN in a padded form - local ec = _C.EVP_PKEY_get0_EC_KEY(self.evp_pkey) - local ecgroup = _C.EC_KEY_get0_group(ec) - - local order = _C.BN_new() - ffi_gc(order, _C.BN_free) - - -- res is an int, if 0, curve not found - local res = _C.EC_GROUP_get_order(ecgroup, order, nil) - - -- BN_num_bytes is a #define, so have to use BN_num_bits - local order_size_bytes = math.floor((_C.BN_num_bits(order)+7)/8) - local resbuf_len = order_size_bytes *2 - local resbuf = ffi_new("unsigned char[?]", resbuf_len) - - -- Let's whilst preserving MSB - _C.BN_bn2bin(sig.r, resbuf + order_size_bytes - rbytes) - _C.BN_bn2bin(sig.s, resbuf + (order_size_bytes*2) - sbytes) - - local raw = ffi_string(resbuf, resbuf_len) - return raw, nil -end - -local RSAVerifier = {} -_M.RSAVerifier = RSAVerifier - - ---- Create a new RSAVerifier --- @param key_source An instance of Cert or PublicKey used for verification --- @returns RSAVerifier, error_string -function RSAVerifier.new(self, key_source) - if not key_source then - return nil, "You must pass in an key_source for a public key" - end - local evp_public_key = key_source.public_key - self.evp_pkey = evp_public_key - return self, nil -end - ---- Verify a message is properly signed --- @param message The original message --- @param the signature to verify --- @param digest_name The digest type that was used to sign --- @returns bool, error_string -function RSAVerifier.verify(self, message, sig, digest_name) - local md = _C.EVP_get_digestbyname(digest_name) - if md == nil then - return _err(false) - end - - local ctx = ctx_new() - if ctx == nil then - return _err(false) - end - ctx_free(ctx) - - if _C.EVP_DigestInit_ex(ctx, md, nil) ~= 1 then - return _err(false) - end - - local ret = _C.EVP_DigestVerifyInit(ctx, nil, md, nil, self.evp_pkey) - if ret ~= 1 then - return _err(false) - end - if _C.EVP_DigestUpdate(ctx, message, #message) ~= 1 then - return _err(false) - end - local sig_bin = ffi_new("unsigned char[?]", #sig) - ffi_copy(sig_bin, sig, #sig) - if _C.EVP_DigestVerifyFinal(ctx, sig_bin, #sig) == 1 then - return true, nil - else - return false, "Verification failed" - end -end - -local ECVerifier = {} -_M.ECVerifier = ECVerifier ---- Create a new ECVerifier --- @param key_source An instance of Cert or PublicKey used for verification --- @returns ECVerifier, error_string -function ECVerifier.new(self, key_source) - return RSAVerifier.new(self, key_source) -end - ---- Verify a message is properly signed --- @param message The original message --- @param the signature to verify --- @param digest_name The digest type that was used to sign --- @returns bool, error_string -function ECVerifier.verify(self, message, sig, digest_name) - -- We have to convert the signature back from RAW to ASN1 for verification - local der_sig, err = self:get_der_sig(sig) - if not der_sig then - return nil, err - end - return RSAVerifier.verify(self, message, der_sig, digest_name) -end - ---- Converts a RAW r,s signature to ASN.1 DER signature (ECDSA) --- @param signature The raw signature --- @returns signature, error_string -function ECVerifier.get_der_sig(self, signature) - if not signature then - return nil, "Must pass a signature to convert" - end - -- inspired from https://bit.ly/2yZxzxJ - local ec = _C.EVP_PKEY_get0_EC_KEY(self.evp_pkey) - local ecgroup = _C.EC_KEY_get0_group(ec) - - local order = _C.BN_new() - ffi_gc(order, _C.BN_free) - - -- res is an int, if 0, curve not found - local res = _C.EC_GROUP_get_order(ecgroup, order, nil) - - -- BN_num_bytes is a #define, so have to use BN_num_bits - local order_size_bytes = math.floor((_C.BN_num_bits(order)+7)/8) - - if #signature ~= 2 * order_size_bytes then - return nil, "signature length != 2 * order length" - end - - local sig_bytes = ffi_new("unsigned char [?]", #signature) - ffi_copy(sig_bytes, signature, #signature) - local ecdsa = _C.ECDSA_SIG_new() - ffi_gc(ecdsa, _C.ECDSA_SIG_free) - - -- Those do not need to be GCed as they are cleared by the ECDSA_SIG_free() - local r = _C.BN_bin2bn(sig_bytes, order_size_bytes, nil) - local s = _C.BN_bin2bn(sig_bytes + order_size_bytes, order_size_bytes, nil) - - ecdsa.r = r - ecdsa.s = s - - -- Gives us the buffer size to allocate - local der_len = _C.i2d_ECDSA_SIG(ecdsa, nil) - - local der_sig_ptr = ffi_new("unsigned char *[1]") - local der_sig_bin = ffi_new("unsigned char [?]", der_len) - der_sig_ptr[0] = der_sig_bin - der_len = _C.i2d_ECDSA_SIG(ecdsa, der_sig_ptr) - - local der_str = ffi_string(der_sig_bin, der_len) - return der_str, nil -end - - -local Cert = {} -_M.Cert = Cert - - ---- Create a new Certificate object --- @param payload A PEM or DER format X509 certificate --- @returns Cert, error_string -function Cert.new(self, payload) - if not payload then - return nil, "Must pass a PEM or binary DER cert" - end - local bio = _C.BIO_new(_C.BIO_s_mem()) - ffi_gc(bio, _C.BIO_vfree) - local x509 - if payload:find('-----BEGIN') then - if _C.BIO_puts(bio, payload) < 0 then - return _err() - end - x509 = _C.PEM_read_bio_X509(bio, nil, nil, nil) - else - if _C.BIO_write(bio, payload, #payload) < 0 then - return _err() - end - x509 = _C.d2i_X509_bio(bio, nil) - end - if x509 == nil then - return _err() - end - ffi_gc(x509, _C.X509_free) - self.x509 = x509 - local public_key, err = self:get_public_key() - if not public_key then - return nil, err - end - - ffi_gc(public_key, _C.EVP_PKEY_free) - - self.public_key = public_key - return self, nil -end - - ---- Retrieve the DER format of the certificate --- @returns Binary DER format, error_string -function Cert.get_der(self) - local bufp = ffi_new("unsigned char *[1]") - local len = _C.i2d_X509(self.x509, bufp) - if len < 0 then - return _err() - end - local der = ffi_string(bufp[0], len) - return der, nil -end - ---- Retrieve the cert fingerprint --- @param digest_name the Type of digest to use (e.g., "SHA256") --- @returns fingerprint_string, error_string -function Cert.get_fingerprint(self, digest_name) - local md = _C.EVP_get_digestbyname(digest_name) - if md == nil then - return _err() - end - local buf = ffi_new("unsigned char[?]", 32) - local len = ffi_new("unsigned int[1]", 32) - if _C.X509_digest(self.x509, md, buf, len) ~= 1 then - return _err() - end - local raw = ffi_string(buf, len[0]) - local t = {} - raw:gsub('.', function (c) table.insert(t, string.format('%02X', string.byte(c))) end) - return table.concat(t, ":"), nil -end - ---- Retrieve the public key from the CERT --- @returns An OpenSSL EVP PKEY object representing the public key, error_string -function Cert.get_public_key(self) - local evp_pkey = _C.X509_get_pubkey(self.x509) - if evp_pkey == nil then - return _err() - end - - return evp_pkey, nil -end - ---- Verify the Certificate is trusted --- @param trusted_cert_file File path to a list of PEM encoded trusted certificates --- @return bool, error_string -function Cert.verify_trust(self, trusted_cert_file) - local store = _C.X509_STORE_new() - if store == nil then - return _err(false) - end - ffi_gc(store, _C.X509_STORE_free) - if _C.X509_STORE_load_locations(store, trusted_cert_file, nil) ~=1 then - return _err(false) - end - - local ctx = _C.X509_STORE_CTX_new() - if store == nil then - return _err(false) - end - ffi_gc(ctx, _C.X509_STORE_CTX_free) - if _C.X509_STORE_CTX_init(ctx, store, self.x509, nil) ~= 1 then - return _err(false) - end - - if _C.X509_verify_cert(ctx) ~= 1 then - local code = _C.X509_STORE_CTX_get_error(ctx) - local msg = ffi_string(_C.X509_verify_cert_error_string(code)) - _C.X509_STORE_CTX_cleanup(ctx) - return false, msg - end - _C.X509_STORE_CTX_cleanup(ctx) - return true, nil - -end - -local PublicKey = {} -_M.PublicKey = PublicKey - ---- Create a new PublicKey object --- --- If a PEM fornatted key is provided, the key must start with --- --- ----- BEGIN PUBLIC KEY ----- --- --- @param payload A PEM or DER format public key file --- @return PublicKey, error_string -function PublicKey.new(self, payload) - if not payload then - return nil, "Must pass a PEM or binary DER public key" - end - local bio = _C.BIO_new(_C.BIO_s_mem()) - ffi_gc(bio, _C.BIO_vfree) - local pkey - if payload:find('-----BEGIN') then - if _C.BIO_puts(bio, payload) < 0 then - return _err() - end - pkey = _C.PEM_read_bio_PUBKEY(bio, nil, nil, nil) - else - if _C.BIO_write(bio, payload, #payload) < 0 then - return _err() - end - pkey = _C.d2i_PUBKEY_bio(bio, nil) - end - if pkey == nil then - return _err() - end - ffi_gc(pkey, _C.EVP_PKEY_free) - self.public_key = pkey - return self, nil -end - -local RSAEncryptor= {} -_M.RSAEncryptor = RSAEncryptor - ---- Create a new RSAEncryptor --- @param key_source An instance of Cert or PublicKey used for verification --- @param padding padding type to use --- @param digest_alg digest algorithm to use --- @returns RSAEncryptor, err_string -function RSAEncryptor.new(self, key_source, padding, digest_alg) - if not key_source then - return nil, "You must pass in an key_source for a public key" - end - local evp_public_key = key_source.public_key - self.evp_pkey = evp_public_key - self.padding = padding or CONST.RSA_PKCS1_OAEP_PADDING - self.digest_alg = digest_alg or CONST.SHA256_DIGEST - return self, nil -end - - - ---- Encrypts the payload --- @param payload plain text payload --- @returns encrypted payload, error_string -function RSAEncryptor.encrypt(self, payload) - - local ctx, err_str = _create_evp_ctx(self, true) - - if not ctx then - return nil, err_str - end - local len = ffi_new("size_t [1]") - if _C.EVP_PKEY_encrypt(ctx, nil, len, payload, #payload) <= 0 then - return _err() - end - local buf = ffi_new("unsigned char[?]", len[0]) - if _C.EVP_PKEY_encrypt(ctx, buf, len, payload, #payload) <= 0 then - return _err() - end - - return ffi_string(buf, len[0]) - -end - - -local RSADecryptor= {algo="RSA"} -_M.RSADecryptor = RSADecryptor - ---- Create a new RSADecryptor --- @param pem_private_key A private key string in PEM format --- @param password password for the private key (if required) --- @param padding padding type to use --- @param digest_alg digest algorithm to use --- @returns RSADecryptor, error_string -function RSADecryptor.new(self, pem_private_key, password, padding, digest_alg) - self.padding = padding or CONST.RSA_PKCS1_OAEP_PADDING - self.digest_alg = digest_alg or CONST.SHA256_DIGEST - return _new_key ( - self, - { - pem_private_key = pem_private_key, - password = password - } - ) -end - ---- Decrypts the cypher text --- @param cypher_text encrypted payload --- @param padding rsa pading mode to use, Defaults to RSA_PKCS1_PADDING -function RSADecryptor.decrypt(self, cypher_text) - - local ctx, err_code, err_str = _create_evp_ctx(self, false) - - if not ctx then - return nil, err_code, err_str - end - - local len = ffi_new("size_t [1]") - if _C.EVP_PKEY_decrypt(ctx, nil, len, cypher_text, #cypher_text) <= 0 then - return _err() - end - - local buf = ffi_new("unsigned char[?]", len[0]) - if _C.EVP_PKEY_decrypt(ctx, buf, len, cypher_text, #cypher_text) <= 0 then - return _err() - end - - return ffi_string(buf, len[0]) - -end - -return _M |