Skip to content

Commit

Permalink
better Aes export, ref #34
Browse files Browse the repository at this point in the history
- all `Aes*` statics function can be used exported as standlone now
  • Loading branch information
TheNorthMemory committed Aug 23, 2021
1 parent 23114c5 commit 3df9d66
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 89 deletions.
9 changes: 9 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,42 @@ export namespace WechatpayAxiosPlugin {
class Aes {
/**
* @property {string} hex - Alias of `hex` string
* @deprecated v1.0.0 - Only for compatible, use the literal `hex` string instead
*/
static get hex(): string;
/**
* @property {string} utf8 - Alias of `utf8` string
* @deprecated v1.0.0 - Only for compatible, use the literal `utf8` string instead
*/
static get utf8(): string;
/**
* @property {string} base64 - Alias of `base64` string
* @deprecated v1.0.0 - Only for compatible, use the literal `base64` string instead
*/
static get base64(): string;
/**
* @property {integer} BLOCK_SIZE - The `aes` block size
* @deprecated v1.0.0 - Only for compatible, use the literal `16` number instead
*/
static get BLOCK_SIZE(): number;
/**
* @property {string} ALGO_AES_256_GCM - The `aes-256-gcm` algorithm
* @deprecated v1.0.0 - Only for compatible, use the literal `aes-256-gcm` string instead
*/
static get ALGO_AES_256_GCM(): string;
/**
* @property {string} ALGO_AES_256_ECB - The `aes-256-ecb` algorithm
* @deprecated v1.0.0 - Only for compatible, use the literal `aes-256-ecb` string instead
*/
static get ALGO_AES_256_ECB(): string;
/**
* @property {string} ALGO_AES_128_CBC - The `aes-128-cbc` algorithm
* @deprecated v1.0.0 - Only for compatible, use the literal `aes-128-cbc` string instead
*/
static get ALGO_AES_128_CBC(): string;
/**
* Encrypts plaintext.
* @deprecated v1.0.0 - Only for compatible, use the `AesGcm.encrypt` method instead
*
* @param {string} iv - The initialization vector, 16 bytes string.
* @param {string} key - The secret key, 32 bytes string.
Expand All @@ -53,6 +61,7 @@ export namespace WechatpayAxiosPlugin {
static encrypt(iv: string, key: string, plaintext: string, aad?: string): string;
/**
* Decrypts ciphertext.
* @deprecated v1.0.0 - Only for compatible, use the `AesGcm.decrypt` method instead
*
* @param {string} iv - The initialization vector, 16 bytes string.
* @param {string} key - The secret key, 32 bytes string.
Expand Down
191 changes: 106 additions & 85 deletions lib/aes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,112 +2,133 @@
no-bitwise: ["error", { "allow": ["~"] }],
no-use-before-define: ["error", { "classes": false }] */
const { createCipheriv, createDecipheriv } = require('crypto');
/** @constant 'utf8' */
const utf8 = 'utf8';
/** @constant 'base64' */
const base64 = 'base64';
/** @constant 'hex' */
const hex = 'hex';
/** @constant 16 */
const BLOCK_SIZE = 16;
/** @constant 'aes-256-gcm' */
const ALGO_AES_256_GCM = 'aes-256-gcm';
/** @constant 'aes-256-ecb' */
const ALGO_AES_256_ECB = 'aes-256-ecb';
/** @constant 'aes-128-cbc' */
const ALGO_AES_128_CBC = 'aes-128-CBC';
/** The PKCS#7 padding/unpadding implementation */
const PKCS7 = {
/**
* padding, 32 bytes/256 bits `secret key` may optional need the last block.
* @see [rfc2315]{@link https://tools.ietf.org/html/rfc2315#section-10.3}
* @memberof Aes.pkcs7#
* @summary
* The padding can be removed unambiguously since all input is
* padded and no padding string is a suffix of another. This
* padding method is well-defined if and only if k < 256;
* methods for larger k are an open issue for further study.
*
* @param {string|buffer} thing - The input
* @param {boolean} [optional = true] - The flag for the last padding, default `true`
*
* @return {Buffer} - The PADDING tailed payload
*/
padding: (thing, optional = true) => {
const payload = Buffer.from(thing);
const pad = BLOCK_SIZE - (payload.length % BLOCK_SIZE);

if (optional && pad === BLOCK_SIZE) {
return payload;
}

return Buffer.concat([payload, Buffer.alloc(pad, pad)]);
},

/**
* unpadding
* @memberof Aes.pkcs7#
*
* @param {string|buffer} thing - The input
* @return {Buffer} - The PADDING wiped payload
*/
unpadding: (thing) => {
const byte = Buffer.alloc(1);
const payload = Buffer.from(thing);
payload.copy(byte, 0, payload.length - 1);
const pad = byte.readUInt8();

if (!~~Buffer.compare(Buffer.alloc(pad, pad), payload.slice(-pad))) {
return payload.slice(0, -pad);
}

return payload;
},
};

/**
* Aes - Advanced Encryption Standard
*/
class Aes {
/**
* @property {string} hex - Alias of `hex` string
* @deprecated v1.0.0 - Only for compatible, use the literal `hex` string instead
*/
static get hex() { return 'hex'; }
static get hex() { return hex; }

/**
* @property {string} utf8 - Alias of `utf8` string
* @deprecated v1.0.0 - Only for compatible, use the literal `utf8` string instead
*/
static get utf8() { return 'utf8'; }
static get utf8() { return utf8; }

/**
* @property {string} base64 - Alias of `base64` string
* @deprecated v1.0.0 - Only for compatible, use the literal `base64` string instead
*/
static get base64() { return 'base64'; }
static get base64() { return base64; }

/**
* @property {integer} BLOCK_SIZE - The `aes` block size
* @deprecated v1.0.0 - Only for compatible, use the literal `16` number instead
*/
static get BLOCK_SIZE() { return 16; }
static get BLOCK_SIZE() { return BLOCK_SIZE; }

/**
* @property {string} ALGO_AES_256_GCM - The `aes-256-gcm` algorithm
* @deprecated v1.0.0 - Only for compatible, use the literal `aes-256-gcm` string instead
*/
static get ALGO_AES_256_GCM() { return 'aes-256-gcm'; }
static get ALGO_AES_256_GCM() { return ALGO_AES_256_GCM; }

/**
* @property {string} ALGO_AES_256_ECB - The `aes-256-ecb` algorithm
* @deprecated v1.0.0 - Only for compatible, use the literal `aes-256-ecb` string instead
*/
static get ALGO_AES_256_ECB() { return 'aes-256-ecb'; }
static get ALGO_AES_256_ECB() { return ALGO_AES_256_ECB; }

/**
* @property {string} ALGO_AES_128_CBC - The `aes-128-cbc` algorithm
* @deprecated v1.0.0 - Only for compatible, use the literal `aes-128-cbc` string instead
*/
static get ALGO_AES_128_CBC() { return 'aes-128-cbc'; }
static get ALGO_AES_128_CBC() { return ALGO_AES_128_CBC; }

/**
* Back compatible {@link AesGcm.encrypt}, shall removed future
* @deprecated v1.0.0 - Only for compatible, use the `AesGcm.encrypt` method instead
* @returns {string} Base64-encoded ciphertext.
*/
static encrypt(...arg) { return AesGcm.encrypt(...arg); }

/**
* Back compatible {@link AesGcm.decrypt}, shall removed future
* @deprecated v1.0.0 - Only for compatible, use the `AesGcm.decrypt` method instead
* @returns {string} Utf-8 plaintext.
*/
static decrypt(...arg) { return AesGcm.decrypt(...arg); }

/**
* @property {object} pkcs7 - The PKCS7 padding/unpadding container
*/
static get pkcs7() {
const { BLOCK_SIZE } = this;

return {
/**
* padding, 32 bytes/256 bits `secret key` may optional need the last block.
* @see [rfc2315]{@link https://tools.ietf.org/html/rfc2315#section-10.3}
* @memberof Aes.pkcs7#
* @summary
* The padding can be removed unambiguously since all input is
* padded and no padding string is a suffix of another. This
* padding method is well-defined if and only if k < 256;
* methods for larger k are an open issue for further study.
*
* @param {string|buffer} thing - The input
* @param {boolean} [optional = true] - The flag for the last padding, default `true`
*
* @return {Buffer} - The PADDING tailed payload
*/
padding: (thing, optional = true) => {
const payload = Buffer.from(thing);
const pad = BLOCK_SIZE - (payload.length % BLOCK_SIZE);

if (optional && pad === BLOCK_SIZE) {
return payload;
}

return Buffer.concat([payload, Buffer.alloc(pad, pad)]);
},

/**
* unpadding
* @memberof Aes.pkcs7#
*
* @param {string|buffer} thing - The input
* @return {Buffer} - The PADDING wiped payload
*/
unpadding: (thing) => {
const byte = Buffer.alloc(1);
const payload = Buffer.from(thing);
payload.copy(byte, 0, payload.length - 1);
const pad = byte.readUInt8();

if (!~~Buffer.compare(Buffer.alloc(pad, pad), payload.slice(-pad))) {
return payload.slice(0, -pad);
}

return payload;
},
};
}
static get pkcs7() { return PKCS7; }
}

/**
Expand All @@ -125,13 +146,13 @@ class AesGcm extends Aes {
* @returns {string} Base64-encoded ciphertext.
*/
static encrypt(iv, key, plaintext, aad = '') {
const cipher = createCipheriv(this.ALGO_AES_256_GCM, key, iv).setAAD(Buffer.from(aad));
const cipher = createCipheriv(ALGO_AES_256_GCM, key, iv).setAAD(Buffer.from(aad));

return Buffer.concat([
cipher.update(plaintext, this.utf8),
cipher.update(plaintext, utf8),
cipher.final(),
cipher.getAuthTag(),
]).toString(this.base64);
]).toString(base64);
}

