Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Python wrap #21

Open
wants to merge 35 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1e1a586
Add python wrapper of libidpasslite.so library
May 25, 2022
0167c04
Fix type issue, now basic working
May 25, 2022
5bd797d
Add VScode IDE starter script
May 26, 2022
d343991
Working functions: create_card_with_face, authenticate_card_with_pin
May 26, 2022
a52e7b4
Update README.md
typelogic May 26, 2022
60b5057
Update README.md
typelogic May 26, 2022
c07168d
Update README.md
typelogic May 26, 2022
d3946fe
Update README.md
typelogic May 26, 2022
eef664c
Update README.md
typelogic May 26, 2022
b35b0a2
Update README.md
typelogic May 26, 2022
e93095b
Relocate idpass.h constants
May 26, 2022
396e656
Update README.md
typelogic May 26, 2022
37c1257
Generate qrcode.svg file as output
May 27, 2022
5637a29
Fix svg
May 27, 2022
0d42808
Add run.sh for test run
May 27, 2022
30a1c26
chmod a+rx run.sh
May 27, 2022
4378736
Remove unused folder
May 27, 2022
10b7476
Update readme
May 27, 2022
f6d06f0
Update readme
May 27, 2022
4cee60c
Update readme
May 27, 2022
1e6d8e6
Update readme
May 27, 2022
cec52a9
Remove unused method in main.py
May 27, 2022
f27561b
Update main.py
typelogic May 27, 2022
6a47d36
Minor cleanup
May 27, 2022
d4f6972
Generate a readable QR code
May 30, 2022
a422b02
Update main.py
typelogic May 30, 2022
9e5ae33
Fix bytearray compat issue on latest protobuf
May 30, 2022
df1b4f3
Add complete Florence Ident fields
Jun 1, 2022
947c335
Add Card object
Jun 2, 2022
a6da071
Fix a crucial bug in qrcode.cpp; Add Card object
Jun 2, 2022
042d30b
Update README.md
typelogic Jun 2, 2022
2767f02
Add notes
typelogic Jun 2, 2022
2621fb2
Update README.md
typelogic Jun 2, 2022
b569939
Update README.md
typelogic Jun 2, 2022
814abdf
minor
Jun 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A library to create and issue biometrically-binding QR code identity cards.

![Alt text](idpasslite_qr.png?raw=true "api")

## Getting started
## Getting Started

This library can be used in C and C++ projects. Download `libidpasslite.so` from the [Releases](https://github.com/idpass/idpass-lite/releases) page or [build it from source](https://github.com/idpass/idpass-lite/wiki/Building-from-source), then add it to the project that will use it.

Expand Down
4 changes: 2 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ build_desktop_idpasslite() {
assert_exists cmake
assert_exists make
# for #include <jni.h>
assert_exists java
assert_exists javac
# assert_exists java
# assert_exists javac

build_release
}
Expand Down
16 changes: 16 additions & 0 deletions lib/src/idpass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2107,6 +2107,22 @@ unsigned char* idpass_lite_merge_CardDetails(unsigned char* d1buf,
return buf;
}

MODULE_API
char* idpass_lite_qrcodesvg(void* self,
const unsigned char* data,
int data_len)
{
if (self == nullptr || data == nullptr
|| data_len <= 0)
{
return nullptr;
}
Context* context = (Context*)self;
std::lock_guard<std::mutex> guard(context->mtx); // protect the static output buf

return qrcode_svg(data, data_len, context->qrcode_ecc);
}

#ifdef __cplusplus
}
#endif
6 changes: 6 additions & 0 deletions lib/src/idpass.h
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,12 @@ unsigned char* idpass_lite_merge_CardDetails(unsigned char* d1buf,
int d2buf_len,
int* outlen);


MODULE_API
char* idpass_lite_qrcodesvg(void* self,
const unsigned char* data,
int data_len);

#ifdef __cplusplus
}
#endif
73 changes: 73 additions & 0 deletions lib/src/qrcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ static const uint16_t NUM_RAW_DATA_MODULES = 567;

#endif

const char* str1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
const char* str2 = "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">";
const char* str4 = "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>";
const char* str5 = "\t<path d=\"";
const char* str7 = "\" fill=\"#000000\"/>";
const char* str8 = "</svg>";

static void StringBuilder_append(char *dest, const char *stuff)
{
strcat(dest, stuff);
}

