Skip to content

Commit

Permalink
Demo: RND/RNDSIG validation (#408)
Browse files Browse the repository at this point in the history
Co-authored-by: Michal Leszczynski <ml@icedev.pl>
  • Loading branch information
Maceeran and icedevml authored Sep 16, 2024
1 parent 0d49761 commit aad9b93
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/src.ts/web/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ <h4>Mobile-only:</h4>
<li><a href="get_data_struct.html">get_data_struct.html - Retrieve data demo</a></li>
<li><a href="get_key_info.html">get_key_info.html - Key slot information demo</a></li>
<li><a href="typed_data.html">typed_data.html - EIP-712 typed data demo</a></li>
<li><a href="rnd_rndsig_validation.html">rnd_rndsig_validation.html - RND/RNDSIG validation demo</a></li>
</ul>
</div>
</body>
Expand Down
173 changes: 173 additions & 0 deletions core/src.ts/web/examples/rnd_rndsig_validation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html>
<head>
<!--
LibHaLo - Programmatically interact with HaLo tags from the web browser, mobile application or the desktop.
Copyright by Arx Research, Inc., a Delaware corporation
License: MIT
-->
<title>LibHaLo Demo</title>

<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">

<script type="text/javascript">
// ensure the library is always fully reloaded
document.write('<script src="../dist/libhalo.js?_v=' + (
Math.random() + '') + '"></scr' + 'ipt>'
);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/elliptic/6.5.7/elliptic.min.js"></script>
</head>
<body>
<div class="container">
<h1>LibHaLo Demo</h1>
<p class="text-muted">
<b>(Mobile only)</b> RND/RNDSIG validation
</p>
<p class="text-muted">
Please tap a HaLo card on your smartphone to generate a unique signed URL, then paste it below to validate the signature.
</p>
<div class="mb-3">
<label class="form-label">URL</label>
<input type="text" class="form-control" id="url"
value="https://eth.vrfy.ch/?av=A02.03.000001.390DE7BD&v=01.C8.000005.E582A2D2&pk1=04F80774B09C70BD8C572F57C2D05835D0E324BE9133B49285EAC24E177BC56D92BCF464C3B12CECE362D91FAF37D7ACBA0996C61922A3535CBAEA855416EA6034&pk2=0463D44E9708131505F8E8C5917DF96919624173EEF726DF3F0569865CE83BC9CD9A018879E56C2FF8DC12531A354A6E669714871D0A5F2B5D63E100E98F930DA5&rnd=00000001FBAD9E01AA882F8669123390768FC8AF11916589DF638787A2C2700A&rndsig=304502203D6CBC47D7257FC8C324AB7ED3E9F00B478EFDE41967BDB2923D6C8393291B0C022100BE6601EA37CA66EEB74F3E38C9B37CE44ECC99B6CF1D49D7A5271BBB573BF58A04&cmd=0000&res=00">
</div>

<button class="btn btn-primary" onclick="btnClicked()">Validate</button>
<p id="noArgsError" class="text-danger mt-3" style="display:none">Please provide a URL</p>

<pre id="statusText" style="word-break: break-all; white-space: pre-wrap;"></pre>

<script type="text/javascript">
function log(data) {
console.log(data);
document
.getElementById('statusText')
.innerText += '\n' + data;
}

async function btnClicked() {
// Extract url from input
const url = document
.getElementById('url')
.value;

// Extract parameters from url
const urlParams = new URLSearchParams(url);
const pk2 = urlParams.get('pk2');
const rnd = urlParams.get('rnd');
const rndsig = urlParams.get('rndsig');

// Check if pk2, rnd & rndsig parameters are present
if (!pk2 || !rnd || !rndsig) {
log("Please provide a URL with pk2, rnd & rndsig parameters");
return;
}

// Handle displaying/hiding the noArgsError message
if (!url) {
document
.getElementById('noArgsError')
.style
.display = 'block';
return;
} else {
document
.getElementById('noArgsError')
.style
.display = 'none';
}

// Display the parameters along with an explanation of what they are
log("pk2 is is the HaLo tag's public key #2");
log("pk2(extracted from URL): " + pk2 + "\n");
log("rnd is a random message generated by the HaLo tag.\nIts first 4 bytes are an incrementing counter and the rest is random.");
log("rnd(extracted from URL): " + rnd + "\n");
log("rndsig is the DER-encoded signature of the rnd message generated by the HaLo tag.");
log("rndsig(extracted from URL): " + rndsig + "\n");

// Display counter value of rnd
const hexCounter = rnd.slice(0, 8);
const intCounter = BigInt('0x' + hexCounter).toString();
log("(hex) Counter value extracted from rnd: " + hexCounter);
log("(int) Counter value extracted from rnd: " + intCounter + "\n");

// Assemble the message to be verified
const header = new TextEncoder().encode("\x19Attest counter pk2:\n");
const rnd_b = hex2arr(rnd);

// Display an explanation of how the message is assembled
log("Message to be verified is assembled by concatenating the following elements:");
log(String.raw `1. '\x19Attest counter pk2:\n' converted to bytes(Uint8Array): `);
log("2. Hex-encoded rnd parameter converted to bytes(Uint8Array): " + rnd_b + "\n");

// Message before hashing
const data = [
...header,
...rnd_b
];

// Display the message to be verified before hashing
log("(Uint8Array) Message to be verified before hashing: " + data + "\n");
log("(Uint8Array -> hex string) Message to be verified before hashing: " + arr2hex(data) + "\n");

try {
// Hash the assembled message
const hashBuffer = await crypto
.subtle
.digest('SHA-256', Uint8Array.from(data));
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map(b => b.toString(16).padStart(2, '0'))
.join('');

// Display the message to be verified after hashing
log("(Uint8Array) Message to be verified after hashing: " + hex2arr(hashHex) + "\n");
log("(Uint8Array -> hex string) Message to be verified after hashing: " + hashHex + "\n");

// Trim the signature in case it contains excess null bytes
// (the signature could be padded with null bytes in case when it's shorter than the field
// reserved in the dynamic URL's query string, that's a performance optimization on the HaLo tag itself)
let sig = hex2arr(rndsig);
sig = sig.slice(0, sig[1] + 2);
sig = arr2hex(sig);

// Verify the signature
const ec = new elliptic.ec('secp256k1');
const key = ec.keyFromPublic(pk2, "hex");
const verificationResult = key.verify(hashHex, sig)

// Display the result of the verification
displayVerificationResult(verificationResult);
} catch (e) {
// Log any errors
log(e);
}
}

// Convert hex string to an array of 8-bit integers
function hex2arr(hexString) {
return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}

// Convert an array of 8-bit integers to a hex string
function arr2hex(buffer) {
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}

// Display the result of the verification
function displayVerificationResult(verificationResult) {
if (verificationResult) {
log("Signature is valid");
} else {
log("Signature is invalid");
}
}
</script>
</div>
</body>
</html>

0 comments on commit aad9b93

Please sign in to comment.