/**
Expand All @@ -145,11 +166,11 @@ class AesGcm extends Aes {
* @returns {string} Utf-8 plaintext.
*/
static decrypt(iv, key, ciphertext, aad = '') {
const buf = Buffer.from(ciphertext, this.base64);
const tag = buf.slice(-this.BLOCK_SIZE);
const payload = buf.slice(0, -this.BLOCK_SIZE);
const buf = Buffer.from(ciphertext, base64);
const tag = buf.slice(-BLOCK_SIZE);
const payload = buf.slice(0, -BLOCK_SIZE);

const decipher = createDecipheriv(this.ALGO_AES_256_GCM, key, iv);
const decipher = createDecipheriv(ALGO_AES_256_GCM, key, iv);

// Restrict valid GCM tag length, patches for Node < 11.0.0
// more @see https://github.com/nodejs/node/pull/20039
Expand All @@ -162,9 +183,9 @@ class AesGcm extends Aes {
decipher.setAuthTag(tag).setAAD(Buffer.from(aad));

return Buffer.concat([
decipher.update(payload, this.hex),
decipher.update(payload, hex),
decipher.final(),
]).toString(this.utf8);
]).toString(utf8);
}
}

Expand All @@ -182,13 +203,13 @@ class AesEcb extends Aes {
* @returns {string} Base64-encoded ciphertext.
*/
static encrypt(plaintext, key, iv = null) {
const payload = this.pkcs7.padding(plaintext);
const cipher = createCipheriv(this.ALGO_AES_256_ECB, key, iv).setAutoPadding(false);
const payload = PKCS7.padding(plaintext);
const cipher = createCipheriv(ALGO_AES_256_ECB, key, iv).setAutoPadding(false);

return Buffer.concat([
cipher.update(payload, this.utf8),
cipher.update(payload, utf8),
cipher.final(),
]).toString(this.base64);
]).toString(base64);
}