static int max(int a, int b)
{
if (a > b) {
Expand Down Expand Up @@ -1225,3 +1237,64 @@ void qrcode_getHex(QRCode *qrcode, char *result) {

}
*/

char *qrcode_svg(const unsigned char *data,
int data_len,
int ecc)
{
// Create the QR code
QRCode qrcode;
int version = 1; // start with version 1

int status = qrcode_initText(&qrcode,
nullptr,
version,
ecc,
reinterpret_cast<const char *>(data),
data_len);

if (status != 0)
return NULL;

char str3[100];
static char output[524288];
char str6[100];

output[0] = '\0';

auto SetBit = [](unsigned char A[], int k) {
// Set the bit at the k-th position in A[i]
A[k / 8] |= 1 << (k % 8);
};

int n = qrcode.size;
int border = 3;

sprintf(str3, "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 %d %d\" stroke=\"none\">", n + border * 2, n + border * 2);

StringBuilder_append(output, str1);
StringBuilder_append(output, str2);
StringBuilder_append(output, str3);
StringBuilder_append(output, str4);
StringBuilder_append(output, str5);

for (uint8_t y = 0; y < n; y++) {
// Each horizontal module
for (uint8_t x = 0; x < n; x++) {
int p = (y * n + x);
if (qrcode_getModule(&qrcode, x, y)) {
// SetBit(pixels, p);
if (x != 0 || y != 0) {
StringBuilder_append(output, " ");
}
sprintf(str6, "M%d,%dh1v1h-1z", y + border, x + border);
StringBuilder_append(output, str6);
}
}
}

StringBuilder_append(output, str7);
StringBuilder_append(output, str8);
// printf("%s\n", output);
return output;
}
4 changes: 4 additions & 0 deletions lib/src/qrcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ uint8_t *qrcode_getpixel(const unsigned char *data,
int *,
int ecc);

char *qrcode_svg(const unsigned char *data,
int data_len,
int ecc);

#ifdef __cplusplus
}
#endif /* __cplusplus */
Expand Down
222 changes: 222 additions & 0 deletions python-wrap/IDPassLite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
from ctypes import *
import sys
from IDPassNative import IDPassNative
import idpasslite_pb2
import api_pb2

class Helper(object):
def generate_encryption_key():
key = (c_ubyte * 32)()
IDPassNative.lib.idpass_lite_generate_encryption_key(key, len(key))
return key

def generate_secret_signature_keypair():
ver = (c_ubyte * 32)()
sig = (c_ubyte * 64)()
IDPassNative.lib.idpass_lite_generate_secret_signature_keypair(ver, len(ver), sig, len(sig))
return (ver, sig)

def getPublicKey(privatekey):
#print(type(privatekey))
pubkey = (c_ubyte * 32)(*privatekey[32:64])
return pubkey

class Reader(object):
ctx = 0
m_keySet = None

def __init__(self, keySet):
self.m_keySet = keySet
keysetba = bytearray(self.m_keySet.SerializeToString())
self.ctx = IDPassNative.lib.idpass_lite_init((c_ubyte * len(keysetba))(*keysetba), len(keysetba), None , 0)
if self.ctx is None:
raise ValueError('fail to initialize with specified keys')
self.ioctl(IDPassNative.IOCTL_SET_ACL, IDPassNative.DETAIL_SURNAME | IDPassNative.DETAIL_GIVENNAME)

def ioctl(self, cmd, param):
#IOCTL_SET_FDIM = 0x02 # full mode
ioctlcmd = [ cmd, param ]
IDPassNative.lib.idpass_lite_ioctl( self.ctx, None, (c_ubyte * len(ioctlcmd))(*ioctlcmd), len(ioctlcmd) )

# ioctl cannot influence the setting and there is no more setting
# since the template are already generated. There is no context
# this is a static method
def compare_face_template(self, input1, input2):
fdiff = c_float(10.0)
status = IDPassNative.lib.idpass_lite_compare_face_template(
(c_ubyte *len(input1))(*input1),
len(input1),
(c_ubyte *len(input2))(*input2),
len(input2),
byref(fdiff))
if status != 0:
raise ValueError('fail to compute face template error code [%d]' % status)
return fdiff.value

def computeFullTemplate(self, inputfile):
photo = open(inputfile, "rb").read()
# 128 floats with 4 bytes per float (128*4 = 512)
# Do not (c_ubyte * 128*4)() as that creates two-dimensional
# array where len(fdim) = 4 and len(fdim[0]) = 128
fdim = (c_ubyte *512)()
facecount = IDPassNative.lib.idpass_lite_face128dbuf(
self.ctx,
(c_ubyte * len(photo))(*photo),
len(photo),
fdim
)

if facecount != 1:
raise ValueError('Dlib found %d face(s) error' % facecount)
return bytearray(fdim)

def computeHalfTemplate(self, inputfile):
photo = open(inputfile, "rb").read()
# 64 floats with 2 bytes per float (64*2 = 128)
# Do not (c_ubyte * 64*2)() as that creates two-dimensional
# array where len(fdim) = 2 and len(fdim[0]) = 64
hdim = (c_ubyte *128)()
facecount = IDPassNative.lib.idpass_lite_face64dbuf(
self.ctx,
(c_ubyte * len(photo))(*photo),
len(photo),
hdim
)

if facecount != 1:
raise ValueError('Dlib found %d face(s) error' % facecount)
return bytearray(hdim)

def computeFullTemplateAsFloats(self, inputfile):
photo = open(inputfile, "rb").read()
fdim = (c_float *128)()
facecount = IDPassNative.lib.idpass_lite_face128d(
self.ctx,
(c_ubyte * len(photo))(*photo),
len(photo),
fdim)

if facecount != 1:
raise ValueError('Dlib found %d face(s) error' % facecount)
return fdim

# The ioctl setting can switch to full or half and this influences
# the facial biometry representation either in full 512 bytes or in
# half 128 bytes, and from these representation the Euclidean
# difference is calculated. There is context as first param
def compare_face_photo(self, inputfile1, inputfile2):
photo1 = open(inputfile1, "rb").read()
photo2 = open(inputfile2, "rb").read()

fdiff = c_float(10.0)
status = IDPassNative.lib.idpass_lite_compare_face_photo(
self.ctx,
(c_ubyte * len(photo1))(*photo1),
len(photo1),
(c_ubyte * len(photo2))(*photo2),
len(photo2),
byref(fdiff))
if status != 0:
raise ValueError('fail to compute face template error code [%d]' % status)
return fdiff.value

def computeHalfTemplateAsFloats(self, inputfile):
photo = open(inputfile, "rb").read()
hdim = (c_float *64)()
facecount = IDPassNative.lib.idpass_lite_face64d(
self.ctx,
(c_ubyte * len(photo))(*photo),
len(photo),
hdim)

if facecount != 1:
raise ValueError('Dlib found %d face(s) error' % facecount)
return hdim

def create_card_with_face(self, ident):
identba = bytearray(ident.SerializeToString())
cardbalen = c_int(0)

cardba = IDPassNative.lib.idpass_lite_create_card_with_face(
self.ctx,
byref(cardbalen),
(c_ubyte * len(identba))(*identba),
len(identba)
)

if cardba is None:
raise ValueError('create card with face error')
cards = idpasslite_pb2.IDPassCards()
cards.ParseFromString(string_at(cardba, cardbalen.value))
card = Card(self.ctx, cardba, cardbalen.value)
return card

def _create_card_with_face(self, ident):
identba = bytearray(ident.SerializeToString())
cardbalen = c_int(0)

cardba = IDPassNative.lib.idpass_lite_create_card_with_face(
self.ctx,
byref(cardbalen),
(c_ubyte * len(identba))(*identba),
len(identba)
)

if cardba is None:
raise ValueError('create card with face error')
cards = idpasslite_pb2.IDPassCards()
cards.ParseFromString(string_at(cardba, cardbalen.value))
return (cards, cardba, cardbalen.value) # TODO: Improve API

# TODO: Do something like in Java below, or probably better this is done inside
# libidpasslite.so in C++ as the string type format is portable
# https://github.com/idpass/idpass-lite-java/blob/develop/src/main/java/org/idpass/lite/IDPassReader.java#L515-L547
def asQRCode(self, data, datalen):
#inputdata = (c_ubyte * len(data)).from_buffer_copy(data)
buf = IDPassNative.lib.idpass_lite_qrcodesvg(self.ctx, data, datalen)
_strip1 = str(cast(buf,c_char_p).value)[2:]
content = _strip1[:-1]
return content

def authenticateWithPin(self, cardba, cardbalen, pincode):
details = idpasslite_pb2.CardDetails()
buflen = c_int(0)
buf = IDPassNative.lib.idpass_lite_verify_card_with_pin(
self.ctx,
byref(buflen),
cardba,
cardbalen,
pincode.encode('utf-8'))
if buflen.value == 0:
return None
details.ParseFromString(string_at(buf, buflen))
return details

def freemem(self, addr):
IDPassNative.lib.idpass_lite_freemem(self.ctx, addr)

class Card(object):
def __init__(self, ctx, buf, buflen):
self.ctx = ctx
self.buf = buf
self.buflen = buflen

def asQRCodeSVG(self):
buf = IDPassNative.lib.idpass_lite_qrcodesvg(self.ctx, self.buf, self.buflen)
_strip1 = str(cast(buf,c_char_p).value)[2:]
content = _strip1[:-1]
return content

def authenticateWithPin(self, pincode):
details = idpasslite_pb2.CardDetails()
buflen = c_int(0)
buf = IDPassNative.lib.idpass_lite_verify_card_with_pin(
self.ctx,
byref(buflen),
self.buf,
self.buflen,
pincode.encode('utf-8'))
if buflen.value == 0:
return None
details.ParseFromString(string_at(buf, buflen))
return details
Loading