缘起
使用ffi给lua包一个rsa算法, 刚刚学习ffi,连文档都没仔细看过,当返回加密结果时,如果直接返回CDATA,则解密没有问题,如果转为lua string(使用ffi.string) ,则后续无法解密
调试
- 直接在lua中调试基本无法凑效
- 重新编译openssl的libcrypt库,加入调试信息,先是祭出gdb,调的晕乎乎的;然后,则可以地方修改C代码,打印调试信息
- 发现,使用ffi.string 和不使用ffi.string 的差别在于,字符串的前面一部分是相同的,后面一部分是不同的,出于对零字节的敏感,发现是从零字节之后开始不同的;猜测ffi.string()或许可以有第二个参数(悲催,因为给一个参数也好使过,所以再没看过文档)
- 查ffi的文档,发现ffi.string()是有第二个参数的,添加第二个参数,问题解决
- 这个花费了我大约2天的时间,欲哭无泪
结论
- ffi.string(cdata, len) 是有第二个参数的,如果不写第二个参数,则从第一个零字节处截断
- 学习要循序渐进,文档是要看的
成果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
local ffi = require("ffi") local Rsa = ffi.load("crypto") ffi.cdef[[ typedef struct rsa_st RSA; typedef struct bio_st BIO; typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); int RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,int padding); int RSA_private_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,int padding); int RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,int padding); int RSA_private_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,int padding); BIO *BIO_new_mem_buf(void *buf, int len); RSA *PEM_read_bio_RSA_PUBKEY(BIO *bp, RSA **x, pem_password_cb *cb, void *u); RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb, void *u); int RSA_size(const RSA *r); ]] local RSA_PKCS1_PADDING = 1; local function init_public_key(pem_key) local bio = Rsa.BIO_new_mem_buf(ffi.cast("unsigned char *", pem_key), -1) local rsa = Rsa.PEM_read_bio_RSA_PUBKEY(bio, nil, nil, nil); if rsa == nil then return nil, "parse public key fail" end return rsa, nil end local function init_private_key(pem_key) local bio = Rsa.BIO_new_mem_buf(ffi.cast("unsigned char *", pem_key), -1) local rsa = Rsa.PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil); if rsa == nil then return nil, "parse public key fail" end return rsa, nil end local function public_decrypt(key, data) local rsa, err = init_public_key(key) if err ~= nil then return nil, err end local size = tonumber(Rsa.RSA_size(rsa)) local decrypted = ffi.new("unsigned char[?]", size) data = ffi.cast("const unsigned char *" ,data) print("string len"..string.len(ffi.string(data))) local len = Rsa.RSA_public_decrypt(size, data, decrypted, rsa, RSA_PKCS1_PADDING) return ffi.string(decrypted, len),len end local function private_encrypt(key, data) local rsa, err = init_private_key(key) if err ~= nil then return nil, err end local size = tonumber(Rsa.RSA_size(rsa)) local encrypted = ffi.new("unsigned char[?]", size) local len = Rsa.RSA_private_encrypt(#data, data, encrypted, rsa, RSA_PKCS1_PADDING) return ffi.string(encrypted, len),len end |