from cffi import FFI import platform DEF_gcry_sexp=""" typedef unsigned long long size_t; typedef void *gcry_sexp_t; typedef unsigned int gcry_error_t; gcry_error_t gcry_sexp_build (gcry_sexp_t *R_SEXP, size_t *ERROFF, const char *FORMAT, ...); void gcry_sexp_release (gcry_sexp_t SEXP); void gcry_sexp_dump (gcry_sexp_t SEXP); gcry_sexp_t gcry_sexp_find_token (const gcry_sexp_t LIST, const char *TOKEN, size_t TOKLEN); const char * gcry_sexp_nth_data (const gcry_sexp_t LIST, int NUMBER, size_t *DATALEN); """ DEF_gcry_pk_sign=""" typedef void *gcry_sexp_t; typedef unsigned int gcry_error_t; gcry_error_t gcry_pk_sign (gcry_sexp_t *R_SIG, gcry_sexp_t DATA, gcry_sexp_t SKEY); """ DEF_gcry_pk_verify=""" typedef void *gcry_sexp_t; typedef unsigned int gcry_error_t; gcry_error_t gcry_pk_verify (gcry_sexp_t SIG, gcry_sexp_t DATA, gcry_sexp_t PKEY); """ DEF_gcry_pk_encrypt=""" typedef void *gcry_sexp_t; typedef unsigned int gcry_error_t; gcry_error_t gcry_pk_encrypt (gcry_sexp_t *R_CIPH, gcry_sexp_t DATA, gcry_sexp_t PKEY); """ ffi = FFI() ffi.cdef(DEF_gcry_sexp) ffi.cdef(DEF_gcry_pk_sign, override=True) ffi.cdef(DEF_gcry_pk_verify, override=True) ffi.cdef(DEF_gcry_pk_encrypt, override=True) if (platform.system() == 'Darwin'): libgcrypt = ffi.dlopen("libgcrypt.20.dylib") else: libgcrypt = ffi.dlopen("libgcrypt.so.20") FORMAT_DATA=b"(data(value %b))" FORMAT_SIG=b"(sig-val(ecdsa(r %b)(s %b)))" FORMAT_KEY_D_TMPL='(private-key(ecc(curve {0}:{1})(d%b)))' FORMAT_KEY_Q_TMPL='(private-key(ecc(curve {0}:{1})(q%b)))' class PK_libgcrypt(object): def __init__(self, dlen, cn): cn_len = len(cn) self.format_key_d = bytes(FORMAT_KEY_D_TMPL.format(cn_len,cn),"utf-8") self.format_key_q = bytes(FORMAT_KEY_Q_TMPL.format(cn_len,cn),"utf-8") self.dlen = dlen # Unfourtunately, in libgcrypt, D is signed def make_skey_by_secret(self, d): if d[0] >= 128: d = b'\x00' + d sexp = ffi.new("void **") off = ffi.new("unsigned long long *") secret = ffi.new("char []", d) r = libgcrypt.gcry_sexp_build(sexp, off, self.format_key_d, ffi.cast("int", len(d)), secret) if r != 0: raise ValueError("libgcrypt error", r) # libgcrypt.gcry_sexp_dump(sexp[0]) return sexp[0] # Unfourtunately, in libgcrypt, DATA is signed def call_pk_sign(self, d, data): skey = self.make_skey_by_secret(d) if data[0] >= 128: data = b'\x00' + data data_in_c = ffi.new("char []", data) data_sexp = ffi.new("void **") off = ffi.new("unsigned long long *") r = libgcrypt.gcry_sexp_build(data_sexp, off, FORMAT_DATA, ffi.cast("int", len(data)), data_in_c) if r != 0: raise ValueError("libgcrypt error", r) # sig_sexp = ffi.new("void **") libgcrypt.gcry_pk_sign(sig_sexp, data_sexp[0], skey) if r != 0: raise ValueError("libgcrypt error", r) # token_in_c = ffi.new("char []", b"r") r_sexp = libgcrypt.gcry_sexp_find_token(sig_sexp[0], token_in_c, 1) # libgcrypt.gcry_sexp_dump(sig_sexp[0]) length = ffi.new("size_t *") sig_r = libgcrypt.gcry_sexp_nth_data(r_sexp, 1, length) len_sig_r = length[0] token_in_c = ffi.new("char []", b"s") s_sexp = libgcrypt.gcry_sexp_find_token(sig_sexp[0], token_in_c, 1) sig_s = libgcrypt.gcry_sexp_nth_data(s_sexp, 1, length) len_sig_s = length[0] # sig = bytes(self.dlen-len_sig_r) sig += ffi.unpack(sig_r,len_sig_r) sig += bytes(self.dlen-len_sig_s) sig += ffi.unpack(sig_s,len_sig_s) return sig # Unfourtunately, in libgcrypt, Q is signed, but luckily it always # starts with 0x04 def make_skey_by_public(self, q): public = ffi.new("char []", q) sexp = ffi.new("void **") off = ffi.new("unsigned long long *") r = libgcrypt.gcry_sexp_build(sexp, off, self.format_key_q, ffi.cast("int", len(q)), public) if r != 0: raise ValueError("libgcrypt error", r) # libgcrypt.gcry_sexp_dump(sexp[0]) return sexp[0] def call_pk_encrypt(self, q, ecdh_scalar): skey = self.make_skey_by_public(q) # if ecdh_scalar[0] >= 128: ecdh_scalar = b'\x00' + ecdh_scalar len_shared = len(ecdh_scalar) shared_in_c = ffi.new("char []", ecdh_scalar) data_sexp = ffi.new("void **") off = ffi.new("unsigned long long *") r = libgcrypt.gcry_sexp_build(data_sexp, off, b"%b", ffi.cast("int", len_shared), shared_in_c) if r != 0: raise ValueError("libgcrypt error", r) # libgcrypt.gcry_sexp_dump(data_sexp[0]) ct_sexp = ffi.new("void **") r = libgcrypt.gcry_pk_encrypt(ct_sexp, data_sexp[0], skey) if r != 0: raise ValueError("libgcrypt error", r) # libgcrypt.gcry_sexp_dump(ct_sexp[0]) token_in_c = ffi.new("char []", b"s") s_sexp = libgcrypt.gcry_sexp_find_token(ct_sexp[0], token_in_c, 1) length = ffi.new("size_t *") enc_s = libgcrypt.gcry_sexp_nth_data(s_sexp, 1, length) len_s = length[0] # token_in_c = ffi.new("char []", b"e") e_sexp = libgcrypt.gcry_sexp_find_token(ct_sexp[0], token_in_c, 1) enc_e = libgcrypt.gcry_sexp_nth_data(e_sexp, 1, length) len_e = length[0] # return (ffi.unpack(enc_s,len_s), ffi.unpack(enc_e,len_e)) # Unfourtunately, in libgcrypt, DATA, R and S is signed def call_pk_verify(self, q, data, sig): skey = self.make_skey_by_public(q) if data[0] >= 128: data = b'\x00' + data data_in_c = ffi.new("char []", data) data_sexp = ffi.new("void **") off = ffi.new("unsigned long long *") r = libgcrypt.gcry_sexp_build(data_sexp, off, FORMAT_DATA, ffi.cast("int", len(data)), data_in_c) if r != 0: raise ValueError("libgcrypt error", r) sig_r = sig[0:self.dlen] if sig_r[0] >= 128: sig_r = b'\x00' + sig_r sig_s = sig[self.dlen:] if sig_s[0] >= 128: sig_s = b'\x00' + sig_s sig_r_in_c = ffi.new("char []", sig_r) sig_s_in_c = ffi.new("char []", sig_s) sig_sexp = ffi.new("void **") off = ffi.new("unsigned long long *") r = libgcrypt.gcry_sexp_build(sig_sexp, off, FORMAT_SIG, ffi.cast("int", len(sig_r)), sig_r_in_c, ffi.cast("int", len(sig_s)), sig_s_in_c) if r != 0: raise ValueError("libgcrypt error", r) # libgcrypt.gcry_sexp_dump(sig_sexp[0]) # libgcrypt.gcry_sexp_dump(data_sexp[0]) r = libgcrypt.gcry_pk_verify(sig_sexp[0], data_sexp[0], skey) return r == 0