Skip to content

Commit

Permalink
refactor: replaced built-in crypto library with @web5/crypto
Browse files Browse the repository at this point in the history
  • Loading branch information
Toheeb-Ojuolape committed Oct 4, 2024
1 parent 8b6eb13 commit 517317f
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 55 deletions.
102 changes: 79 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@noble/curves": "1.4.2",
"@noble/ed25519": "2.0.0",
"@noble/secp256k1": "2.0.0",
"@web5/crypto": "^1.0.5",
"@web5/dids": "^1.1.3",
"abstract-level": "1.0.3",
"ajv": "8.12.0",
Expand Down
96 changes: 66 additions & 30 deletions src/utils/encryption.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as crypto from 'crypto';
import * as eciesjs from 'eciesjs';
import { AesCtr } from '@web5/crypto';
import type { Jwk } from '@web5/crypto';
import { Readable } from 'readable-stream';

// compress publicKey for message encryption
Expand All @@ -12,65 +13,98 @@ export class Encryption {
/**
* Encrypts the given plaintext stream using AES-256-CTR algorithm.
*/
public static async aes256CtrEncrypt(key: Uint8Array, initializationVector: Uint8Array, plaintextStream: Readable): Promise<Readable> {
const cipher = crypto.createCipheriv('aes-256-ctr', key, initializationVector);

private static async convertToJwk(key: Uint8Array): Promise<Jwk> {
return {
kty : 'oct',
k : Buffer.from(key).toString('base64url'),
alg : 'A256CTR',
ext : 'true',
};
}

public static async aes256CtrEncrypt(
key: Uint8Array,
initializationVector: Uint8Array,
plaintextStream: Readable
): Promise<Readable> {
const jwkKey = await this.convertToJwk(key);

// Create a cipher stream
const cipherStream = new Readable({
read(): void { }
read(): void {},
});

plaintextStream.on('data', (chunk) => {
const encryptedChunk = cipher.update(chunk);
plaintextStream.on('data', async (chunk) => {
// Encrypt the chunk using AesCtr
const encryptedChunk = await AesCtr.encrypt({
data : chunk,
key : jwkKey,
counter : initializationVector,
length : 256,
});

cipherStream.push(encryptedChunk);
});

plaintextStream.on('end', () => {
const finalChunk = cipher.final();
cipherStream.push(finalChunk);
cipherStream.push(null);
cipherStream.push(null); // Signal the end of the stream
});

plaintextStream.on('error', (err) => {
cipherStream.emit('error', err);
cipherStream.emit('error', err); // Emit error if any occurs in the plaintext stream
});

return cipherStream;
return cipherStream; // Return the cipher stream
}

/**
* Decrypts the given cipher stream using AES-256-CTR algorithm.
*/
public static async aes256CtrDecrypt(key: Uint8Array, initializationVector: Uint8Array, cipherStream: Readable): Promise<Readable> {
const decipher = crypto.createDecipheriv('aes-256-ctr', key, initializationVector);

public static async aes256CtrDecrypt(
key: Uint8Array,
initializationVector: Uint8Array,
cipherStream: Readable
): Promise<Readable> {
const jwkKey = await this.convertToJwk(key); // Convert key to JWK format

// Create a plaintext stream
const plaintextStream = new Readable({
read(): void { }
read(): void {},
});

cipherStream.on('data', (chunk) => {
const decryptedChunk = decipher.update(chunk);
plaintextStream.push(decryptedChunk);
cipherStream.on('data', async (chunk) => {
// Decrypt the chunk using AesCtr
const decryptedChunk = await AesCtr.decrypt({
data : chunk,
key : jwkKey,
counter : initializationVector,
length : 256, // Length of the key in bits
});

plaintextStream.push(decryptedChunk); // Push the decrypted chunk to the plaintext stream
});

cipherStream.on('end', () => {
const finalChunk = decipher.final();
plaintextStream.push(finalChunk);
plaintextStream.push(null);
plaintextStream.push(null); // Signal the end of the stream
});

cipherStream.on('error', (err) => {
plaintextStream.emit('error', err);
plaintextStream.emit('error', err); // Emit error if any occurs in the cipher stream
});

return plaintextStream;
return plaintextStream; // Return the plaintext stream
}

/**
* Encrypts the given plaintext using ECIES (Elliptic Curve Integrated Encryption Scheme)
* with SECP256K1 for the asymmetric calculations, HKDF as the key-derivation function,
* and AES-GCM for the symmetric encryption and MAC algorithms.
*/
public static async eciesSecp256k1Encrypt(publicKeyBytes: Uint8Array, plaintext: Uint8Array): Promise<EciesEncryptionOutput> {
public static async eciesSecp256k1Encrypt(
publicKeyBytes: Uint8Array,
plaintext: Uint8Array
): Promise<EciesEncryptionOutput> {
// underlying library requires Buffer as input
const publicKey = Buffer.from(publicKeyBytes);
const plaintextBuffer = Buffer.from(plaintext);
Expand All @@ -96,7 +130,7 @@ export class Encryption {
ciphertext,
ephemeralPublicKey,
initializationVector,
messageAuthenticationCode
messageAuthenticationCode,
};
}

Expand All @@ -105,14 +139,16 @@ export class Encryption {
* with SECP256K1 for the asymmetric calculations, HKDF as the key-derivation function,
* and AES-GCM for the symmetric encryption and MAC algorithms.
*/
public static async eciesSecp256k1Decrypt(input: EciesEncryptionInput): Promise<Uint8Array> {
public static async eciesSecp256k1Decrypt(
input: EciesEncryptionInput
): Promise<Uint8Array> {
// underlying library requires Buffer as input
const privateKeyBuffer = Buffer.from(input.privateKey);
const eciesEncryptionOutput = Buffer.concat([
input.ephemeralPublicKey,
input.initializationVector,
input.messageAuthenticationCode,
input.ciphertext
input.ciphertext,
]);

const plaintext = eciesjs.decrypt(privateKeyBuffer, eciesEncryptionOutput);
Expand All @@ -123,7 +159,7 @@ export class Encryption {
/**
* Expose eciesjs library configuration
*/
static get isEphemeralKeyCompressed():boolean {
static get isEphemeralKeyCompressed(): boolean {
return eciesjs.ECIES_CONFIG.isEphemeralKeyCompressed;
}
}
Expand All @@ -141,5 +177,5 @@ export type EciesEncryptionInput = EciesEncryptionOutput & {

export enum EncryptionAlgorithm {
Aes256Ctr = 'A256CTR',
EciesSecp256k1 = 'ECIES-ES256K'
}
EciesSecp256k1 = 'ECIES-ES256K',
}
3 changes: 1 addition & 2 deletions src/utils/hd-key.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { PrivateJwk, PublicJwk } from '../types/jose-types.js';

import { Encoder } from './encoder.js';
import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto';
import { Secp256k1 } from './secp256k1.js';
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
import type { PrivateJwk, PublicJwk } from '../types/jose-types.js';

export enum KeyDerivationScheme {
/**
Expand Down

0 comments on commit 517317f

Please sign in to comment.