Skip to content

Commit 056430d

Browse files
committed
Add importKey, format code, improve tests
1 parent c765b69 commit 056430d

File tree

4 files changed

+196
-186
lines changed

4 files changed

+196
-186
lines changed

src/algorithms/ECDSA.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const crypto = require('isomorphic-webcrypto')
99
const TextEncoder = require('../text-encoder')
1010

1111
/**
12-
* HMAC with SHA-2 Functions
12+
* ECDSA with SHA-2 Functions and P Curves
1313
*/
1414
class ECDSA {
1515

@@ -84,6 +84,43 @@ class ECDSA {
8484
throw new Error('The key is too short.')
8585
}
8686
}
87+
88+
/**
89+
* importKey
90+
* copied from ./RSASSA-PKCS1-v1_5.js, and it works!
91+
*
92+
* @param {JWK} key
93+
* @returns {Promise}
94+
*/
95+
async importKey (key) {
96+
let jwk = Object.assign({}, key)
97+
let algorithm = this.params
98+
let usages = key['key_ops'] || []
99+
100+
if (key.use === 'sig') {
101+
usages.push('verify')
102+
}
103+
104+
if (key.use === 'enc') {
105+
// TODO: handle encryption keys
106+
return Promise.resolve(key)
107+
}
108+
109+
if (key.key_ops) {
110+
usages = key.key_ops
111+
}
112+
113+
return crypto.subtle
114+
.importKey('jwk', jwk, algorithm, true, usages)
115+
.then(cryptoKey => {
116+
Object.defineProperty(jwk, 'cryptoKey', {
117+
enumerable: false,
118+
value: cryptoKey
119+
})
120+
121+
return jwk
122+
})
123+
}
87124
}
88125

89126
/**

src/algorithms/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ supportedAlgorithms.define('RS512', 'importKey', new RSASSA_PKCS1_v1_5({
157157
}
158158
}))
159159

160+
supportedAlgorithms.define('ES256', 'importKey', new ECDSA({
161+
name: 'ECDSA',
162+
hash: {
163+
name: 'SHA-256'
164+
},
165+
namedCurve: 'P-256'
166+
}))
167+
160168
/**
161169
* Export
162170
*/

test/algorithms/ECDSASpec.js

Lines changed: 70 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -18,161 +18,20 @@ const ECDSA = require('../../src/algorithms/ECDSA')
1818
const {TextEncoder} = require('@sinonjs/text-encoding')
1919
const crypto = require('isomorphic-webcrypto')
2020
const base64url = require('base64url')
21+
const { getPublicKey, getPrivateKey } = require('../keys/ES256')
22+
const { should } = require('chai')
23+
24+
/**
25+
* Reused test constants
26+
*/
27+
const alg = { name: 'ECDSA', hash: { name: 'SHA-256' }, namedCurve: "P-256" }
28+
29+
const data = 'signed with Chrome generated webcrypto key'
2130

