Skip to content

Commit

Permalink
wip -- util is ok
Browse files Browse the repository at this point in the history
  • Loading branch information
nichoth committed Nov 5, 2024
1 parent c5dbb79 commit 4e66dd6
Show file tree
Hide file tree
Showing 8 changed files with 513 additions and 174 deletions.
52 changes: 0 additions & 52 deletions .github/workflows/gh-pages.yml

This file was deleted.

83 changes: 0 additions & 83 deletions README.example.md

This file was deleted.

91 changes: 58 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,61 @@
# template ts browser
# keys
![tests](https://github.com/bicycle-codes/keys/actions/workflows/nodejs.yml/badge.svg)
[![types](https://img.shields.io/npm/types/@bicycle-codes/keys?style=flat-square)](README.md)
[![module](https://img.shields.io/badge/module-ESM%2FCJS-blue?style=flat-square)](README.md)
[![semantic versioning](https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&style=flat-square)](https://semver.org/)
[![Common Changelog](https://nichoth.github.io/badge/common-changelog.svg)](./CHANGELOG.md)
[![install size](https://flat.badgen.net/packagephobia/install/@bicycle-codes/keys)](https://packagephobia.com/result?p=@bicycle-codes/keys)
[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)

A template for typescript *dependency* modules that run in a browser environment.
Uses `tape-run` for tests in a browser. See [template-ts](https://github.com/nichoth/template-ts) for the same thing but targeting Node.
Create and store keypairs in-browser with the [web crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API).

Use [indexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) to store [non-extractable keypairs](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey#extractable). This means that the browser prevents you from reading the private key, but it can be persisted and re-used indefinitely.

<details><summary><h2>Contents</h2></summary>
<!-- toc -->
</details>

## install

```sh
npm i -S @bicycle-codes/keys
```

## API

This exposes ESM and common JS via [package.json `exports` field](https://nodejs.org/api/packages.html#exports).

### ESM
```js
import '@bicycle-codes/keys'
```

### Common JS
```js
require('@bicycle-codes/keys')
```

### pre-built JS
This package exposes minified JS files too. Copy them to a location that is
accessible to your web server, then link to them in HTML.

#### copy
```sh
cp ./node_modules/@bicycle-codes/keys/dist/index.min.js ./public/keys.min.js
```

#### HTML
```html
<script type="module" src="./keys.min.js"></script>
```

------------------------------------------------------

## use
1. Use the template button in github. Or clone this then
`rm -rf .git && git init`. Then `npm i && npm init`.

2. Edit the source code in `src/index.ts`.

3. Delete either `.github/workflows/gh-pages-docs.yml` or `.github/workflows/gh-pages.yml`, depending on whether you want to deploy an example or docs to github pages.

4. __Edit things__
* Use `./README.example.md` as a starter for docs:
```sh
cp ./README.example.md ./README.md
```
* edit the [build-example](https://github.com/nichoth/template-web-component/blob/c580636f1c912fe2633f7c2478f28b11729c9b80/package.json#L20) command in `package.json` so that it has the right
namespace for github pages

## featuring

* compile the source to both ESM and CJS format, and put compiled files in `dist`.
* ignore `dist` and `*.js` in git, but don't ignore them in npm. That way we
don't commit any compiled code to git, but it is available to consumers.
* use npm's `prepublishOnly` hook to compile the code before publishing to npm.
* use [exports](./package.json#L41) field in `package.json` to make sure the right format is used
by consumers.
* `preversion` npm hook -- lint
* `postversion` npm hook -- `git push --follow-tags && npm publish`
* eslint -- `npm run lint`
* tests run in a browser environment via `tape-run` -- see [`npm test`](./package.json#L12).
Includes `tap` testing tools -- [tapzero](https://github.com/bicycle-codes/tapzero)
and [tap-spec](https://www.npmjs.com/package/tap-spec)
* CI via github actions

### example

### JS
```js
import '@bicycle-codes/keys'
```
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"build-cjs:min": "esbuild src/*.ts --format=cjs --minify --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.min.cjs --sourcemap",
"build-esm": "esbuild src/*.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist",
"build-esm:min": "esbuild ./src/*.ts --format=esm --keep-names --bundle --tsconfig=tsconfig.build.json --minify --out-extension:.js=.min.js --outdir=./dist --sourcemap",
"build-example": "mkdir -p ./public && rm -rf ./public/* && VITE_DEBUG_MODE=staging vite --mode staging --base=\"/repo-name\" build",
"build-example": "mkdir -p ./public && rm -rf ./public/* && VITE_DEBUG_MODE=staging vite --mode staging --base=\"/keys\" build",
"build-docs": "typedoc ./src/index.ts",
"build": "mkdir -p ./dist && rm -rf ./dist/* && npm run build-cjs && npm run build-esm && npm run build-esm:min && npm run build-cjs:min",
"start": "vite",
Expand All @@ -25,7 +25,10 @@
"postversion": "git push --follow-tags && npm publish",
"prepublishOnly": "npm run build"
},
"dependencies": {},
"dependencies": {
"@bicycle-codes/one-webcrypto": "^1.1.2",
"uint8arrays": "^5.1.0"
},
"devDependencies": {
"@bicycle-codes/debug": "^0.6.13",
"@bicycle-codes/tapzero": "^0.10.3",
Expand Down
13 changes: 13 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { RsaSize, HashAlg, CharSize } from './types'

export const BASE58_DID_PREFIX = 'did:key:z'

export const RSA_ALGORITHM = 'RSA-OAEP'
export const RSA_SIGN_ALGORITHM = 'RSASSA-PKCS1-v1_5'
export const RSA_HASHING_ALGORITHM = 'SHA-256'

export const DEFAULT_RSA_SIZE = RsaSize.B2048
export const DEFAULT_HASH_ALGORITHM = HashAlg.SHA_256
export const DEFAULT_CHAR_SIZE = CharSize.B8

export const SALT_LENGTH = 128
95 changes: 91 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,93 @@
import { createDebug } from '@bicycle-codes/debug'
const debug = createDebug()
import { webcrypto } from '@bicycle-codes/one-webcrypto'
import {
RSA_ALGORITHM,
DEFAULT_RSA_SIZE,
DEFAULT_HASH_ALGORITHM,
RSA_SIGN_ALGORITHM
} from './constants'
import {
KeyUse,
type RsaSize,
type HashAlg,
type DID
} from './types'
import Debug from '@bicycle-codes/debug'
const debug = Debug()

export function example ():void {
debug('hello')
/**
* Expose RSA keys only for now, because we are
* waiting for more browsers to support ECC.
*/
export class Keys {
encrypt:CryptoKeyPair
sign:CryptoKeyPair

constructor (keys:{ encrypt:CryptoKeyPair, sign:CryptoKeyPair }) {
this.encrypt = keys.encrypt
this.sign = keys.sign
}

static async create ():Promise<Keys> {
const encryptionKeypair = await makeRSAKeypair(
DEFAULT_RSA_SIZE,
DEFAULT_HASH_ALGORITHM,
KeyUse.Encrypt
)
const signingKeypair = await makeRSAKeypair(
DEFAULT_RSA_SIZE,
DEFAULT_HASH_ALGORITHM,
KeyUse.Sign
)

const keys = new Keys({
encrypt: encryptionKeypair,
sign: signingKeypair
})

const rootDID = await writeKeyToDid(signingKeypair)

debug('create new keys', keys)

return keys
}
}

async function makeRSAKeypair (
size:RsaSize,
hashAlg:HashAlg,
use:KeyUse
):Promise<CryptoKeyPair> {
if (!(Object.values(KeyUse).includes(use))) {
throw new Error('invalid key use')
}
const alg = use === KeyUse.Encrypt ? RSA_ALGORITHM : RSA_SIGN_ALGORITHM
const uses:KeyUsage[] = (use === KeyUse.Encrypt ?
['encrypt', 'decrypt'] :
['sign', 'verify'])

return webcrypto.subtle.generateKey({
name: alg,
modulusLength: size,
publicExponent: publicExponent(),
hash: { name: hashAlg }
}, false, uses)
}

function publicExponent ():Uint8Array {
return new Uint8Array([0x01, 0x00, 0x01])
}

/**
* "write" key is for signing things
*
* @param {CryptoKeyPair} publicWriteKey This device's write key.
* @returns {Promise<DID>}
*/
export async function writeKeyToDid (
publicWriteKey:CryptoKeyPair
):Promise<DID> {
const arr = await getPublicKeyAsArrayBuffer(publicWriteKey)

Check failure on line 89 in src/index.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Cannot find name 'getPublicKeyAsArrayBuffer'.
const ksAlg = 'rsa'

return publicKeyToDid(new Uint8Array(arr), ksAlg)

Check failure on line 92 in src/index.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Cannot find name 'publicKeyToDid'.
}
26 changes: 26 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export type DID = `did:key:z${string}`

export enum RsaSize {
B1024 = 1024,
B2048 = 2048,
B4096 = 4096
}

export enum HashAlg {
SHA_1 = 'SHA-1',
SHA_256 = 'SHA-256',
SHA_384 = 'SHA-384',
SHA_512 = 'SHA-512',
}

export enum KeyUse {
Encrypt = 'encryption', // encrypt/decrypt
Sign = 'signing', // sign
}

export enum CharSize {
B8 = 8,
B16 = 16,
}

export type Msg = ArrayBuffer|string|Uint8Array
Loading

0 comments on commit 4e66dd6

Please sign in to comment.