commit
e512422a2b
|
@ -40,6 +40,7 @@ npm install @peculiar/webcrypto
|
||||||
| AES-CBC | X | | X | | X | X | |
|
| AES-CBC | X | | X | | X | X | |
|
||||||
| AES-CTR | X | | X | | X | X | |
|
| AES-CTR | X | | X | | X | X | |
|
||||||
| AES-GCM | X | | X | | X | X | |
|
| AES-GCM | X | | X | | X | X | |
|
||||||
|
| AES-KW | X | | X | | | X | |
|
||||||
| ECDSA<sup>1</sup> | X | | X | X | | | |
|
| ECDSA<sup>1</sup> | X | | X | X | | | |
|
||||||
| ECDH<sup>1</sup> | X | | X | | | | X |
|
| ECDH<sup>1</sup> | X | | X | | | | X |
|
||||||
| PBKDF2 | | | X | | | | X |
|
| PBKDF2 | | | X | | | | X |
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import * as core from "webcrypto-core";
|
||||||
|
import { AesCrypto } from "./crypto";
|
||||||
|
import { AesCryptoKey } from "./key";
|
||||||
|
|
||||||
|
export class AesKwProvider extends core.AesKwProvider {
|
||||||
|
|
||||||
|
public async onGenerateKey(algorithm: AesKeyGenParams, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKeyPair | CryptoKey> {
|
||||||
|
return await AesCrypto.generateKey(
|
||||||
|
{
|
||||||
|
name: this.name,
|
||||||
|
length: algorithm.length,
|
||||||
|
},
|
||||||
|
extractable,
|
||||||
|
keyUsages,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onExportKey(format: KeyFormat, key: AesCryptoKey): Promise<JsonWebKey | ArrayBuffer> {
|
||||||
|
return AesCrypto.exportKey(format, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onImportKey(format: KeyFormat, keyData: JsonWebKey | ArrayBuffer, algorithm: Algorithm, extractable: boolean, keyUsages: KeyUsage[]): Promise<CryptoKey> {
|
||||||
|
return AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onEncrypt(algorithm: AesKeyAlgorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
|
return AesCrypto.encrypt(algorithm, key, new Uint8Array(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onDecrypt(algorithm: AesKeyAlgorithm, key: AesCryptoKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||||
|
return AesCrypto.decrypt(algorithm, key, new Uint8Array(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public checkCryptoKey(key: CryptoKey, keyUsage?: KeyUsage) {
|
||||||
|
super.checkCryptoKey(key, keyUsage);
|
||||||
|
if (!(key instanceof AesCryptoKey)) {
|
||||||
|
throw new TypeError("key: Is not a AesCryptoKey");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,8 @@ export class AesCrypto {
|
||||||
return this.encryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
|
return this.encryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
|
||||||
case "AES-GCM":
|
case "AES-GCM":
|
||||||
return this.encryptAesGCM(algorithm as AesGcmParams, key, Buffer.from(data));
|
return this.encryptAesGCM(algorithm as AesGcmParams, key, Buffer.from(data));
|
||||||
|
case "AES-KW":
|
||||||
|
return this.encryptAesKW(algorithm as AesKeyAlgorithm, key, Buffer.from(data));
|
||||||
default:
|
default:
|
||||||
throw new core.OperationError("algorithm: Is not recognized");
|
throw new core.OperationError("algorithm: Is not recognized");
|
||||||
}
|
}
|
||||||
|
@ -91,6 +93,8 @@ export class AesCrypto {
|
||||||
return this.decryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
|
return this.decryptAesCTR(algorithm as AesCtrParams, key, Buffer.from(data));
|
||||||
case "AES-GCM":
|
case "AES-GCM":
|
||||||
return this.decryptAesGCM(algorithm as AesGcmParams, key, Buffer.from(data));
|
return this.decryptAesGCM(algorithm as AesGcmParams, key, Buffer.from(data));
|
||||||
|
case "AES-KW":
|
||||||
|
return this.decryptAesKW(algorithm as AesKeyAlgorithm, key, Buffer.from(data));
|
||||||
default:
|
default:
|
||||||
throw new core.OperationError("algorithm: Is not recognized");
|
throw new core.OperationError("algorithm: Is not recognized");
|
||||||
}
|
}
|
||||||
|
@ -153,4 +157,20 @@ export class AesCrypto {
|
||||||
return new Uint8Array(dec).buffer;
|
return new Uint8Array(dec).buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async encryptAesKW(algorithm: AesKeyAlgorithm, key: AesCryptoKey, data: Buffer) {
|
||||||
|
const iv = Buffer.from("A6A6A6A6A6A6A6A6", "hex");
|
||||||
|
const cipher = crypto.createCipheriv(`id-aes${key.algorithm.length}-wrap`, key.data, iv);
|
||||||
|
let enc = cipher.update(data);
|
||||||
|
enc = Buffer.concat([enc, cipher.final()]);
|
||||||
|
const res = new Uint8Array(enc).buffer;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async decryptAesKW(algorithm: AesKeyAlgorithm, key: AesCryptoKey, data: Buffer) {
|
||||||
|
const iv = Buffer.from("A6A6A6A6A6A6A6A6", "'hex");
|
||||||
|
const decipher = crypto.createDecipheriv(`id-aes${key.algorithm.length}-wrap`, key.data, iv);
|
||||||
|
let dec = decipher.update(data);
|
||||||
|
dec = Buffer.concat([dec, decipher.final()]);
|
||||||
|
return new Uint8Array(dec).buffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,3 +3,4 @@ export * from "./aes_cbc";
|
||||||
export * from "./aes_cmac";
|
export * from "./aes_cmac";
|
||||||
export * from "./aes_ctr";
|
export * from "./aes_ctr";
|
||||||
export * from "./aes_gcm";
|
export * from "./aes_gcm";
|
||||||
|
export * from "./aes_kw";
|
||||||
|
|
|
@ -18,6 +18,8 @@ export class AesCryptoKey extends SymmetricKey {
|
||||||
return `A${this.algorithm.length}CTR`;
|
return `A${this.algorithm.length}CTR`;
|
||||||
case "AES-GCM":
|
case "AES-GCM":
|
||||||
return `A${this.algorithm.length}GCM`;
|
return `A${this.algorithm.length}GCM`;
|
||||||
|
case "AES-KW":
|
||||||
|
return `A${this.algorithm.length}KW`;
|
||||||
default:
|
default:
|
||||||
throw new core.AlgorithmError("Unsupported algorithm name");
|
throw new core.AlgorithmError("Unsupported algorithm name");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as core from "webcrypto-core";
|
import * as core from "webcrypto-core";
|
||||||
import {
|
import {
|
||||||
AesCbcProvider, AesCtrProvider, AesGcmProvider,
|
AesCbcProvider, AesCtrProvider, AesGcmProvider, AesKwProvider,
|
||||||
DesCbcProvider, DesEde3CbcProvider,
|
DesCbcProvider, DesEde3CbcProvider,
|
||||||
EcdhProvider, EcdsaProvider,
|
EcdhProvider, EcdsaProvider,
|
||||||
HmacProvider,
|
HmacProvider,
|
||||||
|
@ -17,6 +17,7 @@ export class SubtleCrypto extends core.SubtleCrypto {
|
||||||
this.providers.set(new AesCbcProvider());
|
this.providers.set(new AesCbcProvider());
|
||||||
this.providers.set(new AesCtrProvider());
|
this.providers.set(new AesCtrProvider());
|
||||||
this.providers.set(new AesGcmProvider());
|
this.providers.set(new AesGcmProvider());
|
||||||
|
this.providers.set(new AesKwProvider());
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region DES
|
//#region DES
|
||||||
|
|
177
test/aes.ts
177
test/aes.ts
|
@ -529,6 +529,183 @@ context("AES", () => {
|
||||||
},
|
},
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region AES-KW
|
||||||
|
{
|
||||||
|
name: "AES-128-KW",
|
||||||
|
actions: {
|
||||||
|
generateKey: [
|
||||||
|
{
|
||||||
|
algorithm: { name: "AES-KW", length: 128 } as AesKeyGenParams,
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"] as KeyUsage[],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
wrapKey: [
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
format: "raw",
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
data: Buffer.from("000102030405060708090A0B0C0D0E0F", "hex"),
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
wKey: {
|
||||||
|
format: "raw" as KeyFormat,
|
||||||
|
data: Buffer.from("00112233445566778899AABBCCDDEEFF", "hex"),
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
algorithm: {
|
||||||
|
name: "AES-KW",
|
||||||
|
},
|
||||||
|
wrappedKey: Buffer.from("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5", "hex")
|
||||||
|
},
|
||||||
|
],
|
||||||
|
import: [
|
||||||
|
{
|
||||||
|
name: "raw",
|
||||||
|
format: "raw" as KeyFormat,
|
||||||
|
data: Buffer.from("1234567890abcdef12345678"),
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"] as KeyUsage[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jwk",
|
||||||
|
format: "jwk",
|
||||||
|
data: {
|
||||||
|
kty: "oct",
|
||||||
|
alg: "A192KW",
|
||||||
|
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
|
||||||
|
ext: true,
|
||||||
|
key_ops: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AES-192-KW",
|
||||||
|
actions: {
|
||||||
|
generateKey: [
|
||||||
|
{
|
||||||
|
algorithm: { name: "AES-KW", length: 192 } as AesKeyGenParams,
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"] as KeyUsage[],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
wrapKey: [
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
format: "raw",
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
data: Buffer.from("000102030405060708090A0B0C0D0E0F1011121314151617", "hex"),
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
wKey: {
|
||||||
|
format: "raw" as KeyFormat,
|
||||||
|
data: Buffer.from("00112233445566778899AABBCCDDEEFF0001020304050607", "hex"),
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
algorithm: {
|
||||||
|
name: "AES-KW",
|
||||||
|
},
|
||||||
|
wrappedKey: Buffer.from("031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2", "hex")
|
||||||
|
},
|
||||||
|
],
|
||||||
|
import: [
|
||||||
|
{
|
||||||
|
name: "raw",
|
||||||
|
format: "raw" as KeyFormat,
|
||||||
|
data: Buffer.from("1234567890abcdef12345678"),
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"] as KeyUsage[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jwk",
|
||||||
|
format: "jwk",
|
||||||
|
data: {
|
||||||
|
kty: "oct",
|
||||||
|
alg: "A192KW",
|
||||||
|
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4",
|
||||||
|
ext: true,
|
||||||
|
key_ops: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AES-256-KW",
|
||||||
|
actions: {
|
||||||
|
generateKey: [
|
||||||
|
{
|
||||||
|
algorithm: { name: "AES-KW", length: 256 } as AesKeyGenParams,
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
wrapKey: [
|
||||||
|
{
|
||||||
|
key: {
|
||||||
|
format: "raw",
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
data: Buffer.from("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", "hex"),
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
wKey: {
|
||||||
|
format: "raw" as KeyFormat,
|
||||||
|
data: Buffer.from("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F", "hex"),
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
algorithm: {
|
||||||
|
name: "AES-KW",
|
||||||
|
},
|
||||||
|
wrappedKey: Buffer.from("28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21", "hex"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
import: [
|
||||||
|
{
|
||||||
|
name: "raw",
|
||||||
|
format: "raw",
|
||||||
|
data: Buffer.from("1234567890abcdef1234567890abcdef"),
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jwk",
|
||||||
|
format: "jwk",
|
||||||
|
data: {
|
||||||
|
kty: "oct",
|
||||||
|
alg: "A256KW",
|
||||||
|
k: "MTIzNDU2Nzg5MGFiY2RlZjEyMzQ1Njc4OTBhYmNkZWY",
|
||||||
|
ext: true,
|
||||||
|
key_ops: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
algorithm: "AES-KW",
|
||||||
|
extractable: true,
|
||||||
|
keyUsages: ["wrapKey", "unwrapKey"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//#endregion
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Reference in New Issue