qrcode_certificate: prototype
This commit is contained in:
parent
2d84144ee3
commit
629bbc155b
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,24 @@
|
|||
import base64
|
||||
import binascii
|
||||
|
||||
import nacl.signing
|
||||
|
||||
|
||||
def base64url_decode(raw):
|
||||
rem = len(raw) % 4
|
||||
if rem > 0:
|
||||
raw += b'=' * (4 - rem)
|
||||
return base64.urlsafe_b64decode(raw)
|
||||
|
||||
|
||||
hex_verify_key = '79a441b73a99ead8c4446aa36c973dd23787d459122fe2169232a07a181e37a9'
|
||||
|
||||
verify_key = nacl.signing.VerifyKey(binascii.unhexlify(hex_verify_key))
|
||||
|
||||
message = 'eyJ0eXBlIjogImNlcnRpZmljYXQtemZlIiwgImltbWF0cmljdWxhdGlvbiI6ICJBWi0xMjM0LUJFIiwgIm5vbSI6ICJKZWFuIE1hcmMgRHVwb25kIiwgImRhdGVfZW1pc3Npb24iOiAiMjAyMi0wNi0wNyJ9.Nno9aCyay5sxducJtpmuBDP8VBoRdwe4rfw3ohBP1bSBnmFJf17sXQdMDGDXt6eN6bzyfeI3mDrmpTnVDf81DQ'
|
||||
|
||||
parts = message.encode().split(b'.')
|
||||
payload = base64url_decode(parts[0])
|
||||
signature = base64url_decode(parts[1])
|
||||
|
||||
assert verify_key.verify(payload, signature)
|
|
@ -0,0 +1,49 @@
|
|||
import base64
|
||||
import binascii
|
||||
import json
|
||||
|
||||
import nacl.signing
|
||||
import qrcode
|
||||
import qrcode.constants
|
||||
import qrcode.image.svg
|
||||
|
||||
|
||||
def base64url_decode(raw):
|
||||
rem = len(raw) % 4
|
||||
if rem > 0:
|
||||
raw += b'=' * (4 - rem)
|
||||
return base64.urlsafe_b64decode(raw)
|
||||
|
||||
|
||||
def base64url_encode(raw):
|
||||
return base64.urlsafe_b64encode(raw).rstrip(b'=')
|
||||
|
||||
|
||||
key = nacl.signing.SigningKey(seed=b'1234' * 8)
|
||||
print('Hex verify key:', binascii.hexlify(key.verify_key.encode()).decode())
|
||||
doc = {
|
||||
'type': 'certificat-zfe',
|
||||
'immatriculation': 'AZ-1234-BE',
|
||||
'nom': 'Jean Marc Dupond',
|
||||
'date_emission': '2022-06-07',
|
||||
}
|
||||
|
||||
payload = json.dumps(doc).encode()
|
||||
signed = key.sign(payload)
|
||||
certificate = b'%s.%s' % (base64url_encode(payload), base64url_encode(signed.signature))
|
||||
|
||||
# verify
|
||||
b64_payload, b64_signature = certificate.split(b'.')
|
||||
assert key.verify_key.verify(base64url_decode(b64_payload), base64url_decode(b64_signature))
|
||||
|
||||
print(f'Certificate({len(certificate)} bytes):', certificate)
|
||||
|
||||
qr = qrcode.QRCode(
|
||||
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
||||
# image_factory=qrcode.image.svg.SvgPathImage,
|
||||
)
|
||||
qr.add_data(certificate)
|
||||
qr.make(fit=True)
|
||||
img = qr.make_image(fill_color='black', back_color='white')
|
||||
with open('certificate.png', 'wb') as fd:
|
||||
img.save(fd)
|
|
@ -0,0 +1,89 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/js-nacl/1.4.0/nacl_factory.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Base64/1.1.0/base64.min.js"></script>
|
||||
<script type="text/javascript" src="https://unpkg.com/html5-qrcode"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<label>SCAN QR CODE</label>
|
||||
<div id="reader" style="width: 70vmin"></div>
|
||||
<label>RESULT</label>
|
||||
<pre id="text" style="font-size: 2rem; white-space: pre-wrap;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var base64url_to_base64 = function(input) {
|
||||
// Replace non-url compatible chars with base64 standard chars
|
||||
input = input
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
|
||||
// Pad out with standard base64 required padding characters
|
||||
var pad = input.length % 4;
|
||||
if(pad) {
|
||||
if(pad === 1) {
|
||||
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
|
||||
}
|
||||
input += new Array(5-pad).join('=');
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
function b64urldecode(base64url) {
|
||||
var binary_string = window.atob(base64url_to_base64(base64url));
|
||||
console.log('binary string', binary_string)
|
||||
var len = binary_string.length;
|
||||
var bytes = new Uint8Array(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
nacl_factory.instantiate(function (nacl) {
|
||||
let key = '79a441b73a99ead8c4446aa36c973dd23787d459122fe2169232a07a181e37a9';
|
||||
let uint8_key = nacl.from_hex(key);
|
||||
|
||||
function onScanSuccess(c, decodedResult) {
|
||||
// handle the scanned code as you like, for example:
|
||||
let pre = document.getElementById('text');
|
||||
pre.textContent = 'Decoding "' + c + '"\n';
|
||||
console.log('1');
|
||||
let parts = c.split('.');
|
||||
console.log('2');
|
||||
if (parts.length != 2) {
|
||||
pre.textContent += "ERROR wrong length";
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
let b64_payload = parts[0];
|
||||
let b64_signature = parts[1];
|
||||
let payload = b64urldecode(b64_payload);
|
||||
let signature = b64urldecode(b64_signature);
|
||||
let decoder = new TextDecoder('utf-8');
|
||||
const jsonString = decoder.decode(payload);
|
||||
const signatureString = decoder.decode(signature);
|
||||
const data = JSON.parse(jsonString)
|
||||
console.log(signature);
|
||||
console.log(nacl.to_hex(signature));
|
||||
} catch (e) {
|
||||
pre.textContent += 'ERROR ' + e;
|
||||
}
|
||||
if (! nacl.crypto_sign_verify_detached(signature, payload, uint8_key)) {
|
||||
pre.textContent += "ERROR wrong signature";
|
||||
return true;
|
||||
}
|
||||
|
||||
pre.textContent = "signature OK\n" + JSON.stringify(data, null, " ");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
let scanner = new Html5Qrcode("reader");
|
||||
scanner.start({ facingMode: "environment" }, { fps: 10, qrbox: {width: 500, height: 500} }, onScanSuccess);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue