Skip to content

Commit

Permalink
timelockEncrypt no longer needs a client info object
Browse files Browse the repository at this point in the history
* added a README
* added an eslint config
  • Loading branch information
CluEleSsUK committed Aug 8, 2022
1 parent fcf1a20 commit 9f67b53
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 13 deletions.
11 changes: 11 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: [
"@typescript-eslint",
],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
],
};
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# tlock-js

A typescript library for encrypting data which can only be decrypted at a set time in the future using [drand](https://drand.love).
tlock-js uses [AGE](https://age-encryption.org/v1) to symmetrically encrypt a payload, and encrypts the symmetric key using [pairing-based cryptography](https://drand.love/docs/cryptography/#pairing-based-cryptography) ensuring that it can only be decrypted when the drand's [threshold network](https://drand.love/docs/cryptography/#randomness-generation) has generated randomness at a future point.

## Prerequisites
- Node 16+

## Quickstart
- install the dependencies by running `npm install`
- run the tests with `npm test`

### `timelockEncrypt`
This encrypts a payload that can only be decrypted when the `roundNumber` has been reached.
The time of this `roundNumber` depends on the genesis and round frequency of the network you connect to.
By default, the drand testnet HTTP client will be used, but you can implement your own and pass it in here.
The output ciphertext should be compatible with any of the drand tlock implementations

### `timelockDecrypt`
This takes a payload that has been encrypted with any of the drand tlock implementations, reads the `roundNumber` from it and attempts to decrypt it.
If the round number has not yet been reached by the network, an error will be thrown.
It accepts both armored and unarmored payloads.

### `roundForTime`
Given a `NetworkInfo` object, it calculates what the latest-emitted round will have been at that `time`

### `timeForRound`
Given a `NetworkInfo` object, it calculates the approximate time the given `round` will be emitted at (approximate because the network must work together to create the randomness).

## Possible issues
- you may need a `fetch` polyfill on some versions of node, e.g. [isomorphic fetch](https://www.npmjs.com/package/isomorphic-fetch). You can provide your own `DrandHttpClientOptions` to the `DrandHttpClient` if you don't want to use fetch, but it may be necessary to declare a fake `fetch` somewhere for compilation
7 changes: 6 additions & 1 deletion src/drand/drand-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as bls from "@noble/bls12-381"

export interface DrandClient {
get(round: number): Promise<Beacon>
info(): Promise<DrandNetworkInfo>
}

export type Beacon = {
Expand Down Expand Up @@ -86,7 +87,11 @@ class DrandHttpClient implements DrandClient {
}
}

static createFetchClient(options: DrandNetworkInfo = defaultClientInfo): DrandHttpClient {
async info() {
return this.options
}

static createFetchClient(options: DrandNetworkInfo): DrandHttpClient {
const fetchSuccess = async (url: string) => {
const response = await fetch(url)
if (response.status === 404) {
Expand Down
11 changes: 5 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import {defaultClientInfo, DrandClient, DrandHttpClient, DrandNetworkInfo, roundForTime, timeForRound} from "./drand/drand-client"
import {defaultClientInfo, DrandClient, DrandHttpClient, roundForTime, timeForRound} from "./drand/drand-client"
import {createTimelockEncrypter} from "./drand/timelock-encrypter"
import {decryptAge, encryptAge} from "./age/age-encrypt-decrypt"
import {decodeArmor, encodeArmor, isProbablyArmored} from "./age/armor"
import {createTimelockDecrypter} from "./drand/timelock-decrypter"

export async function timelockEncrypt(
config: DrandNetworkInfo,
roundNumber: number,
payload: string,
drandHttpClient: DrandClient = DrandHttpClient.createFetchClient(),
drandHttpClient: DrandClient = DrandHttpClient.createFetchClient(defaultClientInfo),
): Promise<string> {
// probably should get `chainInfo` through /info
const timelockEncrypter = createTimelockEncrypter(defaultClientInfo, drandHttpClient, roundNumber)
const chainInfo = await drandHttpClient.info()
const timelockEncrypter = createTimelockEncrypter(chainInfo, drandHttpClient, roundNumber)
const agePayload = await encryptAge(Buffer.from(payload), timelockEncrypter)
return encodeArmor(agePayload)
}

export async function timelockDecrypt(
ciphertext: string,
drandHttpClient: DrandClient = DrandHttpClient.createFetchClient()
drandHttpClient: DrandClient = DrandHttpClient.createFetchClient(defaultClientInfo)
): Promise<string> {
const timelockDecrypter = createTimelockDecrypter(drandHttpClient)

Expand Down
5 changes: 5 additions & 0 deletions test/drand/mock-drand-client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Beacon, DrandClient} from "../../src/drand/drand-client"
import {defaultClientInfo} from "../../src"

class MockDrandClient implements DrandClient {

Expand All @@ -8,6 +9,10 @@ class MockDrandClient implements DrandClient {
get(_: number): Promise<Beacon> {
return Promise.resolve(this.beacon)
}

async info() {
return defaultClientInfo
}
}

const validBeacon = {
Expand Down
4 changes: 2 additions & 2 deletions test/drand/timelock-decrypter.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {expect} from "chai"
import {MockDrandClient} from "./mock-drand-client"
import {defaultClientInfo} from "../../src/drand/drand-client"
import {defaultClientInfo} from "../../src"
import {readAge} from "../../src/age/age-reader-writer"
import {decodeArmor} from "../../src/age/armor"
import {createTimelockDecrypter} from "../../src/drand/timelock-decrypter"
Expand All @@ -18,7 +18,7 @@ describe("timelock decrypter", () => {

it("should decrypt for stanzas created using tlock", async () => {
const plaintext = "hello world"
const ciphertext = await timelockEncrypt(defaultClientInfo, 1, plaintext, mockClient)
const ciphertext = await timelockEncrypt(1, plaintext, mockClient)
const parsedAgeEncryption = readAge(decodeArmor(ciphertext))

const decryptedFileKey = await createTimelockDecrypter(mockClient)(parsedAgeEncryption.header.recipients)
Expand Down
7 changes: 3 additions & 4 deletions test/drand/timelock.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {expect} from "chai"
import {defaultClientInfo} from "../../src/drand/drand-client"
import {assertError} from "../utils"
import {MockDrandClient} from "./mock-drand-client"
import {timelockDecrypt, timelockEncrypt} from "../../src"

describe("timelock", () => {
describe("encryption", () => {
it("should fail for roundNumber less than 0", async () => {
await assertError(() => timelockEncrypt(defaultClientInfo, -1, "hello world"))
await assertError(() => timelockEncrypt(-1, "hello world"))
})
it("should pass for a valid roundNumber", async () => {
expect(await timelockEncrypt(defaultClientInfo, 1, "hello world")).to.have.length.greaterThan(0)
expect(await timelockEncrypt(1, "hello world")).to.have.length.greaterThan(0)
})
})

Expand All @@ -23,7 +22,7 @@ describe("timelock", () => {
const mockClient = new MockDrandClient(validBeacon)
it("should succeed for a correctly timelock encrypted payload", async () => {
const plaintext = "hello world"
const decryptedPayload = await timelockDecrypt(await timelockEncrypt(defaultClientInfo, 1, plaintext, mockClient), mockClient)
const decryptedPayload = await timelockDecrypt(await timelockEncrypt(1, plaintext, mockClient), mockClient)

expect(decryptedPayload).to.equal(plaintext)
})
Expand Down

0 comments on commit 9f67b53

Please sign in to comment.