/**
Expand All @@ -204,13 +225,13 @@ class AesEcb extends Aes {
* @returns {string} Utf-8 plaintext.
*/
static decrypt(ciphertext, key, iv = null) {
const payload = Buffer.from(ciphertext, this.base64);
const decipher = createDecipheriv(this.ALGO_AES_256_ECB, key, iv).setAutoPadding(false);
const payload = Buffer.from(ciphertext, base64);
const decipher = createDecipheriv(ALGO_AES_256_ECB, key, iv).setAutoPadding(false);

return this.pkcs7.unpadding(Buffer.concat([
decipher.update(payload, this.hex),
return PKCS7.unpadding(Buffer.concat([
decipher.update(payload, hex),
decipher.final(),
])).toString(this.utf8);
])).toString(utf8);
}
}

Expand All @@ -228,13 +249,13 @@ class AesCbc extends Aes {
* @returns {string} Base64-encoded ciphertext.
*/
static encrypt(plaintext, key, iv) {
const payload = this.pkcs7.padding(plaintext, false);
const cipher = createCipheriv(this.ALGO_AES_128_CBC, key, iv).setAutoPadding(false);
const payload = PKCS7.padding(plaintext, false);
const cipher = createCipheriv(ALGO_AES_128_CBC, key, iv).setAutoPadding(false);

return Buffer.concat([
cipher.update(payload, this.utf8),
cipher.update(payload, utf8),
cipher.final(),
]).toString(this.base64);
]).toString(base64);
}

/**
Expand All @@ -250,12 +271,12 @@ class AesCbc extends Aes {
* @returns {string} Utf-8 plaintext.
*/
static decrypt(ciphertext, key, iv) {
const payload = Buffer.from(ciphertext, this.base64);
const decipher = createDecipheriv(this.ALGO_AES_128_CBC, key, iv).setAutoPadding(false);
return this.pkcs7.unpadding(Buffer.concat([
decipher.update(payload, this.hex),
const payload = Buffer.from(ciphertext, base64);
const decipher = createDecipheriv(ALGO_AES_128_CBC, key, iv).setAutoPadding(false);
return PKCS7.unpadding(Buffer.concat([
decipher.update(payload, hex),
decipher.final(),
])).toString(this.utf8);
])).toString(utf8);
}
}

Expand Down
8 changes: 4 additions & 4 deletions lib/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ const {
publicEncrypt, privateDecrypt, createSign, createVerify, constants: { RSA_PKCS1_OAEP_PADDING },
} = require('crypto');

/** @constant {string} */
/** @constant 'sha1' */
const sha1 = 'sha1';
/** @constant {string} */
/** @constant 'utf8' */
const utf8 = 'utf8';
/** @constant {string} */
/** @constant 'base64' */
const base64 = 'base64';
/** @constant {string} */
/** @constant 'sha256WithRSAEncryption' */
const sha256WithRSAEncryption = 'sha256WithRSAEncryption';

/**
Expand Down

0 comments on commit 3df9d66

Please sign in to comment.