nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
313 lines (271 loc) • 9.87 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/cipher.js
import {
CipherBase,
privateDecrypt as _privateDecrypt,
privateEncrypt as _privateEncrypt,
publicDecrypt as _publicDecrypt,
publicEncrypt as _publicEncrypt,
getCipherInfo as _getCipherInfo,
} from "nstdlib/stub/binding/crypto";
import { crypto as __crypto__ } from "nstdlib/stub/binding/constants";
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import {
validateEncoding,
validateInt32,
validateObject,
validateString,
} from "nstdlib/lib/internal/validators";
import {
preparePrivateKey,
preparePublicOrPrivateKey,
prepareSecretKey,
} from "nstdlib/lib/internal/crypto/keys";
import {
getArrayBufferOrView,
getStringOption,
kHandle,
} from "nstdlib/lib/internal/crypto/util";
import { isArrayBufferView } from "nstdlib/lib/internal/util/types";
import * as assert from "nstdlib/lib/internal/assert";
import * as LazyTransform from "nstdlib/lib/internal/streams/lazy_transform";
import { normalizeEncoding } from "nstdlib/lib/internal/util";
import { StringDecoder } from "nstdlib/lib/string_decoder";
const { RSA_PKCS1_OAEP_PADDING, RSA_PKCS1_PADDING } = __crypto__;
const {
ERR_CRYPTO_INVALID_STATE,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_UNKNOWN_ENCODING,
} = __codes__;
function rsaFunctionFor(method, defaultPadding, keyType) {
return (options, buffer) => {
const { format, type, data, passphrase } =
keyType === "private"
? preparePrivateKey(options)
: preparePublicOrPrivateKey(options);
const padding = options.padding || defaultPadding;
const { oaepHash, encoding } = options;
let { oaepLabel } = options;
if (oaepHash !== undefined) validateString(oaepHash, "key.oaepHash");
if (oaepLabel !== undefined)
oaepLabel = getArrayBufferOrView(oaepLabel, "key.oaepLabel", encoding);
buffer = getArrayBufferOrView(buffer, "buffer", encoding);
return method(
data,
format,
type,
passphrase,
buffer,
padding,
oaepHash,
oaepLabel,
);
};
}
const publicEncrypt = rsaFunctionFor(
_publicEncrypt,
RSA_PKCS1_OAEP_PADDING,
"public",
);
const publicDecrypt = rsaFunctionFor(
_publicDecrypt,
RSA_PKCS1_PADDING,
"public",
);
const privateEncrypt = rsaFunctionFor(
_privateEncrypt,
RSA_PKCS1_PADDING,
"private",
);
const privateDecrypt = rsaFunctionFor(
_privateDecrypt,
RSA_PKCS1_OAEP_PADDING,
"private",
);
function getDecoder(decoder, encoding) {
const normalizedEncoding = normalizeEncoding(encoding);
decoder = decoder || new StringDecoder(encoding);
if (decoder.encoding !== normalizedEncoding) {
if (normalizedEncoding === undefined) {
throw new ERR_UNKNOWN_ENCODING(encoding);
}
assert(false, "Cannot change encoding");
}
return decoder;
}
function getUIntOption(options, key) {
let value;
if (options && (value = options[key]) != null) {
if (value >>> 0 !== value)
throw new ERR_INVALID_ARG_VALUE(`options.${key}`, value);
return value;
}
return -1;
}
function createCipherBase(cipher, credential, options, decipher, iv) {
const authTagLength = getUIntOption(options, "authTagLength");
this[kHandle] = new CipherBase(decipher);
if (iv === undefined) {
this[kHandle].init(cipher, credential, authTagLength);
} else {
this[kHandle].initiv(cipher, credential, iv, authTagLength);
}
this._decoder = null;
ReflectApply(LazyTransform, this, [options]);
}
function createCipherWithIV(cipher, key, options, decipher, iv) {
validateString(cipher, "cipher");
const encoding = getStringOption(options, "encoding");
key = prepareSecretKey(key, encoding);
iv = iv === null ? null : getArrayBufferOrView(iv, "iv");
ReflectApply(createCipherBase, this, [cipher, key, options, decipher, iv]);
}
// The Cipher class is part of the legacy Node.js crypto API. It exposes
// a stream-based encryption/decryption model. For backwards compatibility
// the Cipher class is defined using the legacy function syntax rather than
// ES6 classes.
function Cipher(cipher, password, options) {
if (!(this instanceof Cipher)) return new Cipher(cipher, password, options);
}
Object.setPrototypeOf(Cipher.prototype, LazyTransform.prototype);
Object.setPrototypeOf(Cipher, LazyTransform);
Cipher.prototype._transform = function _transform(chunk, encoding, callback) {
this.push(this[kHandle].update(chunk, encoding));
callback();
};
Cipher.prototype._flush = function _flush(callback) {
try {
this.push(this[kHandle].final());
} catch (e) {
callback(e);
return;
}
callback();
};
Cipher.prototype.update = function update(data, inputEncoding, outputEncoding) {
if (typeof data === "string") {
validateEncoding(data, inputEncoding);
} else if (!isArrayBufferView(data)) {
throw new ERR_INVALID_ARG_TYPE(
"data",
["string", "Buffer", "TypedArray", "DataView"],
data,
);
}
const ret = this[kHandle].update(data, inputEncoding);
if (outputEncoding && outputEncoding !== "buffer") {
this._decoder = getDecoder(this._decoder, outputEncoding);
return this._decoder.write(ret);
}
return ret;
};
Cipher.prototype.final = function final(outputEncoding) {
const ret = this[kHandle].final();
if (outputEncoding && outputEncoding !== "buffer") {
this._decoder = getDecoder(this._decoder, outputEncoding);
return this._decoder.end(ret);
}
return ret;
};
Cipher.prototype.setAutoPadding = function setAutoPadding(ap) {
if (!this[kHandle].setAutoPadding(!!ap))
throw new ERR_CRYPTO_INVALID_STATE("setAutoPadding");
return this;
};
Cipher.prototype.getAuthTag = function getAuthTag() {
const ret = this[kHandle].getAuthTag();
if (ret === undefined) throw new ERR_CRYPTO_INVALID_STATE("getAuthTag");
return ret;
};
function setAuthTag(tagbuf, encoding) {
tagbuf = getArrayBufferOrView(tagbuf, "buffer", encoding);
if (!this[kHandle].setAuthTag(tagbuf))
throw new ERR_CRYPTO_INVALID_STATE("setAuthTag");
return this;
}
Cipher.prototype.setAAD = function setAAD(aadbuf, options) {
const encoding = getStringOption(options, "encoding");
const plaintextLength = getUIntOption(options, "plaintextLength");
aadbuf = getArrayBufferOrView(aadbuf, "aadbuf", encoding);
if (!this[kHandle].setAAD(aadbuf, plaintextLength))
throw new ERR_CRYPTO_INVALID_STATE("setAAD");
return this;
};
// The Cipheriv class is part of the legacy Node.js crypto API. It exposes
// a stream-based encryption/decryption model. For backwards compatibility
// the Cipheriv class is defined using the legacy function syntax rather than
// ES6 classes.
function Cipheriv(cipher, key, iv, options) {
if (!(this instanceof Cipheriv))
return new Cipheriv(cipher, key, iv, options);
ReflectApply(createCipherWithIV, this, [cipher, key, options, true, iv]);
}
function addCipherPrototypeFunctions(constructor) {
constructor.prototype._transform = Cipher.prototype._transform;
constructor.prototype._flush = Cipher.prototype._flush;
constructor.prototype.update = Cipher.prototype.update;
constructor.prototype.final = Cipher.prototype.final;
constructor.prototype.setAutoPadding = Cipher.prototype.setAutoPadding;
if (constructor === Cipheriv) {
constructor.prototype.getAuthTag = Cipher.prototype.getAuthTag;
} else {
constructor.prototype.setAuthTag = setAuthTag;
}
constructor.prototype.setAAD = Cipher.prototype.setAAD;
}
Object.setPrototypeOf(Cipheriv.prototype, LazyTransform.prototype);
Object.setPrototypeOf(Cipheriv, LazyTransform);
addCipherPrototypeFunctions(Cipheriv);
// The Decipher class is part of the legacy Node.js crypto API. It exposes
// a stream-based encryption/decryption model. For backwards compatibility
// the Decipher class is defined using the legacy function syntax rather than
// ES6 classes.
function Decipher(cipher, password, options) {
if (!(this instanceof Decipher))
return new Decipher(cipher, password, options);
}
Object.setPrototypeOf(Decipher.prototype, LazyTransform.prototype);
Object.setPrototypeOf(Decipher, LazyTransform);
addCipherPrototypeFunctions(Decipher);
// The Decipheriv class is part of the legacy Node.js crypto API. It exposes
// a stream-based encryption/decryption model. For backwards compatibility
// the Decipheriv class is defined using the legacy function syntax rather than
// ES6 classes.
function Decipheriv(cipher, key, iv, options) {
if (!(this instanceof Decipheriv))
return new Decipheriv(cipher, key, iv, options);
ReflectApply(createCipherWithIV, this, [cipher, key, options, false, iv]);
}
Object.setPrototypeOf(Decipheriv.prototype, LazyTransform.prototype);
Object.setPrototypeOf(Decipheriv, LazyTransform);
addCipherPrototypeFunctions(Decipheriv);
function getCipherInfo(nameOrNid, options) {
if (typeof nameOrNid !== "string" && typeof nameOrNid !== "number") {
throw new ERR_INVALID_ARG_TYPE(
"nameOrNid",
["string", "number"],
nameOrNid,
);
}
if (typeof nameOrNid === "number") validateInt32(nameOrNid, "nameOrNid");
let keyLength, ivLength;
if (options !== undefined) {
validateObject(options, "options");
({ keyLength, ivLength } = options);
if (keyLength !== undefined) validateInt32(keyLength, "options.keyLength");
if (ivLength !== undefined) validateInt32(ivLength, "options.ivLength");
}
const ret = _getCipherInfo({}, nameOrNid, keyLength, ivLength);
if (ret !== undefined) {
if (ret.name) ret.name = String.prototype.toLowerCase.call(ret.name);
if (ret.type) ret.type = String.prototype.toLowerCase.call(ret.type);
}
return ret;
}
export { Cipheriv };
export { Decipheriv };
export { privateDecrypt };
export { privateEncrypt };
export { publicDecrypt };
export { publicEncrypt };
export { getCipherInfo };