Comparing version 2.1.97 to 2.2.0
@@ -77,2 +77,9 @@ "use strict"; | ||
this.recipientCertificate = (0, _pvutils.getParametersValue)(parameters, "recipientCertificate", KeyAgreeRecipientInfo.defaultValues("recipientCertificate")); //endregion | ||
/** | ||
* @type {CryptoKey} | ||
* @desc recipientPublicKey | ||
*/ | ||
this.recipientPublicKey = (0, _pvutils.getParametersValue)(parameters, "recipientPublicKey", KeyAgreeRecipientInfo.defaultValues("recipientPublicKey")); //endregion | ||
//region If input argument array contains "schema" for this object | ||
@@ -109,2 +116,5 @@ | ||
case "recipientPublicKey": | ||
return null; | ||
default: | ||
@@ -143,2 +153,5 @@ throw new Error(`Invalid member name for KeyAgreeRecipientInfo class: ${memberName}`); | ||
case "recipientPublicKey": | ||
return false; | ||
default: | ||
@@ -145,0 +158,0 @@ throw new Error(`Invalid member name for KeyAgreeRecipientInfo class: ${memberName}`); |
@@ -82,4 +82,6 @@ { | ||
"ex18:es5": "cd examples/OpenSSLPrivateKeyEncryption && npm run build:es5 && cd ../..", | ||
"build:examples": "npm run ex1 && npm run ex2 && npm run ex3 && npm run ex4 && npm run ex5 && npm run ex6 && npm run ex7 && npm run ex8 && npm run ex9 && npm run ex10 && npm run ex11 && npm run ex12 && npm run ex13 && npm run ex14 && npm run ex15 && npm run ex16 && npm run ex17 && npm run ex18", | ||
"build:examples:es5": "npm run ex1:es5 && npm run ex2:es5 && npm run ex3 && npm run ex4:es5 && npm run ex5:es5 && npm run ex6:es5 && npm run ex7:es5 && npm run ex8:es5 && npm run ex9:es5 && npm run ex10:es5 && npm run ex11:es5 && npm run ex12:es5 && npm run ex13:es5 && npm run ex14:es5 && npm run ex15:es5 && npm run ex16:es5 && npm run ex18:es5", | ||
"ex19": "cd examples/HowToEncryptCMSviaKey && npm run build && cd ../..", | ||
"ex19:es5": "cd examples/HowToEncryptCMSviaKey && npm run build:es5 && cd ../..", | ||
"build:examples": "npm run ex1 && npm run ex2 && npm run ex3 && npm run ex4 && npm run ex5 && npm run ex6 && npm run ex7 && npm run ex8 && npm run ex9 && npm run ex10 && npm run ex11 && npm run ex12 && npm run ex13 && npm run ex14 && npm run ex15 && npm run ex16 && npm run ex17 && npm run ex18 && npm run ex19", | ||
"build:examples:es5": "npm run ex1:es5 && npm run ex2:es5 && npm run ex3 && npm run ex4:es5 && npm run ex5:es5 && npm run ex6:es5 && npm run ex7:es5 && npm run ex8:es5 && npm run ex9:es5 && npm run ex10:es5 && npm run ex11:es5 && npm run ex12:es5 && npm run ex13:es5 && npm run ex14:es5 && npm run ex15:es5 && npm run ex16:es5 && npm run ex18:es5 && npm run ex19:es5", | ||
"build:tests": "npm run build:examples", | ||
@@ -109,3 +111,3 @@ "build:docs": "esdoc", | ||
"name": "pkijs", | ||
"version": "2.1.97", | ||
"version": "2.2.0", | ||
"license": "BSD-3-Clause", | ||
@@ -112,0 +114,0 @@ "esdoc": { |
@@ -12,2 +12,3 @@ import * as asn1js from "asn1js"; | ||
import IssuerAndSerialNumber from "./IssuerAndSerialNumber.js"; | ||
import RecipientKeyIdentifier from "./RecipientKeyIdentifier.js"; | ||
import RecipientEncryptedKey from "./RecipientEncryptedKey.js"; | ||
@@ -25,2 +26,12 @@ import KeyAgreeRecipientIdentifier from "./KeyAgreeRecipientIdentifier.js"; | ||
//************************************************************************************** | ||
const defaultEncryptionParams = { | ||
kdfAlgorithm: "SHA-512", | ||
kekEncryptionLength: 256 | ||
}; | ||
const curveLengthByName = { | ||
"P-256": 256, | ||
"P-384": 384, | ||
"P-521": 528 | ||
}; | ||
//************************************************************************************** | ||
/** | ||
@@ -334,6 +345,10 @@ * Class from RFC5652 | ||
{ | ||
//region Initial variables | ||
const encryptionParameters = parameters || {}; | ||
//endregion | ||
//region Initialize encryption parameters | ||
const encryptionParameters = Object.assign( | ||
{ useOAEP: true, oaepHashAlgorithm: "SHA-512" }, | ||
defaultEncryptionParams, | ||
parameters || {} | ||
); | ||
//endregion | ||
//region Check type of certificate | ||
@@ -349,16 +364,2 @@ if(certificate.subjectPublicKeyInfo.algorithm.algorithmId.indexOf("1.2.840.113549") !== (-1)) | ||
} | ||
//endregion | ||
//region Initialize encryption parameters | ||
if(("oaepHashAlgorithm" in encryptionParameters) === false) | ||
encryptionParameters.oaepHashAlgorithm = "SHA-512"; | ||
if(("kdfAlgorithm" in encryptionParameters) === false) | ||
encryptionParameters.kdfAlgorithm = "SHA-512"; | ||
if(("kekEncryptionLength" in encryptionParameters) === false) | ||
encryptionParameters.kekEncryptionLength = 256; | ||
if(("useOAEP" in encryptionParameters) === false) | ||
encryptionParameters.useOAEP = true; | ||
//endregion | ||
@@ -446,64 +447,14 @@ | ||
{ | ||
//region RecipientEncryptedKey | ||
const encryptedKey = new RecipientEncryptedKey({ | ||
rid: new KeyAgreeRecipientIdentifier({ | ||
variant: 1, | ||
value: new IssuerAndSerialNumber({ | ||
issuer: certificate.issuer, | ||
serialNumber: certificate.serialNumber | ||
}) | ||
const recipientIdentifier = new KeyAgreeRecipientIdentifier({ | ||
variant: 1, | ||
value: new IssuerAndSerialNumber({ | ||
issuer: certificate.issuer, | ||
serialNumber: certificate.serialNumber | ||
}) | ||
// "encryptedKey" will be calculated in "encrypt" function | ||
}); | ||
//endregion | ||
//region keyEncryptionAlgorithm | ||
const aesKWoid = getOIDByAlgorithm({ | ||
name: "AES-KW", | ||
length: encryptionParameters.kekEncryptionLength | ||
}); | ||
if(aesKWoid === "") | ||
throw new Error(`Unknown length for key encryption algorithm: ${encryptionParameters.kekEncryptionLength}`); | ||
const aesKW = new AlgorithmIdentifier({ | ||
algorithmId: aesKWoid, | ||
algorithmParams: new asn1js.Null() | ||
}); | ||
//endregion | ||
//region KeyAgreeRecipientInfo | ||
const ecdhOID = getOIDByAlgorithm({ | ||
name: "ECDH", | ||
kdf: encryptionParameters.kdfAlgorithm | ||
}); | ||
if(ecdhOID === "") | ||
throw new Error(`Unknown KDF algorithm: ${encryptionParameters.kdfAlgorithm}`); | ||
// In fact there is no need in so long UKM, but RFC2631 | ||
// has requirement that "UserKeyMaterial" must be 512 bits long | ||
const ukmBuffer = new ArrayBuffer(64); | ||
const ukmView = new Uint8Array(ukmBuffer); | ||
getRandomValues(ukmView); // Generate random values in 64 bytes long buffer | ||
const keyInfo = new KeyAgreeRecipientInfo({ | ||
version: 3, | ||
// "originator" will be calculated in "encrypt" function because ephemeral key would be generated there | ||
ukm: new asn1js.OctetString({ valueHex: ukmBuffer }), | ||
keyEncryptionAlgorithm: new AlgorithmIdentifier({ | ||
algorithmId: ecdhOID, | ||
algorithmParams: aesKW.toSchema() | ||
}), | ||
recipientEncryptedKeys: new RecipientEncryptedKeys({ | ||
encryptedKeys: [encryptedKey] | ||
}), | ||
recipientCertificate: certificate | ||
}); | ||
//endregion | ||
//region Final values for "CMS_ENVELOPED_DATA" | ||
this.recipientInfos.push(new RecipientInfo({ | ||
variant: 2, | ||
value: keyInfo | ||
})); | ||
//endregion | ||
this._addKeyAgreeRecipientInfo( | ||
recipientIdentifier, | ||
encryptionParameters, | ||
{recipientCertificate: certificate} | ||
); | ||
} | ||
@@ -682,2 +633,93 @@ break; | ||
/** | ||
* Add a "RecipientInfo" using a KeyAgreeRecipientInfo of type RecipientKeyIdentifier. | ||
* @param {CryptoKey} [key] Recipient's public key | ||
* @param {ArrayBuffer} [keyId] The id for the recipient's public key | ||
* @param {Object} [parameters] Additional parameters for "fine tuning" the encryption process | ||
*/ | ||
addRecipientByKeyIdentifier(key, keyId, parameters) | ||
{ | ||
//region Initialize encryption parameters | ||
const encryptionParameters = Object.assign({}, defaultEncryptionParams, parameters || {}); | ||
//endregion | ||
const recipientIdentifier = new KeyAgreeRecipientIdentifier({ | ||
variant: 2, | ||
value: new RecipientKeyIdentifier({ | ||
subjectKeyIdentifier: new asn1js.OctetString({valueHex: keyId}), | ||
}) | ||
}); | ||
this._addKeyAgreeRecipientInfo( | ||
recipientIdentifier, | ||
encryptionParameters, | ||
{recipientPublicKey: key} | ||
); | ||
} | ||
//********************************************************************************** | ||
/** | ||
* Add a "RecipientInfo" using a KeyAgreeRecipientInfo of type RecipientKeyIdentifier. | ||
* @param {KeyAgreeRecipientIdentifier} [recipientIdentifier] Recipient identifier | ||
* @param {Object} [encryptionParameters] Additional parameters for "fine tuning" the encryption process | ||
* @param {Object} [extraRecipientInfoParams] Additional params for KeyAgreeRecipientInfo | ||
*/ | ||
_addKeyAgreeRecipientInfo(recipientIdentifier, encryptionParameters, extraRecipientInfoParams) | ||
{ | ||
//region RecipientEncryptedKey | ||
const encryptedKey = new RecipientEncryptedKey({ | ||
rid: recipientIdentifier | ||
// "encryptedKey" will be calculated in "encrypt" function | ||
}); | ||
//endregion | ||
//region keyEncryptionAlgorithm | ||
const aesKWoid = getOIDByAlgorithm({ | ||
name: "AES-KW", | ||
length: encryptionParameters.kekEncryptionLength | ||
}); | ||
if (aesKWoid === "") | ||
throw new Error(`Unknown length for key encryption algorithm: ${encryptionParameters.kekEncryptionLength}`); | ||
const aesKW = new AlgorithmIdentifier({ | ||
algorithmId: aesKWoid, | ||
algorithmParams: new asn1js.Null() | ||
}); | ||
//endregion | ||
//region KeyAgreeRecipientInfo | ||
const ecdhOID = getOIDByAlgorithm({ | ||
name: "ECDH", | ||
kdf: encryptionParameters.kdfAlgorithm | ||
}); | ||
if (ecdhOID === "") | ||
throw new Error(`Unknown KDF algorithm: ${encryptionParameters.kdfAlgorithm}`); | ||
// In fact there is no need in so long UKM, but RFC2631 | ||
// has requirement that "UserKeyMaterial" must be 512 bits long | ||
const ukmBuffer = new ArrayBuffer(64); | ||
const ukmView = new Uint8Array(ukmBuffer); | ||
getRandomValues(ukmView); // Generate random values in 64 bytes long buffer | ||
const recipientInfoParams = { | ||
version: 3, | ||
// "originator" will be calculated in "encrypt" function because ephemeral key would be generated there | ||
ukm: new asn1js.OctetString({valueHex: ukmBuffer}), | ||
keyEncryptionAlgorithm: new AlgorithmIdentifier({ | ||
algorithmId: ecdhOID, | ||
algorithmParams: aesKW.toSchema() | ||
}), | ||
recipientEncryptedKeys: new RecipientEncryptedKeys({ | ||
encryptedKeys: [encryptedKey] | ||
}) | ||
}; | ||
const keyInfo = new KeyAgreeRecipientInfo(Object.assign(recipientInfoParams, extraRecipientInfoParams)); | ||
//endregion | ||
//region Final values for "CMS_ENVELOPED_DATA" | ||
this.recipientInfos.push(new RecipientInfo({ | ||
variant: 2, | ||
value: keyInfo | ||
})); | ||
//endregion | ||
} | ||
//********************************************************************************** | ||
/** | ||
* Create a new CMS Enveloped Data content with encrypted data | ||
@@ -777,6 +819,9 @@ * @param {Object} contentEncryptionAlgorithm WebCrypto algorithm. For the moment here could be only "AES-CBC" or "AES-GCM" algorithms. | ||
let currentSequence = Promise.resolve(); | ||
const recipientInfo = _this.recipientInfos[index]; | ||
let ecdhPublicKey; | ||
let ecdhPrivateKey; | ||
let recipientPublicKey; | ||
let recipientCurve; | ||
@@ -787,32 +832,42 @@ let recipientCurveLength; | ||
//endregion | ||
//region Get "namedCurve" parameter from recipient's certificate | ||
//region Get public key and named curve from recipient's certificate or public key | ||
currentSequence = currentSequence.then(() => | ||
{ | ||
const curveObject = _this.recipientInfos[index].value.recipientCertificate.subjectPublicKeyInfo.algorithm.algorithmParams; | ||
if(curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) | ||
return Promise.reject(`Incorrect "recipientCertificate" for index ${index}`); | ||
const curveOID = curveObject.valueBlock.toString(); | ||
switch(curveOID) | ||
{ | ||
case "1.2.840.10045.3.1.7": | ||
recipientCurve = "P-256"; | ||
recipientCurveLength = 256; | ||
break; | ||
case "1.3.132.0.34": | ||
recipientCurve = "P-384"; | ||
recipientCurveLength = 384; | ||
break; | ||
case "1.3.132.0.35": | ||
recipientCurve = "P-521"; | ||
recipientCurveLength = 528; | ||
break; | ||
default: | ||
return Promise.reject(`Incorrect curve OID for index ${index}`); | ||
if (recipientInfo.value.recipientPublicKey) { | ||
recipientCurve = recipientInfo.value.recipientPublicKey.algorithm.namedCurve; | ||
return recipientInfo.value.recipientPublicKey; | ||
} else { | ||
const curveObject = recipientInfo.value.recipientCertificate.subjectPublicKeyInfo.algorithm.algorithmParams; | ||
if (curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) | ||
return Promise.reject(`Incorrect "recipientCertificate" for index ${index}`); | ||
const curveOID = curveObject.valueBlock.toString(); | ||
switch (curveOID) { | ||
case "1.2.840.10045.3.1.7": | ||
recipientCurve = "P-256"; | ||
break; | ||
case "1.3.132.0.34": | ||
recipientCurve = "P-384"; | ||
break; | ||
case "1.3.132.0.35": | ||
recipientCurve = "P-521"; | ||
break; | ||
default: | ||
return Promise.reject(`Incorrect curve OID for index ${index}`); | ||
} | ||
return recipientInfo.value.recipientCertificate.getPublicKey({ | ||
algorithm: { | ||
algorithm: { | ||
name: "ECDH", | ||
namedCurve: recipientCurve | ||
}, | ||
usages: [] | ||
} | ||
}); | ||
} | ||
return recipientCurve; | ||
}, error => | ||
@@ -823,9 +878,12 @@ Promise.reject(error)); | ||
//region Generate ephemeral ECDH key | ||
currentSequence = currentSequence.then(result => | ||
crypto.generateKey({ | ||
name: "ECDH", | ||
namedCurve: result | ||
}, | ||
true, | ||
["deriveBits"]), | ||
currentSequence = currentSequence.then(result => { | ||
recipientPublicKey = result; | ||
recipientCurveLength = curveLengthByName[recipientCurve]; | ||
return crypto.generateKey( | ||
{name: "ECDH", namedCurve: recipientCurve}, | ||
true, | ||
["deriveBits"] | ||
); | ||
}, | ||
error => | ||
@@ -846,17 +904,7 @@ Promise.reject(error) | ||
//endregion | ||
//region Import recipient's public key | ||
//region Save public key of ephemeral ECDH key pair | ||
currentSequence = currentSequence.then(result => | ||
{ | ||
exportedECDHPublicKey = result; | ||
return _this.recipientInfos[index].value.recipientCertificate.getPublicKey({ | ||
algorithm: { | ||
algorithm: { | ||
name: "ECDH", | ||
namedCurve: recipientCurve | ||
}, | ||
usages: [] | ||
} | ||
}); | ||
}, error => | ||
@@ -866,5 +914,5 @@ Promise.reject(error)); | ||
//region Create shared secret | ||
currentSequence = currentSequence.then(result => crypto.deriveBits({ | ||
currentSequence = currentSequence.then(() => crypto.deriveBits({ | ||
name: "ECDH", | ||
public: result | ||
public: recipientPublicKey | ||
}, | ||
@@ -885,4 +933,4 @@ ecdhPrivateKey, | ||
//region Get length of used AES-KW algorithm | ||
const aesKWAlgorithm = new AlgorithmIdentifier({ schema: _this.recipientInfos[index].value.keyEncryptionAlgorithm.algorithmParams }); | ||
const aesKWAlgorithm = new AlgorithmIdentifier({ schema: recipientInfo.value.keyEncryptionAlgorithm.algorithmParams }); | ||
const KWalgorithm = getAlgorithmByOID(aesKWAlgorithm.algorithmId); | ||
@@ -917,3 +965,3 @@ if(("name" in KWalgorithm) === false) | ||
}), | ||
entityUInfo: _this.recipientInfos[index].value.ukm, | ||
entityUInfo: recipientInfo.value.ukm, | ||
suppPubInfo: new asn1js.OctetString({ valueHex: kwLengthBuffer }) | ||
@@ -926,5 +974,5 @@ }); | ||
//region Get SHA algorithm used together with ECDH | ||
const ecdhAlgorithm = getAlgorithmByOID(_this.recipientInfos[index].value.keyEncryptionAlgorithm.algorithmId); | ||
const ecdhAlgorithm = getAlgorithmByOID(recipientInfo.value.keyEncryptionAlgorithm.algorithmId); | ||
if(("name" in ecdhAlgorithm) === false) | ||
return Promise.reject(`Incorrect OID for key encryption algorithm: ${_this.recipientInfos[index].value.keyEncryptionAlgorithm.algorithmId}`); | ||
return Promise.reject(`Incorrect OID for key encryption algorithm: ${recipientInfo.value.keyEncryptionAlgorithm.algorithmId}`); | ||
//endregion | ||
@@ -959,7 +1007,4 @@ | ||
originator.value = new OriginatorPublicKey({ schema: asn1.result }); | ||
// There is option when we can stay with ECParameters, but here index prefer to avoid the params | ||
if("algorithmParams" in originator.value.algorithm) | ||
delete originator.value.algorithm.algorithmParams; | ||
_this.recipientInfos[index].value.originator = originator; | ||
recipientInfo.value.originator = originator; | ||
//endregion | ||
@@ -971,3 +1016,3 @@ | ||
*/ | ||
_this.recipientInfos[index].value.recipientEncryptedKeys.encryptedKeys[0].encryptedKey = new asn1js.OctetString({ valueHex: result }); | ||
recipientInfo.value.recipientEncryptedKeys.encryptedKeys[0].encryptedKey = new asn1js.OctetString({ valueHex: result }); | ||
//endregion | ||
@@ -1263,19 +1308,27 @@ | ||
//endregion | ||
const originator = _this.recipientInfos[index].value.originator; | ||
//region Get "namedCurve" parameter from recipient's certificate | ||
currentSequence = currentSequence.then(() => | ||
{ | ||
if(("recipientCertificate" in decryptionParameters) === false) | ||
return Promise.reject("Parameter \"recipientCertificate\" is mandatory for \"KeyAgreeRecipientInfo\""); | ||
if ("recipientCertificate" in decryptionParameters) { | ||
const curveObject = decryptionParameters.recipientCertificate.subjectPublicKeyInfo.algorithm.algorithmParams; | ||
if(curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) { | ||
return Promise.reject(`Incorrect "recipientCertificate" for index ${index}`); | ||
} | ||
curveOID = curveObject.valueBlock.toString(); | ||
} else if ("algorithmParams" in originator.value.algorithm) { | ||
const curveObject = originator.value.algorithm.algorithmParams; | ||
if(curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) { | ||
return Promise.reject(`Incorrect originator for index ${index}`); | ||
} | ||
curveOID = curveObject.valueBlock.toString(); | ||
} else { | ||
return Promise.reject("Parameter \"recipientCertificate\" is mandatory for \"KeyAgreeRecipientInfo\" if algorithm params are missing from originator"); | ||
} | ||
if(("recipientPrivateKey" in decryptionParameters) === false) | ||
return Promise.reject("Parameter \"recipientPrivateKey\" is mandatory for \"KeyAgreeRecipientInfo\""); | ||
const curveObject = decryptionParameters.recipientCertificate.subjectPublicKeyInfo.algorithm.algorithmParams; | ||
if(curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) | ||
return Promise.reject(`Incorrect "recipientCertificate" for index ${index}`); | ||
curveOID = curveObject.valueBlock.toString(); | ||
switch(curveOID) | ||
@@ -1318,8 +1371,8 @@ { | ||
//region Change "OriginatorPublicKey" if "curve" parameter absent | ||
if(("algorithmParams" in _this.recipientInfos[index].value.originator.value.algorithm) === false) | ||
_this.recipientInfos[index].value.originator.value.algorithm.algorithmParams = new asn1js.ObjectIdentifier({ value: curveOID }); | ||
if(("algorithmParams" in originator.value.algorithm) === false) | ||
originator.value.algorithm.algorithmParams = new asn1js.ObjectIdentifier({ value: curveOID }); | ||
//endregion | ||
//region Create ArrayBuffer with sender's public key | ||
const buffer = _this.recipientInfos[index].value.originator.value.toSchema().toBER(false); | ||
const buffer = originator.value.toSchema().toBER(false); | ||
//endregion | ||
@@ -1326,0 +1379,0 @@ |
@@ -56,2 +56,8 @@ import * as asn1js from "asn1js"; | ||
//endregion | ||
/** | ||
* @type {CryptoKey} | ||
* @desc recipientPublicKey | ||
*/ | ||
this.recipientPublicKey = getParametersValue(parameters, "recipientPublicKey", KeyAgreeRecipientInfo.defaultValues("recipientPublicKey")); | ||
//endregion | ||
@@ -84,2 +90,4 @@ //region If input argument array contains "schema" for this object | ||
return new Certificate(); | ||
case "recipientPublicKey": | ||
return null; | ||
default: | ||
@@ -111,2 +119,4 @@ throw new Error(`Invalid member name for KeyAgreeRecipientInfo class: ${memberName}`); | ||
return false; // For now leave it as is | ||
case "recipientPublicKey": | ||
return false; | ||
default: | ||
@@ -113,0 +123,0 @@ throw new Error(`Invalid member name for KeyAgreeRecipientInfo class: ${memberName}`); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
4526649
63360