SkillAgentSearch skills...

Qrdate

Trusted timestamps that you can physically include in photos, videos and live streams using QR codes and audible data signals.

Install / Use

/learn @qrdate/Qrdate
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<a href="https://qrdate.org" target="_blank">QR Date - Signed timestamps inside QR codes for verifying dates in realtime reporting.</a>

QR Date

CI passing

This is the reference implementation for the first version of QR Date, a signed timestamp inside a QR code that you can use to verify the date in (near-) real-time photojournalism, photo/video uploads and live streams.

We are actively working on both the specification and this library. Things may change dramatically in the coming weeks.

What is it?

QR Date is a trusted timestamp that you can physically include in photos, videos and live streams using QR codes and audible data signals. It is for newsrooms, citizen journalists and social media users who wish to verify dates in videos, live streams and photos by visibly and/or audibly including trusted timestamps in the content.

  • For newsrooms, it helps publishers to make better informed editorial decisions on which media to trust from professional and citizen journalists.
  • For citizen journalists, it allows the public to play an active role in news, helping to verify when what's being recorded happened.
  • For social media users, it works via rapid dissemination to people around the world who can help to verify the QR Date and the media's authenticity.

How is it used?

QR Dates are included in photos and videos using physical means to make faking them harder (but not impossible) within a reasonable amount of time.

  • QR Codes: Physically hold a device or piece of paper displaying the QR code to the camera. With photos, take several with different codes. With video, you can move the device or paper around a little.
  • Sonify: Play the data signal from a speaker over the air close enough so that the microphone on your camera can pick it up over background noise. It does not need to be played loud in a quiet environment. Natural reverb and other ambient sounds will make the signal harder to fake.

Observers of QR Dates included in photos and videos can look for normal signs of tampering and verify the date independently without any proprietary tools.

  • QR codes: Any QR code reader will work with QR Dates visible within photos and videos.
  • Data signals: Programs such as fldigi can be used to decode the MT63 encoded signal.

Security and limitations

Like all media, QR Dates can be faked by embedding new codes into old photos and videos. It is a new tool that does not replace old ones. It can and should be combined with other forensic tools to determine if an image has been manipulated.

How does it work?

A server returns the current time, which it signs using a private key to produce a verification signature.

The URL embedded within the QR Date contains the timestamp and the signature.

Accessing the URL will take observers to QRDate.org, or your website if you run your own implementation, which tells them if the date and signature is valid. The signature can also be verified using a public key without access to the internet.

Distributed clock source

We are working on making qrdate compatible with Roughtime for a distributed, auditable clock source.

Want to help? Feel free to raise issues with your ideas!

Offline use

We are also working to specify QR Dates to work offline using a chained certificate and mobile apps.

Using this library

This library does NOT generate the QR code image for you! It only aims to help you conform to the QR Date spec. You need to feed the output of the date creation function (specifically, the url value) into a QR code image generator to get the correct output image.

This is an ES module written in TypeScript. CommonJS is not supported. The minimum NodeJS version is 14.19.0.

Installation

npm i --save qrdate

API

createDynamicQRDate(params: { privateKey: KeyLike; urlBase?: string; formatter?: CustomQRDateURLFormatter; }): DynamicQRDate

Create a Dynamic QR Date spec object with web-based verification. Use this function to create QR Date that users can verify on your website. The client flow is:

  1. User requests your website.
  2. Your server calls createDynamicQRDate, returning the results to the client.
  3. Draw the QR code on the client from the url property on the return object.

Input object

Attribute | Type | Required | Explanation -------------|--------------------------|-------------------------------|-------------------- params.urlBase | string | Either params.urlBase or params.formatter are required. If params.formatter is defined, params.urlBase is not used. | Your verification base URL - do NOT change this once you have decided on it without some kind of redirection in place! All your QR codes will start with the base URL. params.formatter | CustomQRDateURLFormatter | See above | A formatter params.privateKey | KeyLike (string / Buffer / KeyObject) | Yes | Your ed25519 private key. The key can be base64url encoded, and the function will try to parse it for you.

Output object

All the return values are designed to be safe to be shown to the client:

Attribute | Type | Explanation -------------|--------------------------|--------------------- timestamp | number | UNIX timestamp url | string | The text to render into a QR code. signature | string | Base64url-encoded signed timestamp version | number | The version of the QR Date

Example

