@trust/webcrypto
Advanced tools
Comparing version 0.3.0 to 0.4.0
{ | ||
"name": "@trust/webcrypto", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "WebCrypto API for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -87,4 +87,4 @@ # W3C Web Cryptography API _(@trust/webcrypto)_ | ||
|AES-CTR | _ | _ | | | | _ | | | _ | _ | _ | _ | | ||
|AES-CBC | ✔ | ✔ | | | | ✔ | | | ✔ | ✔ | _ | _ | | ||
|AES-GCM | ✔ | ✔ | | | | ✔ | | | ✔ | ✔ | _ | _ | | ||
|AES-CBC | ✔ | ✔ | | | | ✔ | | | ✔ | ✔ | ✔ | ✔ | | ||
|AES-GCM | ✔ | ✔ | | | | ✔ | | | ✔ | ✔ | ✔ | ✔ | | ||
|AES-KW | | | | | | _ | | | _ | _ | _ | _ | | ||
@@ -91,0 +91,0 @@ |HMAC | | | ✔ | ✔ | | ✔ | | | ✔ | ✔ | | | |
@@ -256,3 +256,3 @@ /** | ||
if (jwk.key_ops){ | ||
key_ops.forEach(op => { | ||
jwk.key_ops.forEach(op => { | ||
if (op !== 'encrypt' | ||
@@ -328,3 +328,3 @@ && op !== 'decrypt' | ||
// 2.2.1 Validate JsonWebKey | ||
let jwk = new JsonWebKey(key) | ||
let jwk = new JsonWebKey() | ||
@@ -331,0 +331,0 @@ // 2.2.2 Set kty property |
@@ -331,3 +331,3 @@ /** | ||
if (jwk.key_ops){ | ||
key_ops.forEach(op => { | ||
jwk.key_ops.forEach(op => { | ||
if (op !== 'encrypt' | ||
@@ -403,3 +403,3 @@ && op !== 'decrypt' | ||
// 2.2.1 Validate JsonWebKey | ||
let jwk = new JsonWebKey(key) | ||
let jwk = new JsonWebKey() | ||
@@ -406,0 +406,0 @@ // 2.2.2 Set kty property |
@@ -134,3 +134,3 @@ /** | ||
//supportedAlgorithms.define('AES-CBC', 'wrapKey', ) | ||
//supportedAlgorithms.define('AES-GCM', 'wrapKey', ) | ||
supportedAlgorithms.define('AES-GCM', 'wrapKey', '../algorithms/AES-GCM') | ||
//supportedAlgorithms.define('AES-CFB', 'wrapKey', ) | ||
@@ -145,3 +145,3 @@ //supportedAlgorithms.define('AES-KW', 'wrapKey', ) | ||
//supportedAlgorithms.define('AES-CBC', 'unwrapKey', ) | ||
//supportedAlgorithms.define('AES-GCM', 'unwrapKey', ) | ||
supportedAlgorithms.define('AES-GCM', 'unwrapKey', '../algorithms/AES-GCM') | ||
//supportedAlgorithms.define('AES-CFB', 'unwrapKey', ) | ||
@@ -148,0 +148,0 @@ //supportedAlgorithms.define('AES-KW', 'unwrapKey', ) |
@@ -10,2 +10,3 @@ /** | ||
const {InvalidAccessError, NotSupportedError} = require('./errors') | ||
const {TextEncoder,TextDecoder} = require('text-encoding') | ||
@@ -367,3 +368,71 @@ /** | ||
wrapKey (format, key, wrappingKey, wrapAlgorithm) { | ||
return new Promise() | ||
// 1. Parameters | ||
// 2. Setup normalizedAlgorithm with op as 'unwrap' | ||
let normalizedAlgorithm = supportedAlgorithms.normalize('wrapKey', wrapAlgorithm) | ||
if (normalizedAlgorithm instanceof Error) { | ||
// 3. If failed, then try again with op as 'encrypt' | ||
normalizedAlgorithm = supportedAlgorithms.normalize('encrypt', wrapAlgorithm) | ||
} | ||
// 4. Otherwise reject outright | ||
if (normalizedAlgorithm instanceof Error) { | ||
return Promise.reject(normalizedAlgorithm) | ||
} | ||
// 5-6. Setup and asynchronously return a new promise | ||
return new Promise((resolve, reject) => { | ||
// 7. Try catch the following step... | ||
// if anything goes wrong then reject the promise outright | ||
try { | ||
// 8. Validate normalizedAlgorithm name property | ||
if (normalizedAlgorithm.name !== wrappingKey.algorithm.name) { | ||
throw new InvalidAccessError('NormalizedAlgorthm name must be same as wrappingKey algorithm name') | ||
} | ||
// 9. Validate usages property contains wrap | ||
if (!wrappingKey.usages.includes('wrapKey')) { | ||
throw new InvalidAccessError('Wrapping key usages must include "wrapKey"') | ||
} | ||
// 10. Validate algorithm contains exportKey | ||
let exportKeyAlgorithms = supportedAlgorithms['exportKey'] | ||
if (!exportKeyAlgorithms[key.algorithm.name]) { | ||
throw new NotSupportedError(key.algorithm.name) | ||
} | ||
// 11. Validate extractable property | ||
if (key.extractable === false) { | ||
throw new InvalidAccessError('Key is not extractable') | ||
} | ||
// 12. Generate extracted key | ||
return this.exportKey(format,key) | ||
.then(exportedKey => { | ||
let bytes | ||
// 13.1. If format is "raw", "pkcs8", or "spki": | ||
if (["raw", "pkcs8","spki"].includes(format)) { | ||
bytes = exportedKey | ||
} | ||
// 13.2. If format is "jwk" | ||
else if (format === "jwk"){ | ||
let json = JSON.stringify(exportedKey) | ||
bytes = new TextEncoder().encode(json) | ||
} | ||
// 14.1. If the normalizedAlgorithm supports wrapKey then use it | ||
if (normalizedAlgorithm['wrapKey']){ | ||
return normalizedAlgorithm.wrapKey(wrapAlgorithm,wrappingKey,new Uint8Array(bytes)) | ||
} | ||
// 14.2. Otherwise try with encrypt | ||
else if (normalizedAlgorithm['encrypt']){ | ||
return normalizedAlgorithm.encrypt(wrapAlgorithm,wrappingKey,new Uint8Array(bytes)) | ||
} | ||
// 14.3. Otherwise throw error | ||
else { | ||
return reject (new NotSupportedError(normalizedAlgorithm.name)) | ||
} | ||
}) | ||
// 15. Return the resulting promise | ||
.then(resolve) | ||
} catch (error) { | ||
return reject(error) | ||
} | ||
}) | ||
} | ||
@@ -386,4 +455,94 @@ | ||
*/ | ||
unwrapKey (format, wrappedKey, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, exractable, keyUsages) { | ||
return new Promise() | ||
unwrapKey (format, wrappedKey, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages) { | ||
// 1. Parameters | ||
// 2. Ommited due to redundancy | ||
// 3. Setup normalizedAlgorithm with op as 'unwrap' | ||
let normalizedAlgorithm = supportedAlgorithms.normalize('unwrapKey', unwrapAlgorithm) | ||
if (normalizedAlgorithm instanceof Error) { | ||
// 4. If failed, then try again with op as 'encrypt' | ||
normalizedAlgorithm = supportedAlgorithms.normalize('decrypt', unwrapAlgorithm) | ||
} | ||
// 5. Otherwise reject outright | ||
if (normalizedAlgorithm instanceof Error) { | ||
return Promise.reject(normalizedAlgorithm) | ||
} | ||
// 6. Setup normalizedKeyAlgorithm | ||
let normalizedKeyAlgorithm = supportedAlgorithms.normalize('importKey', unwrapAlgorithm) | ||
if (normalizedKeyAlgorithm instanceof Error) { | ||
// 7. If failed, then try again with op as 'encrypt' | ||
return Promise.reject(normalizedKeyAlgorithm) | ||
} | ||
// 8-9. Setup and asynchronously return a new promise | ||
return new Promise((resolve, reject) => { | ||
// 10. Try catch the following step... | ||
// if anything goes wrong then reject the promise outright | ||
try { | ||
// 11. Validate normalizedAlgorithm name property | ||
if (normalizedAlgorithm.name !== unwrappingKey.algorithm.name) { | ||
throw new InvalidAccessError('NormalizedAlgorthm name must be same as unwrappingKey algorithm name') | ||
} | ||
// 12. Validate usages property contains unwrap | ||
if (!unwrappingKey.usages.includes('unwrapKey')) { | ||
throw new InvalidAccessError('Unwrapping key usages must include "unwrapKey"') | ||
} | ||
let keyPromise | ||
// 13.1. If the normalizedAlgorithm supports unwrapKey then use it | ||
if (normalizedAlgorithm['unwrapKey']){ | ||
keyPromise = this.unwrapKey(unwrapAlgorithm,unwrappingKey,wrappedKey) | ||
} | ||
// 13.2. Otherwise try with decrypt | ||
else if (normalizedAlgorithm['decrypt']){ | ||
keyPromise = this.decrypt(unwrapAlgorithm,unwrappingKey,wrappedKey) | ||
} | ||
// 13.3. Otherwise throw error | ||
else { | ||
return reject (new NotSupportedError(normalizedAlgorithm.name)) | ||
} | ||
return keyPromise.then( key => { | ||
let bytes | ||
// 14.1. If format is "raw", "pkcs8", or "spki": | ||
if (["raw", "pkcs8","spki"].includes(format)) { | ||
bytes = key | ||
} | ||
// 14.2. If format is "jwk" | ||
else if (format === "jwk"){ | ||
bytes = JSON.parse(new TextDecoder().decode(key)) | ||
} | ||
// 15. Import the resulting unwrapped content | ||
// importKey (format, keyData, algorithm, extractable, keyUsages) | ||
return normalizedKeyAlgorithm.importKey(format, | ||
bytes, | ||
unwrappedKeyAlgorithm, | ||
extractable, | ||
keyUsages) | ||
}).then(result => { | ||
// 16. Validate type parameters and usage length | ||
if ((result.type === "secret" || result.type === "private") && result.usages.length === 0){ | ||
throw new SyntaxError("Usages cannot be empty") | ||
} | ||
// 17. Set extractable | ||
result.extractable = extractable | ||
// 18. Set usages | ||
result.usages = keyUsages | ||
// 19. Resolve promise | ||
return resolve(result) | ||
}).catch(console.log) | ||
} catch (error) { | ||
return reject(error) | ||
} | ||
}) | ||
} | ||
@@ -395,2 +554,2 @@ } | ||
*/ | ||
module.exports = SubtleCrypto | ||
module.exports = SubtleCrypto |
@@ -21,2 +21,3 @@ /** | ||
const AES_CBC = require('../src/algorithms/AES-CBC') | ||
const AES_GCM = require('../src/algorithms/AES-GCM') | ||
const {TextEncoder,TextDecoder} = require('text-encoding') | ||
@@ -36,3 +37,9 @@ | ||
/** | ||
* Test code for AES-GCM | ||
*/ | ||
const good_iv = Buffer.from([ 220, 29, 37, 164, 41, 84, 153, 197, 157, 122, 156, 254, 196, 161, 114, 74 ]) | ||
/** | ||
* Tests | ||
@@ -899,3 +906,181 @@ */ | ||
describe('wrapKey', () => { | ||
it('should return a Promise') | ||
describe('with invalid algorithm', () => { | ||
let promise, error | ||
beforeEach(() => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise = crypto.subtle.wrapKey('jwk',key,key,{name: "AES-NONESENSE"}) | ||
promise.catch(err => error = err) | ||
}) | ||
it('should return a promise', () => { | ||
promise.should.be.instanceof(Promise) | ||
}) | ||
it('should reject the promise', () => { | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('is not a supported algorithm') | ||
}) | ||
}) | ||
describe('with invalid name property', () => { | ||
let promise, error | ||
beforeEach( done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt","wrapKey","unwrapKey"] | ||
) | ||
let alg = {name: "AES-CBC",iv:good_iv} // Not GCM | ||
promise = crypto.subtle.wrapKey('jwk',key,key,alg) | ||
promise.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should reject the promise', () => { | ||
promise.should.be.rejected | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('NormalizedAlgorthm name must be same as wrappingKey algorithm name') | ||
}) | ||
}) | ||
describe('with invalid key ops', () => { | ||
let promise, error | ||
beforeEach(done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt"] | ||
) | ||
let alg = {name: "AES-GCM",iv:good_iv} | ||
promise = crypto.subtle.wrapKey('jwk',key,key,alg) | ||
promise.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should reject the promise', () => { | ||
promise.should.be.rejected | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('Wrapping key usages must include "wrapKey"') | ||
}) | ||
}) | ||
describe('with invalid extractable property', () => { | ||
let promise, error | ||
beforeEach(done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
false, // Incorrect | ||
["encrypt", "decrypt","wrapKey"] | ||
) | ||
let alg = {name: "AES-GCM",iv:good_iv} | ||
promise = crypto.subtle.wrapKey('jwk',key,key,alg) | ||
promise.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should reject the promise', () => { | ||
promise.should.be.rejected | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('Key is not extractable') | ||
}) | ||
}) | ||
describe('with valid arguments', () => { | ||
let promise, result, error | ||
beforeEach(done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt","wrapKey"] | ||
) | ||
let alg = {name: "AES-GCM",iv:good_iv} | ||
promise = crypto.subtle.wrapKey('jwk',key,key,alg) | ||
promise.then(res => { | ||
result = res | ||
done() | ||
}) | ||
.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should return a promise', () => { | ||
promise.should.be.instanceof(Promise) | ||
}) | ||
it('should resolve the promise', () => { | ||
result.should.be.instanceof(ArrayBuffer) | ||
}) | ||
it('should not reject the promise', () => { | ||
expect(error).to.be.undefined | ||
}) | ||
}) | ||
}) | ||
@@ -907,4 +1092,225 @@ | ||
describe('unwrapKey', () => { | ||
it('should return a Promise') | ||
let wrappedKey | ||
before (() => { | ||
wrappedKey = new Uint8Array([ | ||
34,105,32,16,166,133,127,43,31,27,51,61,224,43,87,63,222, | ||
207,113,84,80,101,73,170,5,67,147,1,16,76,204,98,165,66, | ||
25,48,219,41,143,247,79,136,187,202,198,2,176,61,50,127, | ||
32,227,5,15,115,49,174,204,21,201,34,37,45,36,68,51,55, | ||
138,56,65,242,121,247,165,89,74,183,157,112,42,245,233, | ||
236,138,177,94,14,151,55,134,166,103,181,77,59,234,225, | ||
115,127,249,108,64,97,144,99,41,205,18,8,123,16,203,141, | ||
104,145,133,96,15,25,97,109,66,16,32,120,207,212,230,175, | ||
31,202,237,230,158,207,35,145,82,87,110,67,159,36,146,148, | ||
147,222,172,81,162,70,16,152,136,228,100,27,111,138,171]) | ||
}) | ||
describe('with invalid algorithm', () => { | ||
let promise, error | ||
beforeEach(() => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise = crypto.subtle.unwrapKey( | ||
'jwk', | ||
wrappedKey, | ||
key, | ||
{ | ||
name:"AES-NONSENSE", | ||
iv: good_iv, | ||
tagLength: 128 | ||
}, | ||
{ | ||
name: "AES-NONSENSE", | ||
length: 256 | ||
}, | ||
true, | ||
["encrypt","decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise.catch(err => error = err) | ||
}) | ||
it('should return a promise', () => { | ||
promise.should.be.instanceof(Promise) | ||
}) | ||
it('should reject the promise', () => { | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('is not a supported algorithm') | ||
}) | ||
}) | ||
describe('with invalid name property', () => { | ||
let promise, result, error | ||
before( done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise = crypto.subtle.unwrapKey( | ||
'jwk', | ||
wrappedKey, | ||
key, | ||
{ | ||
name:"AES-CBC", // Incorrect | ||
iv: good_iv, | ||
tagLength: 128 | ||
}, | ||
{ | ||
name: "AES-CBC", // Incorrect | ||
length: 256 | ||
}, | ||
true, | ||
["encrypt","decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise | ||
.then ( res => { | ||
result = res | ||
done() | ||
}) | ||
.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should reject the promise', () => { | ||
promise.should.be.rejected | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('NormalizedAlgorthm name must be same as unwrappingKey algorithm name') | ||
}) | ||
}) | ||
describe('with invalid key ops', () => { | ||
let promise, error | ||
before(done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt"] | ||
) | ||
promise = crypto.subtle.unwrapKey( | ||
'jwk', | ||
wrappedKey, | ||
key, | ||
{ | ||
name:"AES-GCM", | ||
iv: good_iv, | ||
tagLength: 128 | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
length: 256 | ||
}, | ||
true, | ||
["encrypt","decrypt"] | ||
) | ||
promise.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should reject the promise', () => { | ||
promise.should.be.rejected | ||
error.should.be.instanceof(Error) | ||
error.message.should.include('Unwrapping key usages must include "unwrapKey"') | ||
}) | ||
}) | ||
describe('with valid arguments', () => { | ||
let promise, result, error | ||
before(done => { | ||
let aes = new AES_GCM({ name: "AES-GCM", length: 256 }) | ||
let key = aes.importKey( | ||
"jwk", | ||
{ | ||
kty: "oct", | ||
k: "Y0zt37HgOx-BY7SQjYVmrqhPkO44Ii2Jcb9yydUDPfE", | ||
alg: "A256GCM", | ||
ext: true, | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
}, | ||
true, | ||
["encrypt", "decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise = crypto.subtle.unwrapKey( | ||
'jwk', | ||
wrappedKey, | ||
key, | ||
{ | ||
name:"AES-GCM", | ||
iv: good_iv, | ||
tagLength: 128 | ||
}, | ||
{ | ||
name: "AES-GCM", | ||
length: 256 | ||
}, | ||
true, | ||
["encrypt","decrypt","wrapKey","unwrapKey"] | ||
) | ||
promise | ||
.then ( res => { | ||
result = res | ||
done() | ||
}) | ||
.catch(err => { | ||
error = err | ||
done() | ||
}) | ||
}) | ||
it('should return a promise', () => { | ||
promise.should.be.instanceof(Promise) | ||
}) | ||
it('should resolve the promise', () => { | ||
result.should.be.instanceof(CryptoKey) | ||
}) | ||
it('should not reject the promise', () => { | ||
expect(error).to.be.undefined | ||
}) | ||
}) | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
246837
7703
3