cython pkcs11
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
This module provides functions for easily generating PKCS11 bindings. | |
Intended call method is: | |
generate_pxd(headers_directory, vendor_top_level_include_header) | |
''' | |
import re | |
import collections | |
import parsimonious.grammar | |
from parsimonious.nodes import NodeVisitor | |
def generate_pxd(include_path, header_name): | |
''' | |
include path should be the directory containing pkcs11f.h and pkcs11t.h | |
header name is the name of the header the functions will actually load from | |
''' | |
pkcs11f_data = open(include_path + '/pkcs11f.h').read() | |
pkcs11t_data = open(include_path + '/pkcs11t.h').read() | |
lines = pxd_lines( | |
parse_pkcs11f(pkcs11f_data), | |
parse_pkcs11t(pkcs11t_data)) | |
output = '# auto generated by p112pxd.py DO NOT EDIT\n' | |
output += 'cdef extern from "{0}" nogil:\n'.format(header_name) | |
output += "\n".join([e.replace('CK_PTR', '*') for e in lines]) + "\n" | |
open('pkcs11.pxd', 'wb').write(output) | |
def pxd_lines(parsed_funcs, parsed_types): | |
''' | |
converts the output from parse_pkcs11f and parse_pkcs11t | |
into a form suitable for inclusion in a pxd | |
''' | |
lines = [] | |
for name, typ in parsed_types.typedefs.items(): | |
lines.append('ctypedef' + typ + name) | |
for name, members in parsed_types.func_typedefs.items(): | |
lines.append('ctypedef CK_RV (*{0})({1})'.format( | |
name, ", ".join([typ + " " + name for typ, name in members]))) | |
for name, members in parsed_types.typedef_structs.items(): | |
lines.append('ctypedef struct {0}:'.format(name.strip())) | |
if not members: | |
lines.append(' pass') | |
for typ, name in members: | |
lines.append(' ' + typ.strip() + ' ' + name.strip()) | |
lines.append('') | |
for alias, struct in parsed_types.struct_aliases.items(): | |
#treat struct typedefs as forward-declared opaque structs | |
lines.append('cdef struct' + alias + ':') | |
lines.append(' pass') | |
for name, args in parsed_funcs.items(): | |
lines.append('CK_RV {0}({1})'.format( | |
name, ', '.join(["{0} {1}".format(*arg) for arg in args]))) | |
return_value_dict = collections.OrderedDict() | |
for name, value in parsed_types.defines.items(): | |
try: | |
int(value, 0) | |
except: | |
lines.append('#' + name + value) | |
else: | |
lines.append('unsigned long' + name + ' #' + value) | |
if name.strip().startswith('CKR_'): | |
return_value_dict[value] = name.strip()[4:] | |
lines = [" " + e for e in lines] # apply indentation | |
lines.append('') | |
lines.append('CKR_NAMES = {') | |
for val, name in return_value_dict.items(): | |
lines.append(' ' + val + ': "' + name + '",') | |
lines.append('}') | |
return lines | |
def parse_pkcs11f(data): | |
'parse pkcs11f.h to a dict of {funcname: [(arg_type, arg_name), ...]}' | |
# normalize all white-space to single spaces between tokens | |
tokenized = re.sub("\s+", " ", data, flags=re.M) | |
# run PEG grammar over tokenized output to generate AST | |
ast = TOKENIZED_PKCS11F_PARSER.parse(tokenized) | |
# use NodeVisitor subclass to extract func name and arg list from ast | |
return dict(_FuncVisitor().visit(ast)) | |
def parse_pkcs11t(data): | |
''' | |
parse pkcs11t.h, return an object with dicts of file contents as attributes | |
defines -- {name: value} | |
typedef_structs -- {name: [(type, name), ...]} | |
typedefs -- {defined type: definition} (e.g. {"CK_ULONG": "unsigned long"}) | |
struct_aliases -- {defined type: definition} (e.g. {"CK_FOO_PTR": "CK_FOO PTR"}) | |
''' | |
# collapse line continuations and eliminate C++ style comments | |
data = data.replace('\\\n', '') | |
data = re.sub("//.*\n", "", data) | |
# normalize all white-space to single spaces between tokens | |
tokenized = re.sub("\s+", " ", data, flags=re.M) | |
# run PEG grammar over tokenized output to generate AST | |
ast = TOKENIZED_PKCS11T_PARSER.parse(tokenized) | |
# extract the useful bits | |
visitor = _TypeVisitor() | |
visitor.visit(ast) | |
return visitor | |
TOKENIZED_PKCS11F_GRAMMAR = r''' | |
file = ( comment / func / " " )* | |
func = func_hdr func_args | |
func_hdr = "CK_PKCS11_FUNCTION_INFO(" name ")" | |
func_args = arg_hdr " (" arg* " ); #endif" | |
arg_hdr = " #ifdef CK_NEED_ARG_LIST" (" " comment)? | |
arg = " " type " " name ","? " " comment | |
name = identifier | |
type = identifier | |
identifier = ~"[A-Z_][A-Z0-9_]*"i | |
comment = ~"(/\*.*?\*/)"ms | |
''' | |
TOKENIZED_PKCS11F_PARSER = parsimonious.grammar.Grammar( | |
TOKENIZED_PKCS11F_GRAMMAR) | |
TOKENIZED_PKCS11T_GRAMMAR = r''' | |
file = ( comment / define / typedef / struct_typedef / func_typedef / struct_alias_typedef / ignore )* | |
typedef = " typedef" type identifier ";" | |
struct_typedef = " typedef struct" identifier " "? "{" (comment / member)* " }" identifier ";" | |
struct_alias_typedef = " typedef struct" identifier " CK_PTR"? identifier ";" | |
func_typedef = " typedef CK_CALLBACK_FUNCTION(CK_RV," identifier ")(" (identifier identifier ","? comment?)* " );" member = identifier identifier array_size? ";" comment? | |
array_size = "[" ~"[0-9]"+ "]" | |
define = "#define" identifier (hexval / decval / " (~0UL)" / identifier / ~" \([A-Z_]*\|0x[0-9]{8}\)" ) | |
hexval = ~" 0x[A-F0-9]{8}"i | |
decval = ~" [0-9]+" | |
type = " unsigned char" / " unsigned long int" / " long int" / (identifier " CK_PTR") / identifier | |
identifier = " "? ~"[A-Z_][A-Z0-9_]*"i | |
comment = " "? ~"(/\*.*?\*/)"ms | |
ignore = ( " #ifndef" identifier ) / " #endif" / " " | |
''' | |
TOKENIZED_PKCS11T_PARSER = parsimonious.grammar.Grammar( | |
TOKENIZED_PKCS11T_GRAMMAR) | |
class _FuncVisitor(NodeVisitor): | |
def visit_func(self, node, visited_children): | |
func = node | |
# note this depends on the structure of func parsing grammar | |
func_hdr, func_args = func.children | |
fname = func_hdr.children[1].text | |
arg_nodes = func_args.children[2].children # step into arg* | |
args = [] | |
for node in arg_nodes: | |
typ, name = [e for e in node.children if e.expr_name == 'identifier'] | |
args.append((typ.text, name.text)) | |
return (fname, args) | |
def generic_visit(self, node, visited_children): | |
while len(visited_children) == 1: | |
visited_children = visited_children[0] | |
return [e for e in visited_children if type(e) != list or e != []] | |
class _TypeVisitor(NodeVisitor): | |
def __init__(self): | |
self.defines = collections.OrderedDict() | |
self.typedef_structs = collections.OrderedDict() | |
self.typedefs = collections.OrderedDict() | |
self.struct_aliases = collections.OrderedDict() | |
self.func_typedefs = collections.OrderedDict() | |
def visit_define(self, node, visited_children): | |
name = node.children[1].text | |
value = node.children[2].text | |
self.defines[name] = value | |
def visit_struct_typedef(self, node, visited_children): | |
member_nodes = node.children[4] | |
member_nodes = [m.children[0] for m in member_nodes] | |
members = [] | |
for mn in member_nodes: | |
if mn.expr_name != 'member': | |
continue # skip comments | |
members.append((mn.children[0].text, mn.children[1].text + mn.children[2].text)) | |
self.typedef_structs[node.children[1].text] = members | |
def visit_struct_alias_typedef(self, node, visited_children): | |
self.struct_aliases[node.children[3].text] = node.children[1].text + node.children[2].text | |
def visit_typedef(self, node, visited_children): | |
self.typedefs[node.children[2].text] = node.children[1].text | |
def visit_func_typedef(self, node, visited_children): | |
name = node.children[1].text | |
member_nodes = node.children[3] | |
members = [] | |
for mn in member_nodes: | |
members.append((mn.children[0].text, mn.children[1].text)) | |
self.func_typedefs[name] = members | |
def generic_visit(self, node, visited_children): | |
return |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment