forked from ceramicnetwork/js-ceramic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsecp256r1.ts
145 lines (128 loc) · 4.32 KB
/
secp256r1.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Brent Shambaugh <brent.shambaugh@gmail.com>. 2021.
import * as u8a from 'uint8arrays'
import multibase from'multibase'
import * as bigintModArith from 'bigint-mod-arith'
import * as nist_p_common from './nist_p_common'
/**
* x,y point as a BigInt (requires at least ES2020)
* For BigInt see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
*/
interface BigIntPoint {
x: BigInt,
y : BigInt
}
/**
* xm,ym point with components expresse with base64url utilizing multiformats
*
* base64url is expressed in the Multibase Table: https://github.com/multiformats/multibase/blob/master/multibase.csv
*/
interface base64urlPoint {
xm: string,
ym: string
}
/**
* Elliptic curve point with coordinates expressed as byte arrays (Uint8Array)
*/
interface octetPoint {
xOctet: Uint8Array,
yOctet: Uint8Array
}
/**
* Constructs the document based on the method key
*/
export function keyToDidDoc (pubKeyBytes: Uint8Array, fingerprint: string): any {
const did = `did:key:${fingerprint}`
const keyId = `${did}#${fingerprint}`
const key = pubKeyBytesToXY(pubKeyBytes);
return {
id: did,
verificationMethod: [{
id: keyId,
type: 'JsonWebKey2020',
controller: did,
publicKeyJwk: {
kty: "EC",
crv: "P-256",
x: key.xm,
y: key.ym,
},
}],
authentication: [keyId],
assertionMethod: [keyId],
capabilityDelegation: [keyId],
capabilityInvocation: [keyId],
}
}
/**
* Decompress a compressed public key in SEC format.
* See section 2.3.3 in SEC 1 v2 : https://www.secg.org/sec1-v2.pdf.
*
* Code based on: https://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression/30431547#30431547
*
* @param - 33 byte compressed public key. 1st byte: 0x02 for even or 0x03 for odd. Following 32 bytes: x coordinate expressed as big-endian.
* @throws TypeError: input cannot be null or undefined.
*/
export function ECPointDecompress( comp : Uint8Array ) : BigIntPoint {
if(!nist_p_common.testUint8Array(comp)) {
throw new TypeError('input must be a Uint8Array');
}
// two, prime, b, and pIdent are constants for the P-256 curve
const two = BigInt(2);
const prime = (two ** 256n) - (two ** 224n) + (two ** 192n) + (two ** 96n) - 1n;
const b = 41058363725152142129326129780047268409114441015993725554835256314039467401291n;
const pIdent = (prime + 1n) / 4n;
const signY = BigInt(comp[0] - 2);
const x = comp.subarray(1);
const xBig = BigInt(u8a.toString(x,'base10'));
const a = xBig**3n - xBig*3n + b;
let yBig = bigintModArith.modPow(a,pIdent,prime);
// "// If the parity doesn't match it's the *other* root"
if( yBig % 2n !== signY)
{
// y = prime - y
yBig = prime - yBig;
}
return {
x: xBig,
y: yBig
};
}
/**
*
* @param pubKeyBytes - public key as uncompressed byte array with no prefix (raw key),
* uncompressed with 0x04 prefix, or compressed with 0x02 prefix if even and 0x03 prefix if odd.
* @returns point x,y with coordinates as multibase encoded base64urls
*
* See the the did:key specification: https://w3c-ccg.github.io/did-method-key/#p-256.
* At present only raw p-256 keys are covered in the specification.
* @throws TypeError: input cannot be null or undefined.
* @throws Error: Unexpected pubKeyBytes
* @internal
*/
export function pubKeyBytesToXY(pubKeyBytes: Uint8Array) : base64urlPoint {
if(!nist_p_common.testUint8Array(pubKeyBytes)) {
throw new TypeError('input must be a Uint8Array');
}
const publicKeyHex = nist_p_common.pubKeyBytesToHex(pubKeyBytes);
const bytesCount = publicKeyHex.length / 2;
// raw p-256 key
if(bytesCount == 64) {
return nist_p_common.publicKeyToXY(publicKeyHex);
}
// uncompressed p-256 key, SEC format
if(bytesCount == 65) {
if(publicKeyHex.slice(0,2) == '04') {
const publicKey = publicKeyHex.slice(2);
return nist_p_common.publicKeyToXY(publicKey);
}
}
// compressed p-256 key, SEC format
if(bytesCount == 33) {
if(publicKeyHex.slice(0,2) == '03' || publicKeyHex.slice(0,2) == '02') {
const publicKey = u8a.fromString(publicKeyHex,'base16')
const point = ECPointDecompress(publicKey);
return nist_p_common.publicKeyIntToXY(point);
}
}
throw new Error('Unexpected pubKeyBytes');
}