@atproto/crypto 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/const.d.ts +2 -1
- package/dist/did.d.ts +4 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1924 -2556
- package/dist/index.js.map +4 -4
- package/dist/multibase.d.ts +2 -0
- package/dist/p256/keypair.d.ts +9 -9
- package/dist/p256/operations.d.ts +1 -3
- package/dist/random.d.ts +3 -2
- package/dist/secp256k1/operations.d.ts +1 -0
- package/dist/sha.d.ts +0 -2
- package/dist/types.d.ts +1 -0
- package/package.json +10 -7
- package/src/const.ts +2 -1
- package/src/did.ts +27 -10
- package/src/index.ts +0 -1
- package/src/multibase.ts +27 -0
- package/src/p256/encoding.ts +5 -72
- package/src/p256/keypair.ts +25 -41
- package/src/p256/operations.ts +6 -45
- package/src/random.ts +15 -8
- package/src/secp256k1/encoding.ts +3 -5
- package/src/secp256k1/keypair.ts +7 -5
- package/src/secp256k1/operations.ts +12 -3
- package/src/sha.ts +2 -19
- package/src/types.ts +1 -0
- package/tests/did.test.ts +8 -22
- package/tests/key-compression.test.ts +3 -3
- package/tests/keypairs.test.ts +71 -0
- package/tests/random.test.ts +15 -0
- package/tests/signature-fixtures.json +34 -0
- package/tests/signatures.test.ts +161 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/p256/encoding.d.ts +0 -2
- package/dist/p256/plugin.d.ts +0 -3
- package/dist/plugins.d.ts +0 -2
- package/dist/secp256k1/encoding.d.ts +0 -2
- package/dist/secp256k1/keypair.d.ts +0 -20
- package/dist/secp256k1/plugin.d.ts +0 -3
- package/dist/verify.d.ts +0 -2
- package/src/aes.ts +0 -64
- package/tests/export.test.ts +0 -50
package/dist/multibase.d.ts
CHANGED
package/dist/p256/keypair.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { SupportedEncodings } from 'uint8arrays/util/bases';
|
|
2
2
|
import { Keypair } from '../types';
|
|
3
|
-
export declare type
|
|
3
|
+
export declare type P256KeypairOptions = {
|
|
4
4
|
exportable: boolean;
|
|
5
5
|
};
|
|
6
|
-
export declare class
|
|
6
|
+
export declare class P256Keypair implements Keypair {
|
|
7
|
+
private privateKey;
|
|
8
|
+
private exportable;
|
|
7
9
|
jwtAlg: string;
|
|
8
10
|
private publicKey;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
static create(opts?: Partial<EcdsaKeypairOptions>): Promise<EcdsaKeypair>;
|
|
13
|
-
static import(jwk: JsonWebKey, opts?: Partial<EcdsaKeypairOptions>): Promise<EcdsaKeypair>;
|
|
11
|
+
constructor(privateKey: Uint8Array, exportable: boolean);
|
|
12
|
+
static create(opts?: Partial<P256KeypairOptions>): Promise<P256Keypair>;
|
|
13
|
+
static import(privKey: Uint8Array | string, opts?: Partial<P256KeypairOptions>): Promise<P256Keypair>;
|
|
14
14
|
publicKeyBytes(): Uint8Array;
|
|
15
15
|
publicKeyStr(encoding?: SupportedEncodings): string;
|
|
16
16
|
did(): string;
|
|
17
17
|
sign(msg: Uint8Array): Promise<Uint8Array>;
|
|
18
|
-
export(): Promise<
|
|
18
|
+
export(): Promise<Uint8Array>;
|
|
19
19
|
}
|
|
20
|
-
export default
|
|
20
|
+
export default P256Keypair;
|
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export declare const importKeypairJwk: (jwk: JsonWebKey, exportable?: boolean) => Promise<CryptoKeyPair>;
|
|
2
1
|
export declare const verifyDidSig: (did: string, data: Uint8Array, sig: Uint8Array) => Promise<boolean>;
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const importEcdsaPublicKey: (keyBytes: Uint8Array) => Promise<CryptoKey>;
|
|
2
|
+
export declare const verifySig: (publicKey: Uint8Array, data: Uint8Array, sig: Uint8Array) => Promise<boolean>;
|
package/dist/random.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import * as noble from '@noble/hashes/utils';
|
|
1
2
|
import { SupportedEncodings } from 'uint8arrays/to-string';
|
|
2
|
-
export declare const randomBytes:
|
|
3
|
-
export declare const randomIV: () => Uint8Array;
|
|
3
|
+
export declare const randomBytes: typeof noble.randomBytes;
|
|
4
4
|
export declare const randomStr: (byteLength: number, encoding: SupportedEncodings) => string;
|
|
5
|
+
export declare const randomIntFromSeed: (seed: string, high: number, low?: number) => Promise<number>;
|
package/dist/sha.d.ts
CHANGED
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/crypto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/bluesky-social/atproto.git",
|
|
9
|
+
"directory": "packages/crypto"
|
|
10
|
+
},
|
|
6
11
|
"scripts": {
|
|
7
12
|
"test": "jest ",
|
|
8
|
-
"prettier": "prettier --check src/",
|
|
9
|
-
"prettier:fix": "prettier --write src/",
|
|
13
|
+
"prettier": "prettier --check src/ tests/",
|
|
14
|
+
"prettier:fix": "prettier --write src/ tests/",
|
|
10
15
|
"lint": "eslint . --ext .ts,.tsx",
|
|
11
16
|
"lint:fix": "yarn lint --fix",
|
|
12
17
|
"verify": "run-p prettier lint",
|
|
@@ -19,10 +24,8 @@
|
|
|
19
24
|
"postpublish": "npm run update-main-to-src"
|
|
20
25
|
},
|
|
21
26
|
"dependencies": {
|
|
22
|
-
"@noble/
|
|
23
|
-
"
|
|
24
|
-
"multiformats": "^9.6.4",
|
|
25
|
-
"one-webcrypto": "^1.0.3",
|
|
27
|
+
"@noble/curves": "^1.1.0",
|
|
28
|
+
"@noble/hashes": "^1.3.1",
|
|
26
29
|
"uint8arrays": "3.0.0"
|
|
27
30
|
}
|
|
28
31
|
}
|
package/src/const.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const P256_DID_PREFIX = new Uint8Array([0x80, 0x24])
|
|
2
2
|
export const SECP256K1_DID_PREFIX = new Uint8Array([0xe7, 0x01])
|
|
3
|
-
export const
|
|
3
|
+
export const BASE58_MULTIBASE_PREFIX = 'z'
|
|
4
|
+
export const DID_KEY_PREFIX = 'did:key:'
|
|
4
5
|
|
|
5
6
|
export const P256_JWT_ALG = 'ES256'
|
|
6
7
|
export const SECP256K1_JWT_ALG = 'ES256K'
|
package/src/did.ts
CHANGED
|
@@ -2,21 +2,24 @@ import * as uint8arrays from 'uint8arrays'
|
|
|
2
2
|
import * as p256 from './p256/encoding'
|
|
3
3
|
import * as secp from './secp256k1/encoding'
|
|
4
4
|
import plugins from './plugins'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
BASE58_MULTIBASE_PREFIX,
|
|
7
|
+
DID_KEY_PREFIX,
|
|
8
|
+
P256_JWT_ALG,
|
|
9
|
+
SECP256K1_JWT_ALG,
|
|
10
|
+
} from './const'
|
|
6
11
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
export type ParsedDidKey = {
|
|
12
|
+
export type ParsedMultikey = {
|
|
10
13
|
jwtAlg: string
|
|
11
14
|
keyBytes: Uint8Array
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
export const
|
|
15
|
-
if (!
|
|
16
|
-
throw new Error(`Incorrect prefix for
|
|
17
|
+
export const parseMultikey = (multikey: string): ParsedMultikey => {
|
|
18
|
+
if (!multikey.startsWith(BASE58_MULTIBASE_PREFIX)) {
|
|
19
|
+
throw new Error(`Incorrect prefix for multikey: ${multikey}`)
|
|
17
20
|
}
|
|
18
21
|
const prefixedBytes = uint8arrays.fromString(
|
|
19
|
-
|
|
22
|
+
multikey.slice(BASE58_MULTIBASE_PREFIX.length),
|
|
20
23
|
'base58btc',
|
|
21
24
|
)
|
|
22
25
|
const plugin = plugins.find((p) => hasPrefix(prefixedBytes, p.prefix))
|
|
@@ -35,7 +38,10 @@ export const parseDidKey = (did: string): ParsedDidKey => {
|
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
export const
|
|
41
|
+
export const formatMultikey = (
|
|
42
|
+
jwtAlg: string,
|
|
43
|
+
keyBytes: Uint8Array,
|
|
44
|
+
): string => {
|
|
39
45
|
const plugin = plugins.find((p) => p.jwtAlg === jwtAlg)
|
|
40
46
|
if (!plugin) {
|
|
41
47
|
throw new Error('Unsupported key type')
|
|
@@ -47,10 +53,21 @@ export const formatDidKey = (jwtAlg: string, keyBytes: Uint8Array): string => {
|
|
|
47
53
|
}
|
|
48
54
|
const prefixedBytes = uint8arrays.concat([plugin.prefix, keyBytes])
|
|
49
55
|
return (
|
|
50
|
-
|
|
56
|
+
BASE58_MULTIBASE_PREFIX + uint8arrays.toString(prefixedBytes, 'base58btc')
|
|
51
57
|
)
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
export const parseDidKey = (did: string): ParsedMultikey => {
|
|
61
|
+
if (!did.startsWith(DID_KEY_PREFIX)) {
|
|
62
|
+
throw new Error(`Incorrect prefix for did:key: ${did}`)
|
|
63
|
+
}
|
|
64
|
+
return parseMultikey(did.slice(DID_KEY_PREFIX.length))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const formatDidKey = (jwtAlg: string, keyBytes: Uint8Array): string => {
|
|
68
|
+
return DID_KEY_PREFIX + formatMultikey(jwtAlg, keyBytes)
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
const hasPrefix = (bytes: Uint8Array, prefix: Uint8Array): boolean => {
|
|
55
72
|
return uint8arrays.equals(prefix, bytes.subarray(0, prefix.byteLength))
|
|
56
73
|
}
|
package/src/index.ts
CHANGED
package/src/multibase.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as uint8arrays from 'uint8arrays'
|
|
2
|
+
import { SupportedEncodings } from 'uint8arrays/to-string'
|
|
2
3
|
|
|
3
4
|
export const multibaseToBytes = (mb: string): Uint8Array => {
|
|
4
5
|
const base = mb[0]
|
|
@@ -24,3 +25,29 @@ export const multibaseToBytes = (mb: string): Uint8Array => {
|
|
|
24
25
|
throw new Error(`Unsupported multibase: :${mb}`)
|
|
25
26
|
}
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
export const bytesToMultibase = (
|
|
30
|
+
mb: Uint8Array,
|
|
31
|
+
encoding: SupportedEncodings,
|
|
32
|
+
): string => {
|
|
33
|
+
switch (encoding) {
|
|
34
|
+
case 'base16':
|
|
35
|
+
return 'f' + uint8arrays.toString(mb, encoding)
|
|
36
|
+
case 'base16upper':
|
|
37
|
+
return 'F' + uint8arrays.toString(mb, encoding)
|
|
38
|
+
case 'base32':
|
|
39
|
+
return 'b' + uint8arrays.toString(mb, encoding)
|
|
40
|
+
case 'base32upper':
|
|
41
|
+
return 'B' + uint8arrays.toString(mb, encoding)
|
|
42
|
+
case 'base58btc':
|
|
43
|
+
return 'z' + uint8arrays.toString(mb, encoding)
|
|
44
|
+
case 'base64':
|
|
45
|
+
return 'm' + uint8arrays.toString(mb, encoding)
|
|
46
|
+
case 'base64url':
|
|
47
|
+
return 'u' + uint8arrays.toString(mb, encoding)
|
|
48
|
+
case 'base64urlpad':
|
|
49
|
+
return 'U' + uint8arrays.toString(mb, encoding)
|
|
50
|
+
default:
|
|
51
|
+
throw new Error(`Unsupported multibase: :${mb}`)
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/p256/encoding.ts
CHANGED
|
@@ -1,81 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as uint8arrays from 'uint8arrays'
|
|
1
|
+
import { p256 } from '@noble/curves/p256'
|
|
3
2
|
|
|
4
|
-
// PUBLIC KEY COMPRESSION
|
|
5
|
-
// ------------------------
|
|
6
|
-
|
|
7
|
-
// Compression & Decompression algos from:
|
|
8
|
-
// https://stackoverflow.com/questions/48521840/biginteger-to-a-uint8array-of-bytes
|
|
9
|
-
|
|
10
|
-
// Public key compression for NIST P-256
|
|
11
3
|
export const compressPubkey = (pubkeyBytes: Uint8Array): Uint8Array => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} else if (pubkeyBytes[0] !== 0x04) {
|
|
15
|
-
throw new Error('Expected first byte to be 0x04')
|
|
16
|
-
}
|
|
17
|
-
// first byte is a prefix
|
|
18
|
-
const x = pubkeyBytes.slice(1, 33)
|
|
19
|
-
const y = pubkeyBytes.slice(33, 65)
|
|
20
|
-
const out = new Uint8Array(x.length + 1)
|
|
21
|
-
|
|
22
|
-
out[0] = 2 + (y[y.length - 1] & 1)
|
|
23
|
-
out.set(x, 1)
|
|
24
|
-
|
|
25
|
-
return out
|
|
4
|
+
const point = p256.ProjectivePoint.fromHex(pubkeyBytes)
|
|
5
|
+
return point.toRawBytes(true)
|
|
26
6
|
}
|
|
27
7
|
|
|
28
|
-
// Public key decompression for NIST P-256
|
|
29
8
|
export const decompressPubkey = (compressed: Uint8Array): Uint8Array => {
|
|
30
9
|
if (compressed.length !== 33) {
|
|
31
10
|
throw new Error('Expected 33 byte compress pubkey')
|
|
32
|
-
} else if (compressed[0] !== 0x02 && compressed[0] !== 0x03) {
|
|
33
|
-
throw new Error('Expected first byte to be 0x02 or 0x03')
|
|
34
|
-
}
|
|
35
|
-
// Consts for P256 curve
|
|
36
|
-
const two = bigInt(2)
|
|
37
|
-
// 115792089210356248762697446949407573530086143415290314195533631308867097853951
|
|
38
|
-
const prime = two
|
|
39
|
-
.pow(256)
|
|
40
|
-
.subtract(two.pow(224))
|
|
41
|
-
.add(two.pow(192))
|
|
42
|
-
.add(two.pow(96))
|
|
43
|
-
.subtract(1)
|
|
44
|
-
const b = bigInt(
|
|
45
|
-
'41058363725152142129326129780047268409114441015993725554835256314039467401291',
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
// Pre-computed value, or literal
|
|
49
|
-
const pIdent = prime.add(1).divide(4) // 28948022302589062190674361737351893382521535853822578548883407827216774463488
|
|
50
|
-
|
|
51
|
-
// This value must be 2 or 3. 4 indicates an uncompressed key, and anything else is invalid.
|
|
52
|
-
const signY = bigInt(compressed[0] - 2)
|
|
53
|
-
const x = compressed.slice(1)
|
|
54
|
-
const xBig = bigInt(uint8arrays.toString(x, 'base10'))
|
|
55
|
-
|
|
56
|
-
// y^2 = x^3 - 3x + b
|
|
57
|
-
const maybeY = xBig
|
|
58
|
-
.pow(3)
|
|
59
|
-
.subtract(xBig.multiply(3))
|
|
60
|
-
.add(b)
|
|
61
|
-
.modPow(pIdent, prime)
|
|
62
|
-
|
|
63
|
-
let yBig
|
|
64
|
-
// If the parity matches, we found our root, otherwise it's the other root
|
|
65
|
-
if (maybeY.mod(2).equals(signY)) {
|
|
66
|
-
yBig = maybeY
|
|
67
|
-
} else {
|
|
68
|
-
// y = prime - y
|
|
69
|
-
yBig = prime.subtract(maybeY)
|
|
70
11
|
}
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
// left-pad for smaller than 32 byte y
|
|
74
|
-
const offset = 32 - y.length
|
|
75
|
-
const yPadded = new Uint8Array(32)
|
|
76
|
-
yPadded.set(y, offset)
|
|
77
|
-
|
|
78
|
-
// concat coords & prepend P-256 prefix
|
|
79
|
-
const publicKey = uint8arrays.concat([[0x04], x, yPadded])
|
|
80
|
-
return publicKey
|
|
12
|
+
const point = p256.ProjectivePoint.fromHex(compressed)
|
|
13
|
+
return point.toRawBytes(false)
|
|
81
14
|
}
|
package/src/p256/keypair.ts
CHANGED
|
@@ -1,54 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p256 } from '@noble/curves/p256'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
2
3
|
import * as uint8arrays from 'uint8arrays'
|
|
3
4
|
import { SupportedEncodings } from 'uint8arrays/util/bases'
|
|
4
5
|
import * as did from '../did'
|
|
5
|
-
import * as operations from './operations'
|
|
6
6
|
import { P256_JWT_ALG } from '../const'
|
|
7
7
|
import { Keypair } from '../types'
|
|
8
8
|
|
|
9
|
-
export type
|
|
9
|
+
export type P256KeypairOptions = {
|
|
10
10
|
exportable: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export class
|
|
13
|
+
export class P256Keypair implements Keypair {
|
|
14
14
|
jwtAlg = P256_JWT_ALG
|
|
15
15
|
private publicKey: Uint8Array
|
|
16
|
-
private keypair: CryptoKeyPair
|
|
17
|
-
private exportable: boolean
|
|
18
16
|
|
|
19
|
-
constructor(
|
|
20
|
-
|
|
21
|
-
publicKey: Uint8Array,
|
|
22
|
-
exportable: boolean,
|
|
23
|
-
) {
|
|
24
|
-
this.keypair = keypair
|
|
25
|
-
this.publicKey = publicKey
|
|
26
|
-
this.exportable = exportable
|
|
17
|
+
constructor(private privateKey: Uint8Array, private exportable: boolean) {
|
|
18
|
+
this.publicKey = p256.getPublicKey(privateKey)
|
|
27
19
|
}
|
|
28
20
|
|
|
29
21
|
static async create(
|
|
30
|
-
opts?: Partial<
|
|
31
|
-
): Promise<
|
|
22
|
+
opts?: Partial<P256KeypairOptions>,
|
|
23
|
+
): Promise<P256Keypair> {
|
|
32
24
|
const { exportable = false } = opts || {}
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
exportable,
|
|
36
|
-
['sign', 'verify'],
|
|
37
|
-
)
|
|
38
|
-
const pubkeyBuf = await webcrypto.subtle.exportKey('raw', keypair.publicKey)
|
|
39
|
-
const pubkeyBytes = new Uint8Array(pubkeyBuf)
|
|
40
|
-
return new EcdsaKeypair(keypair, pubkeyBytes, exportable)
|
|
25
|
+
const privKey = p256.utils.randomPrivateKey()
|
|
26
|
+
return new P256Keypair(privKey, exportable)
|
|
41
27
|
}
|
|
42
28
|
|
|
43
29
|
static async import(
|
|
44
|
-
|
|
45
|
-
opts?: Partial<
|
|
46
|
-
): Promise<
|
|
30
|
+
privKey: Uint8Array | string,
|
|
31
|
+
opts?: Partial<P256KeypairOptions>,
|
|
32
|
+
): Promise<P256Keypair> {
|
|
47
33
|
const { exportable = false } = opts || {}
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
const privKeyBytes =
|
|
35
|
+
typeof privKey === 'string'
|
|
36
|
+
? uint8arrays.fromString(privKey, 'hex')
|
|
37
|
+
: privKey
|
|
38
|
+
return new P256Keypair(privKeyBytes, exportable)
|
|
52
39
|
}
|
|
53
40
|
|
|
54
41
|
publicKeyBytes(): Uint8Array {
|
|
@@ -64,21 +51,18 @@ export class EcdsaKeypair implements Keypair {
|
|
|
64
51
|
}
|
|
65
52
|
|
|
66
53
|
async sign(msg: Uint8Array): Promise<Uint8Array> {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
return new Uint8Array(buf)
|
|
54
|
+
const msgHash = await sha256(msg)
|
|
55
|
+
// return raw 64 byte sig not DER-encoded
|
|
56
|
+
const sig = await p256.sign(msgHash, this.privateKey, { lowS: true })
|
|
57
|
+
return sig.toCompactRawBytes()
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
async export(): Promise<
|
|
60
|
+
async export(): Promise<Uint8Array> {
|
|
76
61
|
if (!this.exportable) {
|
|
77
62
|
throw new Error('Private key is not exportable')
|
|
78
63
|
}
|
|
79
|
-
|
|
80
|
-
return jwk
|
|
64
|
+
return this.privateKey
|
|
81
65
|
}
|
|
82
66
|
}
|
|
83
67
|
|
|
84
|
-
export default
|
|
68
|
+
export default P256Keypair
|
package/src/p256/operations.ts
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { p256 } from '@noble/curves/p256'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
2
3
|
import { P256_JWT_ALG } from '../const'
|
|
3
4
|
import { parseDidKey } from '../did'
|
|
4
5
|
|
|
5
|
-
export const importKeypairJwk = async (
|
|
6
|
-
jwk: JsonWebKey,
|
|
7
|
-
exportable = false,
|
|
8
|
-
): Promise<CryptoKeyPair> => {
|
|
9
|
-
const privateKey = await webcrypto.subtle.importKey(
|
|
10
|
-
'jwk',
|
|
11
|
-
jwk,
|
|
12
|
-
{ name: 'ECDSA', namedCurve: 'P-256' },
|
|
13
|
-
exportable,
|
|
14
|
-
['sign'],
|
|
15
|
-
)
|
|
16
|
-
const { kty, crv, x, y } = jwk
|
|
17
|
-
const pubKeyJwk = { kty, crv, x, y }
|
|
18
|
-
const publicKey = await webcrypto.subtle.importKey(
|
|
19
|
-
'jwk',
|
|
20
|
-
pubKeyJwk,
|
|
21
|
-
{ name: 'ECDSA', namedCurve: 'P-256' },
|
|
22
|
-
true,
|
|
23
|
-
['verify'],
|
|
24
|
-
)
|
|
25
|
-
return { privateKey, publicKey }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
6
|
export const verifyDidSig = async (
|
|
29
7
|
did: string,
|
|
30
8
|
data: Uint8Array,
|
|
@@ -34,31 +12,14 @@ export const verifyDidSig = async (
|
|
|
34
12
|
if (jwtAlg !== P256_JWT_ALG) {
|
|
35
13
|
throw new Error(`Not a P-256 did:key: ${did}`)
|
|
36
14
|
}
|
|
37
|
-
return
|
|
15
|
+
return verifySig(keyBytes, data, sig)
|
|
38
16
|
}
|
|
39
17
|
|
|
40
|
-
export const
|
|
18
|
+
export const verifySig = async (
|
|
41
19
|
publicKey: Uint8Array,
|
|
42
20
|
data: Uint8Array,
|
|
43
21
|
sig: Uint8Array,
|
|
44
22
|
): Promise<boolean> => {
|
|
45
|
-
const
|
|
46
|
-
return
|
|
47
|
-
{ name: 'ECDSA', hash: { name: 'SHA-256' } },
|
|
48
|
-
importedKey,
|
|
49
|
-
sig,
|
|
50
|
-
data,
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const importEcdsaPublicKey = async (
|
|
55
|
-
keyBytes: Uint8Array,
|
|
56
|
-
): Promise<CryptoKey> => {
|
|
57
|
-
return webcrypto.subtle.importKey(
|
|
58
|
-
'raw',
|
|
59
|
-
keyBytes,
|
|
60
|
-
{ name: 'ECDSA', namedCurve: 'P-256' },
|
|
61
|
-
true,
|
|
62
|
-
['verify'],
|
|
63
|
-
)
|
|
23
|
+
const msgHash = await sha256(data)
|
|
24
|
+
return p256.verify(sig, msgHash, publicKey, { lowS: true })
|
|
64
25
|
}
|
package/src/random.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
+
import * as noble from '@noble/hashes/utils'
|
|
1
2
|
import * as uint8arrays from 'uint8arrays'
|
|
2
|
-
import { webcrypto } from 'one-webcrypto'
|
|
3
3
|
import { SupportedEncodings } from 'uint8arrays/to-string'
|
|
4
|
+
import { sha256 } from './sha'
|
|
4
5
|
|
|
5
|
-
export const randomBytes =
|
|
6
|
-
return webcrypto.getRandomValues(new Uint8Array(length))
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const randomIV = (): Uint8Array => {
|
|
10
|
-
return randomBytes(12)
|
|
11
|
-
}
|
|
6
|
+
export const randomBytes = noble.randomBytes
|
|
12
7
|
|
|
13
8
|
export const randomStr = (
|
|
14
9
|
byteLength: number,
|
|
@@ -17,3 +12,15 @@ export const randomStr = (
|
|
|
17
12
|
const bytes = randomBytes(byteLength)
|
|
18
13
|
return uint8arrays.toString(bytes, encoding)
|
|
19
14
|
}
|
|
15
|
+
|
|
16
|
+
export const randomIntFromSeed = async (
|
|
17
|
+
seed: string,
|
|
18
|
+
high: number,
|
|
19
|
+
low = 0,
|
|
20
|
+
): Promise<number> => {
|
|
21
|
+
const hash = await sha256(seed)
|
|
22
|
+
const number = Buffer.from(hash).readUintBE(0, 6)
|
|
23
|
+
const range = high - low
|
|
24
|
+
const normalized = number % range
|
|
25
|
+
return normalized + low
|
|
26
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { secp256k1 as k256 } from '@noble/curves/secp256k1'
|
|
2
2
|
|
|
3
3
|
export const compressPubkey = (pubkeyBytes: Uint8Array): Uint8Array => {
|
|
4
|
-
const
|
|
5
|
-
const point = secp.Point.fromHex(hex)
|
|
4
|
+
const point = k256.ProjectivePoint.fromHex(pubkeyBytes)
|
|
6
5
|
return point.toRawBytes(true)
|
|
7
6
|
}
|
|
8
7
|
|
|
@@ -10,7 +9,6 @@ export const decompressPubkey = (compressed: Uint8Array): Uint8Array => {
|
|
|
10
9
|
if (compressed.length !== 33) {
|
|
11
10
|
throw new Error('Expected 33 byte compress pubkey')
|
|
12
11
|
}
|
|
13
|
-
const
|
|
14
|
-
const point = secp.Point.fromHex(hex)
|
|
12
|
+
const point = k256.ProjectivePoint.fromHex(compressed)
|
|
15
13
|
return point.toRawBytes(false)
|
|
16
14
|
}
|
package/src/secp256k1/keypair.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { secp256k1 as k256 } from '@noble/curves/secp256k1'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
2
3
|
import * as uint8arrays from 'uint8arrays'
|
|
3
4
|
import { SupportedEncodings } from 'uint8arrays/util/bases'
|
|
4
5
|
import * as did from '../did'
|
|
@@ -14,14 +15,14 @@ export class Secp256k1Keypair implements Keypair {
|
|
|
14
15
|
private publicKey: Uint8Array
|
|
15
16
|
|
|
16
17
|
constructor(private privateKey: Uint8Array, private exportable: boolean) {
|
|
17
|
-
this.publicKey =
|
|
18
|
+
this.publicKey = k256.getPublicKey(privateKey)
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
static async create(
|
|
21
22
|
opts?: Partial<Secp256k1KeypairOptions>,
|
|
22
23
|
): Promise<Secp256k1Keypair> {
|
|
23
24
|
const { exportable = false } = opts || {}
|
|
24
|
-
const privKey =
|
|
25
|
+
const privKey = k256.utils.randomPrivateKey()
|
|
25
26
|
return new Secp256k1Keypair(privKey, exportable)
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -50,9 +51,10 @@ export class Secp256k1Keypair implements Keypair {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
async sign(msg: Uint8Array): Promise<Uint8Array> {
|
|
53
|
-
const msgHash = await
|
|
54
|
+
const msgHash = await sha256(msg)
|
|
54
55
|
// return raw 64 byte sig not DER-encoded
|
|
55
|
-
|
|
56
|
+
const sig = await k256.sign(msgHash, this.privateKey, { lowS: true })
|
|
57
|
+
return sig.toCompactRawBytes()
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
async export(): Promise<Uint8Array> {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { secp256k1 as k256 } from '@noble/curves/secp256k1'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
2
3
|
import { SECP256K1_JWT_ALG } from '../const'
|
|
3
4
|
import { parseDidKey } from '../did'
|
|
4
5
|
|
|
@@ -11,6 +12,14 @@ export const verifyDidSig = async (
|
|
|
11
12
|
if (jwtAlg !== SECP256K1_JWT_ALG) {
|
|
12
13
|
throw new Error(`Not a secp256k1 did:key: ${did}`)
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
return verifySig(keyBytes, data, sig)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const verifySig = async (
|
|
19
|
+
publicKey: Uint8Array,
|
|
20
|
+
data: Uint8Array,
|
|
21
|
+
sig: Uint8Array,
|
|
22
|
+
): Promise<boolean> => {
|
|
23
|
+
const msgHash = await sha256(data)
|
|
24
|
+
return k256.verify(sig, msgHash, publicKey, { lowS: true })
|
|
16
25
|
}
|
package/src/sha.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as noble from '@noble/hashes/sha256'
|
|
2
2
|
import * as uint8arrays from 'uint8arrays'
|
|
3
|
-
import crypto from 'crypto'
|
|
4
|
-
import { Readable } from 'stream'
|
|
5
3
|
|
|
6
4
|
// takes either bytes of utf8 input
|
|
7
5
|
export const sha256 = async (
|
|
@@ -9,20 +7,5 @@ export const sha256 = async (
|
|
|
9
7
|
): Promise<Uint8Array> => {
|
|
10
8
|
const bytes =
|
|
11
9
|
typeof input === 'string' ? uint8arrays.fromString(input, 'utf8') : input
|
|
12
|
-
|
|
13
|
-
return hash.digest
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const sha256Stream = async (stream: Readable): Promise<Uint8Array> => {
|
|
17
|
-
const hash = crypto.createHash('sha256')
|
|
18
|
-
try {
|
|
19
|
-
for await (const chunk of stream) {
|
|
20
|
-
hash.write(chunk)
|
|
21
|
-
}
|
|
22
|
-
} catch (err) {
|
|
23
|
-
hash.end()
|
|
24
|
-
throw err
|
|
25
|
-
}
|
|
26
|
-
hash.end()
|
|
27
|
-
return hash.read()
|
|
10
|
+
return noble.sha256(bytes)
|
|
28
11
|
}
|