import { createDynamicQRDate } from 'qrdate';

const urlBase = 'https://localhost/v';
const privateKey = `-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIDgQtOtTyj6rlKFp2+qwlrgzGeA2sxJz4agZKzsCFGKw\n-----END PRIVATE KEY-----`;

const qrDateDynamic = createDynamicQRDate({
  urlBase,
  privateKey, // or a Buffer or KeyObject
});

console.log(qrDateDynamic);
// -----------^
// {
//   timestamp: 1646109781467,
//   url: "https://qrdate.org/v?s=x9hKYrJH0e0BPyVqwnKMAMmxEudkvJccqzjHgaheWFJEd86rW_XdwCKZid7k0teMq7Ygp1PfAJhnT64WcyD6CA&t=1646109781467&v=1",
//   signature: "x9hKYrJH0e0BPyVqwnKMAMmxEudkvJccqzjHgaheWFJEd86rW_XdwCKZid7k0teMq7Ygp1PfAJhnT64WcyD6CA",
// }

createStaticQRDate(privateKey: KeyLike): StaticQRDate

Note: This is not production ready. The certificate chain is still on a concept level.

Create a Static QR Date spec object for offline verification. Use this function to create QR Date that users can verify without your website using a certificate chain. The client flow is:

For users wanting to display codes:

  1. User requests your website or uses an offline app.
  2. Your server calls createStaticQRDate, returning the results to the client.
  3. Draw the QR code on the client from the url property on the return object.

For users wanting to verify the codes:

  1. You publish your public key either in a public or private place, depending on what sort of a setup you wish.
  2. The user inserts your public key into their key store, with the store creating a SHA256 hash of it as the fingerprint.
  3. The user can verify the signature by looking up the public key in their keystore using the fingerprint supplied in the QR Date and use that to perform the verification.

Attribute | Type | Required | Explanation -------------|--------------------------|-------------------------------|-------------------- privateKey | KeyLike (string / Buffer / KeyObject) | Yes | Your ed25519 private key. The key can be base64url encoded, and the function will try to parse it for you.

Output object

All the return values are designed to be safe to be shown to the client:

Attribute | Type | Explanation --------------|--------------------------|--------------------- timestamp | number | UNIX timestamp url | string | The text to render into a QR code. signature | string | Base64url-encoded signed timestamp fingerprint | string | Base64url-encoded fingerprint hashed from your public key version | number | The version of the QR Date

Example

import { createStaticQRDate } from 'qrdate';

const privateKey = `-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIDgQtOtTyj6rlKFp2+qwlrgzGeA2sxJz4agZKzsCFGKw\n-----END PRIVATE KEY-----`;

const qrDateStatic = createStaticQRDate({
  privateKey // or a Buffer or KeyObject
});

console.log(qrDateStatic);
// -----------^
// {
//   timestamp: 1646142017145,
//   url: 'qrdate://v?s=tyYD957Q3i6TGUJi7-xzypIl4Be6mM8Jqvc2-nAswRuTadlCEELtMnXWqykcpzneuXJa772vNXc3T0pQFcPBBw&t=1646142017145&f=soyUshlcjtJZ8LQVqu4_ObCykgpFN2EUmfoESVaReiE&v=1',
//   signature: 'tyYD957Q3i6TGUJi7-xzypIl4Be6mM8Jqvc2-nAswRuTadlCEELtMnXWqykcpzneuXJa772vNXc3T0pQFcPBBw',
//   fingerprint: 'soyUshlcjtJZ8LQVqu4_ObCykgpFN2EUmfoESVaReiE',
//   version: 1
// }
//
// In the above url, `/v` has been added after qrdate:// automatically.
// If you are making your own implementation, be sure to include the `/v` for compatibility.
// A static URL should always start with `qrdate://v` to keep it universal and not to clutter the produced QR code further.
// Please see the spec for V1 Static URLs for further info.
//

verifyDynamicQRDate(params: { signature: string; timestamp: string|number; publicKey: KeyLike }): boolean

Verify that the signature on a signed dynamic QR Date timestamp is valid.

Attribute | Type | Required | Explanation -------------|---------------------------------------------|----------|-------------------- `params.signature

View on GitHub
GitHub Stars39
CategoryContent
Updated1y ago
Forks3

Languages

TypeScript

Security Score

65/100

Audited on Feb 11, 2025

No findings