Check a signature
An artist can sign their page with a key made in their own browser. This page checks one of those signatures. So can you, without this page, with the twenty lines further down.
What a good check proves
It proves the words have not been altered since they were signed. Not by Talomis, not by anyone who got into our database, not by anyone standing between you and this site. If a single character of the signed record changed, the check fails.
It does not prove who holds the key. A key proves a key, not a person. Somebody who generated a key and signed a page they stole has produced a perfectly valid signature over a lie. Whether the key belongs to the artist named is a separate question, and the marks on their page are what speak to it.
The key's fingerprint is worth one more look. An artist can publish that short string somewhere we do not control, usually their own Instagram bio. If the fingerprint on their page matches the one in a place we cannot edit, then a Talomis that swapped the key for one of its own would be caught by the mismatch. That is the anchor, and it is why the fingerprint is printed.
Run the check here
Check it without us
This is the whole check. It uses nothing but the WebCrypto that is already in your browser, and it will keep working if this site is gone.
// Paste into any browser console. No libraries, no network.
// r = the JSON block published on the artist page, id="talomis-signed-record"
const b = s => Uint8Array.from(
atob(s.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(s.length / 4) * 4, '=')),
c => c.charCodeAt(0));
const params = r.suite === 'Ed25519'
? { name: 'Ed25519' }
: { name: 'ECDSA', namedCurve: 'P-256' };
const alg = r.suite === 'Ed25519'
? { name: 'Ed25519' }
: { name: 'ECDSA', hash: 'SHA-256' };
const key = await crypto.subtle.importKey('spki', b(r.public_key), params, false, ['verify']);
const ok = await crypto.subtle.verify(
alg, key, b(r.signature), new TextEncoder().encode(r.canonical));
console.log(ok ? 'signature is good' : 'signature does NOT match'); One thing that catches people out with the P-256 suite: WebCrypto reads and writes the raw r and s pair, sixty four bytes. OpenSSL expects the same two numbers inside a DER wrapper. If you check with OpenSSL rather than a browser, you have to wrap the signature first, and a failure there is a format mismatch and not a bad signature.
The signed text, exactly
A signature is only worth something if two people can produce the same bytes from the same record, so the record is turned into text by a narrow set of rules with nothing left to interpretation. We publish the signed text itself on the artist's page, so you do not have to reproduce it to check a signature. These rules are here so you can produce it, and so you can build the same thing somewhere else.
- Object keys are sorted ascending by UTF-16 code unit. That is what a plain Array.sort() does in JavaScript.
- No whitespace anywhere. No spaces after colons or commas, no newlines.
- Strings are serialized the way JSON.stringify serializes them.
- Numbers are whole numbers only. Anything that is not a safe integer is refused rather than rounded, so a signature is never made over a number that was quietly changed.
- true, false and null are written as those five, five and four letters.
- Arrays keep the order they were given.
- Nothing else is allowed. No undefined, no dates, no fractions. A field with no value stops the signing instead of vanishing from the signed text.
Inside those limits the output is the same as RFC 8785, the JSON Canonicalization Scheme, so an existing implementation of that works without changes.
What the signature covers: the words and the addresses in the record. What it does not cover: the image bytes at those addresses. A signature over a manifest is not a signature over the photographs, and this site does not imply otherwise.