22-
const jwkEcdsaKey = {
23-
"crv": "P-256",
24-
"d": "XX7AP7HV-6zBeUsAIFwBgpqsQf76ebFN1gquG-9wk8Q",
25-
"ext": true,
26-
"key_ops": [
27-
"sign"
28-
],
29-
"kty": "EC",
30-
"x": "KNeDy7FqchFNXivYDpnNSk0tTvox5cWwJgGoUom24BA",
31-
"y": "qlC7dwMwytkZTY8E6s4Fam1JA8D19OhyFKrUM_aRgPo"
32-
}
33-
const chromeEcdsaSignature = new Uint8Array([
34-
238,
35-
23,
36-
148,
37-
239,
38-
242,
39-
150,
40-
132,
41-
224,
42-
198,
43-
144,
44-
143,
45-
31,
46-
211,
47-
82,
48-
220,
49-
86,
50-
193,
51-
138,
52-
128,
53-
199,
54-
103,
55-
190,
56-
187,
57-
230,
58-
40,
59-
4,
60-
113,
61-
108,
62-
163,
63-
147,
64-
112,
65-
70,
66-
9,
67-
92,
68-
99,
69-
83,
70-
4,
71-
222,
72-
75,
73-
162,
74-
21,
75-
156,
76-
135,
77-
201,
78-
31,
79-
42,
80-
209,
81-
14,
82-
208,
83-
206,
84-
227,
85-
13,
86-
160,
87-
228,
88-
84,
89-
73,
90-
74,
91-
164,
92-
83,
93-
7,
94-
23,
95-
184,
96-
73,
97-
160
98-
])
99-
100-
const publicKey= new Uint8Array([
101-
4,
102-
40,
103-
215,
104-
131,
105-
203,
106-
177,
107-
106,
108-
114,
109-
17,
110-
77,
111-
94,
112-
43,
113-
216,
114-
14,
115-
153,
116-
205,
117-
74,
118-
77,
119-
45,
120-
78,
121-
250,
122-
49,
123-
229,
124-
197,
125-
176,
126-
38,
127-
1,
128-
168,
129-
82,
130-
137,
131-
182,
132-
224,
133-
16,
134-
170,
135-
80,
136-
187,
137-
119,
138-
3,
139-
48,
140-
202,
141-
217,
142-
25,
143-
77,
144-
143,
145-
4,
146-
234,
147-
206,
148-
5,
149-
106,
150-
109,
151-
73,
152-
3,
153-
192,
154-
245,
155-
244,
156-
232,
157-
114,
158-
20,
159-
170,
160-
212,
161-
51,
162-
246,
163-
145,
164-
128,
165-
250
166-
])
167-
const signature = 'YvmWUlEcE3C739KYsNRkykl9roZjr7vn0BHkMt0JZqemnWS3pYUoS7AvFs6D3POPosDEk20GbewTs3Mr/K5pWA=='
168-
const alg = { name: 'ECDSA', hash: { name: 'SHA-256' }, namedCurve: "P-256" }
169-
170-
171-
const data = 'signed with Chrome generated webcrypto key'
17231
/**
17332
* Tests
17433
*/
175-
describe.only('ECDSA', () => {
34+
describe('ECDSA', () => {
17635

17736
/**
17837
* constructor
@@ -189,65 +48,91 @@ describe.only('ECDSA', () => {
18948
* sign
19049
*/
19150
describe('sign', () => {
192-
let importedEcdsaKey, importedPublicKey
193-
194-
before(() => {
51+
let importedEcdsaPrivateKey, importedEcdsaPublicKey
19552

196-
return crypto.subtle
197-
.importKey('jwk', jwkEcdsaKey, alg, true, ['sign', 'verify'])
198-
.then(cryptoKey => importedEcdsaKey = cryptoKey)
199-
})
200-
201-
before(() => {
202-
return crypto.subtle
203-
.importKey('raw', publicKey, alg, true, ['verify'])
204-
.then(cryptoKey => importedPublicKey = cryptoKey)
53+
before(async () => {
54+
importedEcdsaPrivateKey = await getPrivateKey()
55+
importedEcdsaPublicKey = await getPublicKey()
20556
})
20657

20758
it('should return a promise', () => {
20859
let ecdsa = new ECDSA(alg)
209-
return ecdsa.sign(importedEcdsaKey, data).should.be.instanceof(Promise)
60+
return ecdsa.sign(importedEcdsaPrivateKey, data).should.be.instanceof(Promise)
21061
})
21162

21263
it('should reject an insufficient key length')
21364

214-
it('should resolve a base64url encoded value', () => {
215-
let ecdsa = new ECDSA(alg)
216-
return ecdsa.sign(importedEcdsaKey, data)
217-
.then(signature => {
218-
// ECDSA is non-deterministic. Therefore we test that the generated signature gets verified with crypto library
219-
return crypto.subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' }, namedCurve: "P-256"}, importedPublicKey, new TextEncoder().encode(signature), new TextEncoder().encode(data))
220-
// base64url.toBuffer(signature)
221-
// .should.eql(Buffer.from(chromeEcdsaSignature.buffer))
222-
})
65+
it('should resolve a base64url encoded value and verification should pass', async () => {
66+
const ecdsa = new ECDSA(alg)
67+
const signature = await ecdsa.sign(importedEcdsaPrivateKey, data)
68+
69+
// this will fail if signature is anything but base64 string
70+
expect(base64url(base64url.toBuffer(signature))).to.equal(signature)
71+
72+
// ECDSA is non-deterministic. Therefore we test that the generated signature gets verified with crypto library
73+
const verified = await crypto.subtle.verify(
74+
{
75+
name: 'ECDSA',
76+
hash: { name: 'SHA-256' },
77+
namedCurve: "P-256"
78+
},
79+
importedEcdsaPublicKey,
80+
base64url.toBuffer(signature),
81+
new TextEncoder().encode(data)
82+
)
83+
84+
verified.should.eql(true)
22385
})
22486
})
22587

22688
/**
22789
* verify
22890
*/
22991
describe('verify', () => {
230-
let importedEcdsaKey
92+
let importedEcdsaPublicKey, signature
23193

232-
before(() => {
94+
before(async () => {
95+
/**
96+
* This signature was produced in Chromium
97+
* deails can be found in comments of ../keys/ES256.js
98+
*/
99+
signature = 'AUNkOnr//z999flIoTMebaf5EQC56WVQizK3GXW/u4EOQBvs9CtvfgWi0pQ3bi0k8p357ajtNvN/dJ1Vr8gbYg=='
233100

234-
return crypto.subtle
235-
.importKey('raw', publicKey, alg, true, ['verify'])
236-
.then(cryptoKey => importedEcdsaKey = cryptoKey)
101+
importedEcdsaPublicKey = await getPublicKey()
237102
})
238103

239104
it('should return a promise', () => {
240105
let ecdsa = new ECDSA(alg)
241-
ecdsa.verify(importedEcdsaKey, signature, data).should.be.instanceof(Promise)
106+
ecdsa.verify(importedEcdsaPublicKey, signature, data).should.be.instanceof(Promise)
242107
})
243108

244-
it('should resolve a boolean', () => {
245-
let ecdsa = new ECDSA(alg)
246-
return ecdsa.verify(importedEcdsaKey, signature, data)
247-
.then(verified => {
248-
expect(verified).to.equal(true)
249-
})
109+
it('should resolve to true', async () => {
110+
const ecdsa = new ECDSA(alg)
111+
const verified = await ecdsa.verify(importedEcdsaPublicKey, signature, data)
112+
expect(verified).to.equal(true)
113+
})
114+
})
115+
116+
/**
117+
* importKey
118+
*/
119+
describe('importKey', () => {
120+
let exampleJwkPublicKey
121+
122+
before(() => {
123+
exampleJwkPublicKey = {
124+
crv: 'P-256',
125+
kty: 'EC',
126+
x: '-c0_z0ly3xRDR0XQuvIirfgal59hq7BzF9ObdUXrgmI',
127+
y: '_7QdnDYKrkrkYaCqZko0ebDQ1L1RpHLtzg8YwdT79n8',
128+
alg: 'ES256'
129+
}
130+
})
250131

132+
it('should successfully import public jwk key', async () => {
133+
const ecdsa = new ECDSA(alg)
134+
const key = await ecdsa.importKey(exampleJwkPublicKey)
135+
key.cryptoKey.constructor.name.should.equal('CryptoKey')
251136
})
252137
})
253138
})

0 commit comments

Comments
 (0)