You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1261 lines
				
				39 KiB
			
		
		
			
		
	
	
					1261 lines
				
				39 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Javascript implementation of PKCS#7 v1.5.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @author Stefan Siegl
							 | 
						||
| 
								 | 
							
								 * @author Dave Longley
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2012-2015 Digital Bazaar, Inc.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Currently this implementation only supports ContentType of EnvelopedData,
							 | 
						||
| 
								 | 
							
								 * EncryptedData, or SignedData at the root level. The top level elements may
							 | 
						||
| 
								 | 
							
								 * contain only a ContentInfo of ContentType Data, i.e. plain data. Further
							 | 
						||
| 
								 | 
							
								 * nesting is not (yet) supported.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The Forge validators for PKCS #7's ASN.1 structures are available from
							 | 
						||
| 
								 | 
							
								 * a separate file pkcs7asn1.js, since those are referenced from other
							 | 
						||
| 
								 | 
							
								 * PKCS standards like PKCS #12.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var forge = require('./forge');
							 | 
						||
| 
								 | 
							
								require('./aes');
							 | 
						||
| 
								 | 
							
								require('./asn1');
							 | 
						||
| 
								 | 
							
								require('./des');
							 | 
						||
| 
								 | 
							
								require('./oids');
							 | 
						||
| 
								 | 
							
								require('./pem');
							 | 
						||
| 
								 | 
							
								require('./pkcs7asn1');
							 | 
						||
| 
								 | 
							
								require('./random');
							 | 
						||
| 
								 | 
							
								require('./util');
							 | 
						||
| 
								 | 
							
								require('./x509');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// shortcut for ASN.1 API
							 | 
						||
| 
								 | 
							
								var asn1 = forge.asn1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// shortcut for PKCS#7 API
							 | 
						||
| 
								 | 
							
								var p7 = module.exports = forge.pkcs7 = forge.pkcs7 || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a PKCS#7 message from PEM format.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param pem the PEM-formatted PKCS#7 message.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the PKCS#7 message.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								p7.messageFromPem = function(pem) {
							 | 
						||
| 
								 | 
							
								  var msg = forge.pem.decode(pem)[0];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(msg.type !== 'PKCS7') {
							 | 
						||
| 
								 | 
							
								    var error = new Error('Could not convert PKCS#7 message from PEM; PEM ' +
							 | 
						||
| 
								 | 
							
								      'header type is not "PKCS#7".');
							 | 
						||
| 
								 | 
							
								    error.headerType = msg.type;
							 | 
						||
| 
								 | 
							
								    throw error;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if(msg.procType && msg.procType.type === 'ENCRYPTED') {
							 | 
						||
| 
								 | 
							
								    throw new Error('Could not convert PKCS#7 message from PEM; PEM is encrypted.');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // convert DER to ASN.1 object
							 | 
						||
| 
								 | 
							
								  var obj = asn1.fromDer(msg.body);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return p7.messageFromAsn1(obj);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a PKCS#7 message to PEM format.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param msg The PKCS#7 message object
							 | 
						||
| 
								 | 
							
								 * @param maxline The maximum characters per line, defaults to 64.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return The PEM-formatted PKCS#7 message.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								p7.messageToPem = function(msg, maxline) {
							 | 
						||
| 
								 | 
							
								  // convert to ASN.1, then DER, then PEM-encode
							 | 
						||
| 
								 | 
							
								  var pemObj = {
							 | 
						||
| 
								 | 
							
								    type: 'PKCS7',
							 | 
						||
| 
								 | 
							
								    body: asn1.toDer(msg.toAsn1()).getBytes()
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								  return forge.pem.encode(pemObj, {maxline: maxline});
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a PKCS#7 message from an ASN.1 object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param obj the ASN.1 representation of a ContentInfo.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the PKCS#7 message.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								p7.messageFromAsn1 = function(obj) {
							 | 
						||
| 
								 | 
							
								  // validate root level ContentInfo and capture data
							 | 
						||
| 
								 | 
							
								  var capture = {};
							 | 
						||
| 
								 | 
							
								  var errors = [];
							 | 
						||
| 
								 | 
							
								  if(!asn1.validate(obj, p7.asn1.contentInfoValidator, capture, errors)) {
							 | 
						||
| 
								 | 
							
								    var error = new Error('Cannot read PKCS#7 message. ' +
							 | 
						||
| 
								 | 
							
								      'ASN.1 object is not an PKCS#7 ContentInfo.');
							 | 
						||
| 
								 | 
							
								    error.errors = errors;
							 | 
						||
| 
								 | 
							
								    throw error;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var contentType = asn1.derToOid(capture.contentType);
							 | 
						||
| 
								 | 
							
								  var msg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch(contentType) {
							 | 
						||
| 
								 | 
							
								    case forge.pki.oids.envelopedData:
							 | 
						||
| 
								 | 
							
								      msg = p7.createEnvelopedData();
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case forge.pki.oids.encryptedData:
							 | 
						||
| 
								 | 
							
								      msg = p7.createEncryptedData();
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case forge.pki.oids.signedData:
							 | 
						||
| 
								 | 
							
								      msg = p7.createSignedData();
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      throw new Error('Cannot read PKCS#7 message. ContentType with OID ' +
							 | 
						||
| 
								 | 
							
								        contentType + ' is not (yet) supported.');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  msg.fromAsn1(capture.content.value[0]);
							 | 
						||
| 
								 | 
							
								  return msg;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								p7.createSignedData = function() {
							 | 
						||
| 
								 | 
							
								  var msg = null;
							 | 
						||
| 
								 | 
							
								  msg = {
							 | 
						||
| 
								 | 
							
								    type: forge.pki.oids.signedData,
							 | 
						||
| 
								 | 
							
								    version: 1,
							 | 
						||
| 
								 | 
							
								    certificates: [],
							 | 
						||
| 
								 | 
							
								    crls: [],
							 | 
						||
| 
								 | 
							
								    // TODO: add json-formatted signer stuff here?
							 | 
						||
| 
								 | 
							
								    signers: [],
							 | 
						||
| 
								 | 
							
								    // populated during sign()
							 | 
						||
| 
								 | 
							
								    digestAlgorithmIdentifiers: [],
							 | 
						||
| 
								 | 
							
								    contentInfo: null,
							 | 
						||
| 
								 | 
							
								    signerInfos: [],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    fromAsn1: function(obj) {
							 | 
						||
| 
								 | 
							
								      // validate SignedData content block and capture data.
							 | 
						||
| 
								 | 
							
								      _fromAsn1(msg, obj, p7.asn1.signedDataValidator);
							 | 
						||
| 
								 | 
							
								      msg.certificates = [];
							 | 
						||
| 
								 | 
							
								      msg.crls = [];
							 | 
						||
| 
								 | 
							
								      msg.digestAlgorithmIdentifiers = [];
							 | 
						||
| 
								 | 
							
								      msg.contentInfo = null;
							 | 
						||
| 
								 | 
							
								      msg.signerInfos = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if(msg.rawCapture.certificates) {
							 | 
						||
| 
								 | 
							
								        var certs = msg.rawCapture.certificates.value;
							 | 
						||
| 
								 | 
							
								        for(var i = 0; i < certs.length; ++i) {
							 | 
						||
| 
								 | 
							
								          msg.certificates.push(forge.pki.certificateFromAsn1(certs[i]));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // TODO: parse crls
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    toAsn1: function() {
							 | 
						||
| 
								 | 
							
								      // degenerate case with no content
							 | 
						||
| 
								 | 
							
								      if(!msg.contentInfo) {
							 | 
						||
| 
								 | 
							
								        msg.sign();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var certs = [];
							 | 
						||
| 
								 | 
							
								      for(var i = 0; i < msg.certificates.length; ++i) {
							 | 
						||
| 
								 | 
							
								        certs.push(forge.pki.certificateToAsn1(msg.certificates[i]));
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var crls = [];
							 | 
						||
| 
								 | 
							
								      // TODO: implement CRLs
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // [0] SignedData
							 | 
						||
| 
								 | 
							
								      var signedData = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
							 | 
						||
| 
								 | 
							
								        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								          // Version
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
							 | 
						||
| 
								 | 
							
								            asn1.integerToDer(msg.version).getBytes()),
							 | 
						||
| 
								 | 
							
								          // DigestAlgorithmIdentifiers
							 | 
						||
| 
								 | 
							
								          asn1.create(
							 | 
						||
| 
								 | 
							
								            asn1.Class.UNIVERSAL, asn1.Type.SET, true,
							 | 
						||
| 
								 | 
							
								            msg.digestAlgorithmIdentifiers),
							 | 
						||
| 
								 | 
							
								          // ContentInfo
							 | 
						||
| 
								 | 
							
								          msg.contentInfo
							 | 
						||
| 
								 | 
							
								        ])
							 | 
						||
| 
								 | 
							
								      ]);
							 | 
						||
| 
								 | 
							
								      if(certs.length > 0) {
							 | 
						||
| 
								 | 
							
								        // [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL
							 | 
						||
| 
								 | 
							
								        signedData.value[0].value.push(
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, certs));
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if(crls.length > 0) {
							 | 
						||
| 
								 | 
							
								        // [1] IMPLICIT CertificateRevocationLists OPTIONAL
							 | 
						||
| 
								 | 
							
								        signedData.value[0].value.push(
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, crls));
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      // SignerInfos
							 | 
						||
| 
								 | 
							
								      signedData.value[0].value.push(
							 | 
						||
| 
								 | 
							
								        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
							 | 
						||
| 
								 | 
							
								          msg.signerInfos));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // ContentInfo
							 | 
						||
| 
								 | 
							
								      return asn1.create(
							 | 
						||
| 
								 | 
							
								        asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								          // ContentType
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								            asn1.oidToDer(msg.type).getBytes()),
							 | 
						||
| 
								 | 
							
								          // [0] SignedData
							 | 
						||
| 
								 | 
							
								          signedData
							 | 
						||
| 
								 | 
							
								        ]);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Add (another) entity to list of signers.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Note: If authenticatedAttributes are provided, then, per RFC 2315,
							 | 
						||
| 
								 | 
							
								     * they must include at least two attributes: content type and
							 | 
						||
| 
								 | 
							
								     * message digest. The message digest attribute value will be
							 | 
						||
| 
								 | 
							
								     * auto-calculated during signing and will be ignored if provided.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Here's an example of providing these two attributes:
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * forge.pkcs7.createSignedData();
							 | 
						||
| 
								 | 
							
								     * p7.addSigner({
							 | 
						||
| 
								 | 
							
								     *   issuer: cert.issuer.attributes,
							 | 
						||
| 
								 | 
							
								     *   serialNumber: cert.serialNumber,
							 | 
						||
| 
								 | 
							
								     *   key: privateKey,
							 | 
						||
| 
								 | 
							
								     *   digestAlgorithm: forge.pki.oids.sha1,
							 | 
						||
| 
								 | 
							
								     *   authenticatedAttributes: [{
							 | 
						||
| 
								 | 
							
								     *     type: forge.pki.oids.contentType,
							 | 
						||
| 
								 | 
							
								     *     value: forge.pki.oids.data
							 | 
						||
| 
								 | 
							
								     *   }, {
							 | 
						||
| 
								 | 
							
								     *     type: forge.pki.oids.messageDigest
							 | 
						||
| 
								 | 
							
								     *   }]
							 | 
						||
| 
								 | 
							
								     * });
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * TODO: Support [subjectKeyIdentifier] as signer's ID.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param signer the signer information:
							 | 
						||
| 
								 | 
							
								     *          key the signer's private key.
							 | 
						||
| 
								 | 
							
								     *          [certificate] a certificate containing the public key
							 | 
						||
| 
								 | 
							
								     *            associated with the signer's private key; use this option as
							 | 
						||
| 
								 | 
							
								     *            an alternative to specifying signer.issuer and
							 | 
						||
| 
								 | 
							
								     *            signer.serialNumber.
							 | 
						||
| 
								 | 
							
								     *          [issuer] the issuer attributes (eg: cert.issuer.attributes).
							 | 
						||
| 
								 | 
							
								     *          [serialNumber] the signer's certificate's serial number in
							 | 
						||
| 
								 | 
							
								     *           hexadecimal (eg: cert.serialNumber).
							 | 
						||
| 
								 | 
							
								     *          [digestAlgorithm] the message digest OID, as a string, to use
							 | 
						||
| 
								 | 
							
								     *            (eg: forge.pki.oids.sha1).
							 | 
						||
| 
								 | 
							
								     *          [authenticatedAttributes] an optional array of attributes
							 | 
						||
| 
								 | 
							
								     *            to also sign along with the content.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    addSigner: function(signer) {
							 | 
						||
| 
								 | 
							
								      var issuer = signer.issuer;
							 | 
						||
| 
								 | 
							
								      var serialNumber = signer.serialNumber;
							 | 
						||
| 
								 | 
							
								      if(signer.certificate) {
							 | 
						||
| 
								 | 
							
								        var cert = signer.certificate;
							 | 
						||
| 
								 | 
							
								        if(typeof cert === 'string') {
							 | 
						||
| 
								 | 
							
								          cert = forge.pki.certificateFromPem(cert);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        issuer = cert.issuer.attributes;
							 | 
						||
| 
								 | 
							
								        serialNumber = cert.serialNumber;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      var key = signer.key;
							 | 
						||
| 
								 | 
							
								      if(!key) {
							 | 
						||
| 
								 | 
							
								        throw new Error(
							 | 
						||
| 
								 | 
							
								          'Could not add PKCS#7 signer; no private key specified.');
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if(typeof key === 'string') {
							 | 
						||
| 
								 | 
							
								        key = forge.pki.privateKeyFromPem(key);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // ensure OID known for digest algorithm
							 | 
						||
| 
								 | 
							
								      var digestAlgorithm = signer.digestAlgorithm || forge.pki.oids.sha1;
							 | 
						||
| 
								 | 
							
								      switch(digestAlgorithm) {
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids.sha1:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids.sha256:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids.sha384:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids.sha512:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids.md5:
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								      default:
							 | 
						||
| 
								 | 
							
								        throw new Error(
							 | 
						||
| 
								 | 
							
								          'Could not add PKCS#7 signer; unknown message digest algorithm: ' +
							 | 
						||
| 
								 | 
							
								          digestAlgorithm);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // if authenticatedAttributes is present, then the attributes
							 | 
						||
| 
								 | 
							
								      // must contain at least PKCS #9 content-type and message-digest
							 | 
						||
| 
								 | 
							
								      var authenticatedAttributes = signer.authenticatedAttributes || [];
							 | 
						||
| 
								 | 
							
								      if(authenticatedAttributes.length > 0) {
							 | 
						||
| 
								 | 
							
								        var contentType = false;
							 | 
						||
| 
								 | 
							
								        var messageDigest = false;
							 | 
						||
| 
								 | 
							
								        for(var i = 0; i < authenticatedAttributes.length; ++i) {
							 | 
						||
| 
								 | 
							
								          var attr = authenticatedAttributes[i];
							 | 
						||
| 
								 | 
							
								          if(!contentType && attr.type === forge.pki.oids.contentType) {
							 | 
						||
| 
								 | 
							
								            contentType = true;
							 | 
						||
| 
								 | 
							
								            if(messageDigest) {
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          if(!messageDigest && attr.type === forge.pki.oids.messageDigest) {
							 | 
						||
| 
								 | 
							
								            messageDigest = true;
							 | 
						||
| 
								 | 
							
								            if(contentType) {
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            continue;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(!contentType || !messageDigest) {
							 | 
						||
| 
								 | 
							
								          throw new Error('Invalid signer.authenticatedAttributes. If ' +
							 | 
						||
| 
								 | 
							
								            'signer.authenticatedAttributes is specified, then it must ' +
							 | 
						||
| 
								 | 
							
								            'contain at least two attributes, PKCS #9 content-type and ' +
							 | 
						||
| 
								 | 
							
								            'PKCS #9 message-digest.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      msg.signers.push({
							 | 
						||
| 
								 | 
							
								        key: key,
							 | 
						||
| 
								 | 
							
								        version: 1,
							 | 
						||
| 
								 | 
							
								        issuer: issuer,
							 | 
						||
| 
								 | 
							
								        serialNumber: serialNumber,
							 | 
						||
| 
								 | 
							
								        digestAlgorithm: digestAlgorithm,
							 | 
						||
| 
								 | 
							
								        signatureAlgorithm: forge.pki.oids.rsaEncryption,
							 | 
						||
| 
								 | 
							
								        signature: null,
							 | 
						||
| 
								 | 
							
								        authenticatedAttributes: authenticatedAttributes,
							 | 
						||
| 
								 | 
							
								        unauthenticatedAttributes: []
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Signs the content.
							 | 
						||
| 
								 | 
							
								     * @param options Options to apply when signing:
							 | 
						||
| 
								 | 
							
								     *    [detached] boolean. If signing should be done in detached mode. Defaults to false.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    sign: function(options) {
							 | 
						||
| 
								 | 
							
								      options = options || {};
							 | 
						||
| 
								 | 
							
								      // auto-generate content info
							 | 
						||
| 
								 | 
							
								      if(typeof msg.content !== 'object' || msg.contentInfo === null) {
							 | 
						||
| 
								 | 
							
								        // use Data ContentInfo
							 | 
						||
| 
								 | 
							
								        msg.contentInfo = asn1.create(
							 | 
						||
| 
								 | 
							
								          asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								            // ContentType
							 | 
						||
| 
								 | 
							
								            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								              asn1.oidToDer(forge.pki.oids.data).getBytes())
							 | 
						||
| 
								 | 
							
								          ]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // add actual content, if present
							 | 
						||
| 
								 | 
							
								        if('content' in msg) {
							 | 
						||
| 
								 | 
							
								          var content;
							 | 
						||
| 
								 | 
							
								          if(msg.content instanceof forge.util.ByteBuffer) {
							 | 
						||
| 
								 | 
							
								            content = msg.content.bytes();
							 | 
						||
| 
								 | 
							
								          } else if(typeof msg.content === 'string') {
							 | 
						||
| 
								 | 
							
								            content = forge.util.encodeUtf8(msg.content);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          if (options.detached) {
							 | 
						||
| 
								 | 
							
								            msg.detachedContent = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, content);
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            msg.contentInfo.value.push(
							 | 
						||
| 
								 | 
							
								              // [0] EXPLICIT content
							 | 
						||
| 
								 | 
							
								              asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
							 | 
						||
| 
								 | 
							
								                asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
							 | 
						||
| 
								 | 
							
								                  content)
							 | 
						||
| 
								 | 
							
								              ]));
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // no signers, return early (degenerate case for certificate container)
							 | 
						||
| 
								 | 
							
								      if(msg.signers.length === 0) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // generate digest algorithm identifiers
							 | 
						||
| 
								 | 
							
								      var mds = addDigestAlgorithmIds();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // generate signerInfos
							 | 
						||
| 
								 | 
							
								      addSignerInfos(mds);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    verify: function() {
							 | 
						||
| 
								 | 
							
								      throw new Error('PKCS#7 signature verification not yet implemented.');
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Add a certificate.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param cert the certificate to add.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    addCertificate: function(cert) {
							 | 
						||
| 
								 | 
							
								      // convert from PEM
							 | 
						||
| 
								 | 
							
								      if(typeof cert === 'string') {
							 | 
						||
| 
								 | 
							
								        cert = forge.pki.certificateFromPem(cert);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      msg.certificates.push(cert);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Add a certificate revokation list.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param crl the certificate revokation list to add.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    addCertificateRevokationList: function(crl) {
							 | 
						||
| 
								 | 
							
								      throw new Error('PKCS#7 CRL support not yet implemented.');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								  return msg;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function addDigestAlgorithmIds() {
							 | 
						||
| 
								 | 
							
								    var mds = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for(var i = 0; i < msg.signers.length; ++i) {
							 | 
						||
| 
								 | 
							
								      var signer = msg.signers[i];
							 | 
						||
| 
								 | 
							
								      var oid = signer.digestAlgorithm;
							 | 
						||
| 
								 | 
							
								      if(!(oid in mds)) {
							 | 
						||
| 
								 | 
							
								        // content digest
							 | 
						||
| 
								 | 
							
								        mds[oid] = forge.md[forge.pki.oids[oid]].create();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if(signer.authenticatedAttributes.length === 0) {
							 | 
						||
| 
								 | 
							
								        // no custom attributes to digest; use content message digest
							 | 
						||
| 
								 | 
							
								        signer.md = mds[oid];
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        // custom attributes to be digested; use own message digest
							 | 
						||
| 
								 | 
							
								        // TODO: optimize to just copy message digest state if that
							 | 
						||
| 
								 | 
							
								        // feature is ever supported with message digests
							 | 
						||
| 
								 | 
							
								        signer.md = forge.md[forge.pki.oids[oid]].create();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // add unique digest algorithm identifiers
							 | 
						||
| 
								 | 
							
								    msg.digestAlgorithmIdentifiers = [];
							 | 
						||
| 
								 | 
							
								    for(var oid in mds) {
							 | 
						||
| 
								 | 
							
								      msg.digestAlgorithmIdentifiers.push(
							 | 
						||
| 
								 | 
							
								        // AlgorithmIdentifier
							 | 
						||
| 
								 | 
							
								        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								          // algorithm
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								            asn1.oidToDer(oid).getBytes()),
							 | 
						||
| 
								 | 
							
								          // parameters (null)
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
							 | 
						||
| 
								 | 
							
								        ]));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return mds;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function addSignerInfos(mds) {
							 | 
						||
| 
								 | 
							
								    var content;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (msg.detachedContent) {
							 | 
						||
| 
								 | 
							
								      // Signature has been made in detached mode.
							 | 
						||
| 
								 | 
							
								      content = msg.detachedContent;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      // Note: ContentInfo is a SEQUENCE with 2 values, second value is
							 | 
						||
| 
								 | 
							
								      // the content field and is optional for a ContentInfo but required here
							 | 
						||
| 
								 | 
							
								      // since signers are present
							 | 
						||
| 
								 | 
							
								      // get ContentInfo content
							 | 
						||
| 
								 | 
							
								      content = msg.contentInfo.value[1];
							 | 
						||
| 
								 | 
							
								      // skip [0] EXPLICIT content wrapper
							 | 
						||
| 
								 | 
							
								      content = content.value[0];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(!content) {
							 | 
						||
| 
								 | 
							
								      throw new Error(
							 | 
						||
| 
								 | 
							
								        'Could not sign PKCS#7 message; there is no content to sign.');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // get ContentInfo content type
							 | 
						||
| 
								 | 
							
								    var contentType = asn1.derToOid(msg.contentInfo.value[0].value);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // serialize content
							 | 
						||
| 
								 | 
							
								    var bytes = asn1.toDer(content);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // skip identifier and length per RFC 2315 9.3
							 | 
						||
| 
								 | 
							
								    // skip identifier (1 byte)
							 | 
						||
| 
								 | 
							
								    bytes.getByte();
							 | 
						||
| 
								 | 
							
								    // read and discard length bytes
							 | 
						||
| 
								 | 
							
								    asn1.getBerValueLength(bytes);
							 | 
						||
| 
								 | 
							
								    bytes = bytes.getBytes();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // digest content DER value bytes
							 | 
						||
| 
								 | 
							
								    for(var oid in mds) {
							 | 
						||
| 
								 | 
							
								      mds[oid].start().update(bytes);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // sign content
							 | 
						||
| 
								 | 
							
								    var signingTime = new Date();
							 | 
						||
| 
								 | 
							
								    for(var i = 0; i < msg.signers.length; ++i) {
							 | 
						||
| 
								 | 
							
								      var signer = msg.signers[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if(signer.authenticatedAttributes.length === 0) {
							 | 
						||
| 
								 | 
							
								        // if ContentInfo content type is not "Data", then
							 | 
						||
| 
								 | 
							
								        // authenticatedAttributes must be present per RFC 2315
							 | 
						||
| 
								 | 
							
								        if(contentType !== forge.pki.oids.data) {
							 | 
						||
| 
								 | 
							
								          throw new Error(
							 | 
						||
| 
								 | 
							
								            'Invalid signer; authenticatedAttributes must be present ' +
							 | 
						||
| 
								 | 
							
								            'when the ContentInfo content type is not PKCS#7 Data.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        // process authenticated attributes
							 | 
						||
| 
								 | 
							
								        // [0] IMPLICIT
							 | 
						||
| 
								 | 
							
								        signer.authenticatedAttributesAsn1 = asn1.create(
							 | 
						||
| 
								 | 
							
								          asn1.Class.CONTEXT_SPECIFIC, 0, true, []);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // per RFC 2315, attributes are to be digested using a SET container
							 | 
						||
| 
								 | 
							
								        // not the above [0] IMPLICIT container
							 | 
						||
| 
								 | 
							
								        var attrsAsn1 = asn1.create(
							 | 
						||
| 
								 | 
							
								          asn1.Class.UNIVERSAL, asn1.Type.SET, true, []);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for(var ai = 0; ai < signer.authenticatedAttributes.length; ++ai) {
							 | 
						||
| 
								 | 
							
								          var attr = signer.authenticatedAttributes[ai];
							 | 
						||
| 
								 | 
							
								          if(attr.type === forge.pki.oids.messageDigest) {
							 | 
						||
| 
								 | 
							
								            // use content message digest as value
							 | 
						||
| 
								 | 
							
								            attr.value = mds[signer.digestAlgorithm].digest();
							 | 
						||
| 
								 | 
							
								          } else if(attr.type === forge.pki.oids.signingTime) {
							 | 
						||
| 
								 | 
							
								            // auto-populate signing time if not already set
							 | 
						||
| 
								 | 
							
								            if(!attr.value) {
							 | 
						||
| 
								 | 
							
								              attr.value = signingTime;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          // convert to ASN.1 and push onto Attributes SET (for signing) and
							 | 
						||
| 
								 | 
							
								          // onto authenticatedAttributesAsn1 to complete SignedData ASN.1
							 | 
						||
| 
								 | 
							
								          // TODO: optimize away duplication
							 | 
						||
| 
								 | 
							
								          attrsAsn1.value.push(_attributeToAsn1(attr));
							 | 
						||
| 
								 | 
							
								          signer.authenticatedAttributesAsn1.value.push(_attributeToAsn1(attr));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // DER-serialize and digest SET OF attributes only
							 | 
						||
| 
								 | 
							
								        bytes = asn1.toDer(attrsAsn1).getBytes();
							 | 
						||
| 
								 | 
							
								        signer.md.start().update(bytes);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // sign digest
							 | 
						||
| 
								 | 
							
								      signer.signature = signer.key.sign(signer.md, 'RSASSA-PKCS1-V1_5');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // add signer info
							 | 
						||
| 
								 | 
							
								    msg.signerInfos = _signersToAsn1(msg.signers);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates an empty PKCS#7 message of type EncryptedData.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the message.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								p7.createEncryptedData = function() {
							 | 
						||
| 
								 | 
							
								  var msg = null;
							 | 
						||
| 
								 | 
							
								  msg = {
							 | 
						||
| 
								 | 
							
								    type: forge.pki.oids.encryptedData,
							 | 
						||
| 
								 | 
							
								    version: 0,
							 | 
						||
| 
								 | 
							
								    encryptedContent: {
							 | 
						||
| 
								 | 
							
								      algorithm: forge.pki.oids['aes256-CBC']
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Reads an EncryptedData content block (in ASN.1 format)
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param obj The ASN.1 representation of the EncryptedData content block
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    fromAsn1: function(obj) {
							 | 
						||
| 
								 | 
							
								      // Validate EncryptedData content block and capture data.
							 | 
						||
| 
								 | 
							
								      _fromAsn1(msg, obj, p7.asn1.encryptedDataValidator);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Decrypt encrypted content
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param key The (symmetric) key as a byte buffer
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    decrypt: function(key) {
							 | 
						||
| 
								 | 
							
								      if(key !== undefined) {
							 | 
						||
| 
								 | 
							
								        msg.encryptedContent.key = key;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      _decryptContent(msg);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								  return msg;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates an empty PKCS#7 message of type EnvelopedData.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the message.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								p7.createEnvelopedData = function() {
							 | 
						||
| 
								 | 
							
								  var msg = null;
							 | 
						||
| 
								 | 
							
								  msg = {
							 | 
						||
| 
								 | 
							
								    type: forge.pki.oids.envelopedData,
							 | 
						||
| 
								 | 
							
								    version: 0,
							 | 
						||
| 
								 | 
							
								    recipients: [],
							 | 
						||
| 
								 | 
							
								    encryptedContent: {
							 | 
						||
| 
								 | 
							
								      algorithm: forge.pki.oids['aes256-CBC']
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Reads an EnvelopedData content block (in ASN.1 format)
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param obj the ASN.1 representation of the EnvelopedData content block.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    fromAsn1: function(obj) {
							 | 
						||
| 
								 | 
							
								      // validate EnvelopedData content block and capture data
							 | 
						||
| 
								 | 
							
								      var capture = _fromAsn1(msg, obj, p7.asn1.envelopedDataValidator);
							 | 
						||
| 
								 | 
							
								      msg.recipients = _recipientsFromAsn1(capture.recipientInfos.value);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    toAsn1: function() {
							 | 
						||
| 
								 | 
							
								      // ContentInfo
							 | 
						||
| 
								 | 
							
								      return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								        // ContentType
							 | 
						||
| 
								 | 
							
								        asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								          asn1.oidToDer(msg.type).getBytes()),
							 | 
						||
| 
								 | 
							
								        // [0] EnvelopedData
							 | 
						||
| 
								 | 
							
								        asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
							 | 
						||
| 
								 | 
							
								          asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								            // Version
							 | 
						||
| 
								 | 
							
								            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
							 | 
						||
| 
								 | 
							
								              asn1.integerToDer(msg.version).getBytes()),
							 | 
						||
| 
								 | 
							
								            // RecipientInfos
							 | 
						||
| 
								 | 
							
								            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,
							 | 
						||
| 
								 | 
							
								              _recipientsToAsn1(msg.recipients)),
							 | 
						||
| 
								 | 
							
								            // EncryptedContentInfo
							 | 
						||
| 
								 | 
							
								            asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true,
							 | 
						||
| 
								 | 
							
								              _encryptedContentToAsn1(msg.encryptedContent))
							 | 
						||
| 
								 | 
							
								          ])
							 | 
						||
| 
								 | 
							
								        ])
							 | 
						||
| 
								 | 
							
								      ]);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Find recipient by X.509 certificate's issuer.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param cert the certificate with the issuer to look for.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return the recipient object.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    findRecipient: function(cert) {
							 | 
						||
| 
								 | 
							
								      var sAttr = cert.issuer.attributes;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for(var i = 0; i < msg.recipients.length; ++i) {
							 | 
						||
| 
								 | 
							
								        var r = msg.recipients[i];
							 | 
						||
| 
								 | 
							
								        var rAttr = r.issuer;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(r.serialNumber !== cert.serialNumber) {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(rAttr.length !== sAttr.length) {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var match = true;
							 | 
						||
| 
								 | 
							
								        for(var j = 0; j < sAttr.length; ++j) {
							 | 
						||
| 
								 | 
							
								          if(rAttr[j].type !== sAttr[j].type ||
							 | 
						||
| 
								 | 
							
								            rAttr[j].value !== sAttr[j].value) {
							 | 
						||
| 
								 | 
							
								            match = false;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(match) {
							 | 
						||
| 
								 | 
							
								          return r;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return null;
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Decrypt enveloped content
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param recipient The recipient object related to the private key
							 | 
						||
| 
								 | 
							
								     * @param privKey The (RSA) private key object
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    decrypt: function(recipient, privKey) {
							 | 
						||
| 
								 | 
							
								      if(msg.encryptedContent.key === undefined && recipient !== undefined &&
							 | 
						||
| 
								 | 
							
								        privKey !== undefined) {
							 | 
						||
| 
								 | 
							
								        switch(recipient.encryptedContent.algorithm) {
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids.rsaEncryption:
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids.desCBC:
							 | 
						||
| 
								 | 
							
								            var key = privKey.decrypt(recipient.encryptedContent.content);
							 | 
						||
| 
								 | 
							
								            msg.encryptedContent.key = forge.util.createBuffer(key);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          default:
							 | 
						||
| 
								 | 
							
								            throw new Error('Unsupported asymmetric cipher, ' +
							 | 
						||
| 
								 | 
							
								              'OID ' + recipient.encryptedContent.algorithm);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      _decryptContent(msg);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Add (another) entity to list of recipients.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param cert The certificate of the entity to add.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    addRecipient: function(cert) {
							 | 
						||
| 
								 | 
							
								      msg.recipients.push({
							 | 
						||
| 
								 | 
							
								        version: 0,
							 | 
						||
| 
								 | 
							
								        issuer: cert.issuer.attributes,
							 | 
						||
| 
								 | 
							
								        serialNumber: cert.serialNumber,
							 | 
						||
| 
								 | 
							
								        encryptedContent: {
							 | 
						||
| 
								 | 
							
								          // We simply assume rsaEncryption here, since forge.pki only
							 | 
						||
| 
								 | 
							
								          // supports RSA so far.  If the PKI module supports other
							 | 
						||
| 
								 | 
							
								          // ciphers one day, we need to modify this one as well.
							 | 
						||
| 
								 | 
							
								          algorithm: forge.pki.oids.rsaEncryption,
							 | 
						||
| 
								 | 
							
								          key: cert.publicKey
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Encrypt enveloped content.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * This function supports two optional arguments, cipher and key, which
							 | 
						||
| 
								 | 
							
								     * can be used to influence symmetric encryption.  Unless cipher is
							 | 
						||
| 
								 | 
							
								     * provided, the cipher specified in encryptedContent.algorithm is used
							 | 
						||
| 
								 | 
							
								     * (defaults to AES-256-CBC).  If no key is provided, encryptedContent.key
							 | 
						||
| 
								 | 
							
								     * is (re-)used.  If that one's not set, a random key will be generated
							 | 
						||
| 
								 | 
							
								     * automatically.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param [key] The key to be used for symmetric encryption.
							 | 
						||
| 
								 | 
							
								     * @param [cipher] The OID of the symmetric cipher to use.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    encrypt: function(key, cipher) {
							 | 
						||
| 
								 | 
							
								      // Part 1: Symmetric encryption
							 | 
						||
| 
								 | 
							
								      if(msg.encryptedContent.content === undefined) {
							 | 
						||
| 
								 | 
							
								        cipher = cipher || msg.encryptedContent.algorithm;
							 | 
						||
| 
								 | 
							
								        key = key || msg.encryptedContent.key;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var keyLen, ivLen, ciphFn;
							 | 
						||
| 
								 | 
							
								        switch(cipher) {
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids['aes128-CBC']:
							 | 
						||
| 
								 | 
							
								            keyLen = 16;
							 | 
						||
| 
								 | 
							
								            ivLen = 16;
							 | 
						||
| 
								 | 
							
								            ciphFn = forge.aes.createEncryptionCipher;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids['aes192-CBC']:
							 | 
						||
| 
								 | 
							
								            keyLen = 24;
							 | 
						||
| 
								 | 
							
								            ivLen = 16;
							 | 
						||
| 
								 | 
							
								            ciphFn = forge.aes.createEncryptionCipher;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids['aes256-CBC']:
							 | 
						||
| 
								 | 
							
								            keyLen = 32;
							 | 
						||
| 
								 | 
							
								            ivLen = 16;
							 | 
						||
| 
								 | 
							
								            ciphFn = forge.aes.createEncryptionCipher;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids['des-EDE3-CBC']:
							 | 
						||
| 
								 | 
							
								            keyLen = 24;
							 | 
						||
| 
								 | 
							
								            ivLen = 8;
							 | 
						||
| 
								 | 
							
								            ciphFn = forge.des.createEncryptionCipher;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          default:
							 | 
						||
| 
								 | 
							
								            throw new Error('Unsupported symmetric cipher, OID ' + cipher);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if(key === undefined) {
							 | 
						||
| 
								 | 
							
								          key = forge.util.createBuffer(forge.random.getBytes(keyLen));
							 | 
						||
| 
								 | 
							
								        } else if(key.length() != keyLen) {
							 | 
						||
| 
								 | 
							
								          throw new Error('Symmetric key has wrong length; ' +
							 | 
						||
| 
								 | 
							
								            'got ' + key.length() + ' bytes, expected ' + keyLen + '.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Keep a copy of the key & IV in the object, so the caller can
							 | 
						||
| 
								 | 
							
								        // use it for whatever reason.
							 | 
						||
| 
								 | 
							
								        msg.encryptedContent.algorithm = cipher;
							 | 
						||
| 
								 | 
							
								        msg.encryptedContent.key = key;
							 | 
						||
| 
								 | 
							
								        msg.encryptedContent.parameter = forge.util.createBuffer(
							 | 
						||
| 
								 | 
							
								          forge.random.getBytes(ivLen));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var ciph = ciphFn(key);
							 | 
						||
| 
								 | 
							
								        ciph.start(msg.encryptedContent.parameter.copy());
							 | 
						||
| 
								 | 
							
								        ciph.update(msg.content);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // The finish function does PKCS#7 padding by default, therefore
							 | 
						||
| 
								 | 
							
								        // no action required by us.
							 | 
						||
| 
								 | 
							
								        if(!ciph.finish()) {
							 | 
						||
| 
								 | 
							
								          throw new Error('Symmetric encryption failed.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        msg.encryptedContent.content = ciph.output;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Part 2: asymmetric encryption for each recipient
							 | 
						||
| 
								 | 
							
								      for(var i = 0; i < msg.recipients.length; ++i) {
							 | 
						||
| 
								 | 
							
								        var recipient = msg.recipients[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Nothing to do, encryption already done.
							 | 
						||
| 
								 | 
							
								        if(recipient.encryptedContent.content !== undefined) {
							 | 
						||
| 
								 | 
							
								          continue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        switch(recipient.encryptedContent.algorithm) {
							 | 
						||
| 
								 | 
							
								          case forge.pki.oids.rsaEncryption:
							 | 
						||
| 
								 | 
							
								            recipient.encryptedContent.content =
							 | 
						||
| 
								 | 
							
								              recipient.encryptedContent.key.encrypt(
							 | 
						||
| 
								 | 
							
								                msg.encryptedContent.key.data);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          default:
							 | 
						||
| 
								 | 
							
								            throw new Error('Unsupported asymmetric cipher, OID ' +
							 | 
						||
| 
								 | 
							
								              recipient.encryptedContent.algorithm);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								  return msg;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a single recipient from an ASN.1 object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param obj the ASN.1 RecipientInfo.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the recipient object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _recipientFromAsn1(obj) {
							 | 
						||
| 
								 | 
							
								  // validate EnvelopedData content block and capture data
							 | 
						||
| 
								 | 
							
								  var capture = {};
							 | 
						||
| 
								 | 
							
								  var errors = [];
							 | 
						||
| 
								 | 
							
								  if(!asn1.validate(obj, p7.asn1.recipientInfoValidator, capture, errors)) {
							 | 
						||
| 
								 | 
							
								    var error = new Error('Cannot read PKCS#7 RecipientInfo. ' +
							 | 
						||
| 
								 | 
							
								      'ASN.1 object is not an PKCS#7 RecipientInfo.');
							 | 
						||
| 
								 | 
							
								    error.errors = errors;
							 | 
						||
| 
								 | 
							
								    throw error;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    version: capture.version.charCodeAt(0),
							 | 
						||
| 
								 | 
							
								    issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
							 | 
						||
| 
								 | 
							
								    serialNumber: forge.util.createBuffer(capture.serial).toHex(),
							 | 
						||
| 
								 | 
							
								    encryptedContent: {
							 | 
						||
| 
								 | 
							
								      algorithm: asn1.derToOid(capture.encAlgorithm),
							 | 
						||
| 
								 | 
							
								      parameter: capture.encParameter ? capture.encParameter.value : undefined,
							 | 
						||
| 
								 | 
							
								      content: capture.encKey
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a single recipient object to an ASN.1 object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param obj the recipient object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the ASN.1 RecipientInfo.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _recipientToAsn1(obj) {
							 | 
						||
| 
								 | 
							
								  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								    // Version
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
							 | 
						||
| 
								 | 
							
								      asn1.integerToDer(obj.version).getBytes()),
							 | 
						||
| 
								 | 
							
								    // IssuerAndSerialNumber
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								      // Name
							 | 
						||
| 
								 | 
							
								      forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
							 | 
						||
| 
								 | 
							
								      // Serial
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
							 | 
						||
| 
								 | 
							
								        forge.util.hexToBytes(obj.serialNumber))
							 | 
						||
| 
								 | 
							
								    ]),
							 | 
						||
| 
								 | 
							
								    // KeyEncryptionAlgorithmIdentifier
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								      // Algorithm
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								        asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),
							 | 
						||
| 
								 | 
							
								      // Parameter, force NULL, only RSA supported for now.
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
							 | 
						||
| 
								 | 
							
								    ]),
							 | 
						||
| 
								 | 
							
								    // EncryptedKey
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
							 | 
						||
| 
								 | 
							
								      obj.encryptedContent.content)
							 | 
						||
| 
								 | 
							
								  ]);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Map a set of RecipientInfo ASN.1 objects to recipient objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param infos an array of ASN.1 representations RecipientInfo (i.e. SET OF).
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return an array of recipient objects.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _recipientsFromAsn1(infos) {
							 | 
						||
| 
								 | 
							
								  var ret = [];
							 | 
						||
| 
								 | 
							
								  for(var i = 0; i < infos.length; ++i) {
							 | 
						||
| 
								 | 
							
								    ret.push(_recipientFromAsn1(infos[i]));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return ret;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Map an array of recipient objects to ASN.1 RecipientInfo objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param recipients an array of recipientInfo objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return an array of ASN.1 RecipientInfos.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _recipientsToAsn1(recipients) {
							 | 
						||
| 
								 | 
							
								  var ret = [];
							 | 
						||
| 
								 | 
							
								  for(var i = 0; i < recipients.length; ++i) {
							 | 
						||
| 
								 | 
							
								    ret.push(_recipientToAsn1(recipients[i]));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return ret;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a single signer from an ASN.1 object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param obj the ASN.1 representation of a SignerInfo.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the signer object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _signerFromAsn1(obj) {
							 | 
						||
| 
								 | 
							
								  // validate EnvelopedData content block and capture data
							 | 
						||
| 
								 | 
							
								  var capture = {};
							 | 
						||
| 
								 | 
							
								  var errors = [];
							 | 
						||
| 
								 | 
							
								  if(!asn1.validate(obj, p7.asn1.signerInfoValidator, capture, errors)) {
							 | 
						||
| 
								 | 
							
								    var error = new Error('Cannot read PKCS#7 SignerInfo. ' +
							 | 
						||
| 
								 | 
							
								      'ASN.1 object is not an PKCS#7 SignerInfo.');
							 | 
						||
| 
								 | 
							
								    error.errors = errors;
							 | 
						||
| 
								 | 
							
								    throw error;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var rval = {
							 | 
						||
| 
								 | 
							
								    version: capture.version.charCodeAt(0),
							 | 
						||
| 
								 | 
							
								    issuer: forge.pki.RDNAttributesAsArray(capture.issuer),
							 | 
						||
| 
								 | 
							
								    serialNumber: forge.util.createBuffer(capture.serial).toHex(),
							 | 
						||
| 
								 | 
							
								    digestAlgorithm: asn1.derToOid(capture.digestAlgorithm),
							 | 
						||
| 
								 | 
							
								    signatureAlgorithm: asn1.derToOid(capture.signatureAlgorithm),
							 | 
						||
| 
								 | 
							
								    signature: capture.signature,
							 | 
						||
| 
								 | 
							
								    authenticatedAttributes: [],
							 | 
						||
| 
								 | 
							
								    unauthenticatedAttributes: []
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // TODO: convert attributes
							 | 
						||
| 
								 | 
							
								  var authenticatedAttributes = capture.authenticatedAttributes || [];
							 | 
						||
| 
								 | 
							
								  var unauthenticatedAttributes = capture.unauthenticatedAttributes || [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return rval;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Converts a single signerInfo object to an ASN.1 object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param obj the signerInfo object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the ASN.1 representation of a SignerInfo.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _signerToAsn1(obj) {
							 | 
						||
| 
								 | 
							
								  // SignerInfo
							 | 
						||
| 
								 | 
							
								  var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								    // version
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
							 | 
						||
| 
								 | 
							
								      asn1.integerToDer(obj.version).getBytes()),
							 | 
						||
| 
								 | 
							
								    // issuerAndSerialNumber
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								      // name
							 | 
						||
| 
								 | 
							
								      forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),
							 | 
						||
| 
								 | 
							
								      // serial
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
							 | 
						||
| 
								 | 
							
								        forge.util.hexToBytes(obj.serialNumber))
							 | 
						||
| 
								 | 
							
								    ]),
							 | 
						||
| 
								 | 
							
								    // digestAlgorithm
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								      // algorithm
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								        asn1.oidToDer(obj.digestAlgorithm).getBytes()),
							 | 
						||
| 
								 | 
							
								      // parameters (null)
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
							 | 
						||
| 
								 | 
							
								    ])
							 | 
						||
| 
								 | 
							
								  ]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // authenticatedAttributes (OPTIONAL)
							 | 
						||
| 
								 | 
							
								  if(obj.authenticatedAttributesAsn1) {
							 | 
						||
| 
								 | 
							
								    // add ASN.1 previously generated during signing
							 | 
						||
| 
								 | 
							
								    rval.value.push(obj.authenticatedAttributesAsn1);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // digestEncryptionAlgorithm
							 | 
						||
| 
								 | 
							
								  rval.value.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								    // algorithm
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								      asn1.oidToDer(obj.signatureAlgorithm).getBytes()),
							 | 
						||
| 
								 | 
							
								    // parameters (null)
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
							 | 
						||
| 
								 | 
							
								  ]));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // encryptedDigest
							 | 
						||
| 
								 | 
							
								  rval.value.push(asn1.create(
							 | 
						||
| 
								 | 
							
								    asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, obj.signature));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // unauthenticatedAttributes (OPTIONAL)
							 | 
						||
| 
								 | 
							
								  if(obj.unauthenticatedAttributes.length > 0) {
							 | 
						||
| 
								 | 
							
								    // [1] IMPLICIT
							 | 
						||
| 
								 | 
							
								    var attrsAsn1 = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, []);
							 | 
						||
| 
								 | 
							
								    for(var i = 0; i < obj.unauthenticatedAttributes.length; ++i) {
							 | 
						||
| 
								 | 
							
								      var attr = obj.unauthenticatedAttributes[i];
							 | 
						||
| 
								 | 
							
								      attrsAsn1.values.push(_attributeToAsn1(attr));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    rval.value.push(attrsAsn1);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return rval;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Map a set of SignerInfo ASN.1 objects to an array of signer objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param signerInfoAsn1s an array of ASN.1 SignerInfos (i.e. SET OF).
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return an array of signers objects.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _signersFromAsn1(signerInfoAsn1s) {
							 | 
						||
| 
								 | 
							
								  var ret = [];
							 | 
						||
| 
								 | 
							
								  for(var i = 0; i < signerInfoAsn1s.length; ++i) {
							 | 
						||
| 
								 | 
							
								    ret.push(_signerFromAsn1(signerInfoAsn1s[i]));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return ret;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Map an array of signer objects to ASN.1 objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param signers an array of signer objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return an array of ASN.1 SignerInfos.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _signersToAsn1(signers) {
							 | 
						||
| 
								 | 
							
								  var ret = [];
							 | 
						||
| 
								 | 
							
								  for(var i = 0; i < signers.length; ++i) {
							 | 
						||
| 
								 | 
							
								    ret.push(_signerToAsn1(signers[i]));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return ret;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Convert an attribute object to an ASN.1 Attribute.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param attr the attribute object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the ASN.1 Attribute.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _attributeToAsn1(attr) {
							 | 
						||
| 
								 | 
							
								  var value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // TODO: generalize to support more attributes
							 | 
						||
| 
								 | 
							
								  if(attr.type === forge.pki.oids.contentType) {
							 | 
						||
| 
								 | 
							
								    value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								      asn1.oidToDer(attr.value).getBytes());
							 | 
						||
| 
								 | 
							
								  } else if(attr.type === forge.pki.oids.messageDigest) {
							 | 
						||
| 
								 | 
							
								    value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
							 | 
						||
| 
								 | 
							
								      attr.value.bytes());
							 | 
						||
| 
								 | 
							
								  } else if(attr.type === forge.pki.oids.signingTime) {
							 | 
						||
| 
								 | 
							
								    /* Note per RFC 2985: Dates between 1 January 1950 and 31 December 2049
							 | 
						||
| 
								 | 
							
								      (inclusive) MUST be encoded as UTCTime. Any dates with year values
							 | 
						||
| 
								 | 
							
								      before 1950 or after 2049 MUST be encoded as GeneralizedTime. [Further,]
							 | 
						||
| 
								 | 
							
								      UTCTime values MUST be expressed in Greenwich Mean Time (Zulu) and MUST
							 | 
						||
| 
								 | 
							
								      include seconds (i.e., times are YYMMDDHHMMSSZ), even where the
							 | 
						||
| 
								 | 
							
								      number of seconds is zero.  Midnight (GMT) must be represented as
							 | 
						||
| 
								 | 
							
								      "YYMMDD000000Z". */
							 | 
						||
| 
								 | 
							
								    // TODO: make these module-level constants
							 | 
						||
| 
								 | 
							
								    var jan_1_1950 = new Date('1950-01-01T00:00:00Z');
							 | 
						||
| 
								 | 
							
								    var jan_1_2050 = new Date('2050-01-01T00:00:00Z');
							 | 
						||
| 
								 | 
							
								    var date = attr.value;
							 | 
						||
| 
								 | 
							
								    if(typeof date === 'string') {
							 | 
						||
| 
								 | 
							
								      // try to parse date
							 | 
						||
| 
								 | 
							
								      var timestamp = Date.parse(date);
							 | 
						||
| 
								 | 
							
								      if(!isNaN(timestamp)) {
							 | 
						||
| 
								 | 
							
								        date = new Date(timestamp);
							 | 
						||
| 
								 | 
							
								      } else if(date.length === 13) {
							 | 
						||
| 
								 | 
							
								        // YYMMDDHHMMSSZ (13 chars for UTCTime)
							 | 
						||
| 
								 | 
							
								        date = asn1.utcTimeToDate(date);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        // assume generalized time
							 | 
						||
| 
								 | 
							
								        date = asn1.generalizedTimeToDate(date);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(date >= jan_1_1950 && date < jan_1_2050) {
							 | 
						||
| 
								 | 
							
								      value = asn1.create(
							 | 
						||
| 
								 | 
							
								        asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,
							 | 
						||
| 
								 | 
							
								        asn1.dateToUtcTime(date));
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      value = asn1.create(
							 | 
						||
| 
								 | 
							
								        asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false,
							 | 
						||
| 
								 | 
							
								        asn1.dateToGeneralizedTime(date));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // TODO: expose as common API call
							 | 
						||
| 
								 | 
							
								  // create a RelativeDistinguishedName set
							 | 
						||
| 
								 | 
							
								  // each value in the set is an AttributeTypeAndValue first
							 | 
						||
| 
								 | 
							
								  // containing the type (an OID) and second the value
							 | 
						||
| 
								 | 
							
								  return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								    // AttributeType
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								      asn1.oidToDer(attr.type).getBytes()),
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [
							 | 
						||
| 
								 | 
							
								      // AttributeValue
							 | 
						||
| 
								 | 
							
								      value
							 | 
						||
| 
								 | 
							
								    ])
							 | 
						||
| 
								 | 
							
								  ]);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Map messages encrypted content to ASN.1 objects.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param ec The encryptedContent object of the message.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return ASN.1 representation of the encryptedContent object (SEQUENCE).
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _encryptedContentToAsn1(ec) {
							 | 
						||
| 
								 | 
							
								  return [
							 | 
						||
| 
								 | 
							
								    // ContentType, always Data for the moment
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								      asn1.oidToDer(forge.pki.oids.data).getBytes()),
							 | 
						||
| 
								 | 
							
								    // ContentEncryptionAlgorithmIdentifier
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
							 | 
						||
| 
								 | 
							
								      // Algorithm
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
							 | 
						||
| 
								 | 
							
								        asn1.oidToDer(ec.algorithm).getBytes()),
							 | 
						||
| 
								 | 
							
								      // Parameters (IV)
							 | 
						||
| 
								 | 
							
								      !ec.parameter ?
							 | 
						||
| 
								 | 
							
								        undefined :
							 | 
						||
| 
								 | 
							
								        asn1.create(
							 | 
						||
| 
								 | 
							
								          asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
							 | 
						||
| 
								 | 
							
								          ec.parameter.getBytes())
							 | 
						||
| 
								 | 
							
								    ]),
							 | 
						||
| 
								 | 
							
								    // [0] EncryptedContent
							 | 
						||
| 
								 | 
							
								    asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
							 | 
						||
| 
								 | 
							
								      asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
							 | 
						||
| 
								 | 
							
								        ec.content.getBytes())
							 | 
						||
| 
								 | 
							
								    ])
							 | 
						||
| 
								 | 
							
								  ];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Reads the "common part" of an PKCS#7 content block (in ASN.1 format)
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This function reads the "common part" of the PKCS#7 content blocks
							 | 
						||
| 
								 | 
							
								 * EncryptedData and EnvelopedData, i.e. version number and symmetrically
							 | 
						||
| 
								 | 
							
								 * encrypted content block.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The result of the ASN.1 validate and capture process is returned
							 | 
						||
| 
								 | 
							
								 * to allow the caller to extract further data, e.g. the list of recipients
							 | 
						||
| 
								 | 
							
								 * in case of a EnvelopedData object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param msg the PKCS#7 object to read the data to.
							 | 
						||
| 
								 | 
							
								 * @param obj the ASN.1 representation of the content block.
							 | 
						||
| 
								 | 
							
								 * @param validator the ASN.1 structure validator object to use.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the value map captured by validator object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _fromAsn1(msg, obj, validator) {
							 | 
						||
| 
								 | 
							
								  var capture = {};
							 | 
						||
| 
								 | 
							
								  var errors = [];
							 | 
						||
| 
								 | 
							
								  if(!asn1.validate(obj, validator, capture, errors)) {
							 | 
						||
| 
								 | 
							
								    var error = new Error('Cannot read PKCS#7 message. ' +
							 | 
						||
| 
								 | 
							
								      'ASN.1 object is not a supported PKCS#7 message.');
							 | 
						||
| 
								 | 
							
								    error.errors = error;
							 | 
						||
| 
								 | 
							
								    throw error;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Check contentType, so far we only support (raw) Data.
							 | 
						||
| 
								 | 
							
								  var contentType = asn1.derToOid(capture.contentType);
							 | 
						||
| 
								 | 
							
								  if(contentType !== forge.pki.oids.data) {
							 | 
						||
| 
								 | 
							
								    throw new Error('Unsupported PKCS#7 message. ' +
							 | 
						||
| 
								 | 
							
								      'Only wrapped ContentType Data supported.');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(capture.encryptedContent) {
							 | 
						||
| 
								 | 
							
								    var content = '';
							 | 
						||
| 
								 | 
							
								    if(forge.util.isArray(capture.encryptedContent)) {
							 | 
						||
| 
								 | 
							
								      for(var i = 0; i < capture.encryptedContent.length; ++i) {
							 | 
						||
| 
								 | 
							
								        if(capture.encryptedContent[i].type !== asn1.Type.OCTETSTRING) {
							 | 
						||
| 
								 | 
							
								          throw new Error('Malformed PKCS#7 message, expecting encrypted ' +
							 | 
						||
| 
								 | 
							
								            'content constructed of only OCTET STRING objects.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        content += capture.encryptedContent[i].value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      content = capture.encryptedContent;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    msg.encryptedContent = {
							 | 
						||
| 
								 | 
							
								      algorithm: asn1.derToOid(capture.encAlgorithm),
							 | 
						||
| 
								 | 
							
								      parameter: forge.util.createBuffer(capture.encParameter.value),
							 | 
						||
| 
								 | 
							
								      content: forge.util.createBuffer(content)
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(capture.content) {
							 | 
						||
| 
								 | 
							
								    var content = '';
							 | 
						||
| 
								 | 
							
								    if(forge.util.isArray(capture.content)) {
							 | 
						||
| 
								 | 
							
								      for(var i = 0; i < capture.content.length; ++i) {
							 | 
						||
| 
								 | 
							
								        if(capture.content[i].type !== asn1.Type.OCTETSTRING) {
							 | 
						||
| 
								 | 
							
								          throw new Error('Malformed PKCS#7 message, expecting ' +
							 | 
						||
| 
								 | 
							
								            'content constructed of only OCTET STRING objects.');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        content += capture.content[i].value;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      content = capture.content;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    msg.content = forge.util.createBuffer(content);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  msg.version = capture.version.charCodeAt(0);
							 | 
						||
| 
								 | 
							
								  msg.rawCapture = capture;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return capture;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Decrypt the symmetrically encrypted content block of the PKCS#7 message.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Decryption is skipped in case the PKCS#7 message object already has a
							 | 
						||
| 
								 | 
							
								 * (decrypted) content attribute.  The algorithm, key and cipher parameters
							 | 
						||
| 
								 | 
							
								 * (probably the iv) are taken from the encryptedContent attribute of the
							 | 
						||
| 
								 | 
							
								 * message object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param The PKCS#7 message object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _decryptContent(msg) {
							 | 
						||
| 
								 | 
							
								  if(msg.encryptedContent.key === undefined) {
							 | 
						||
| 
								 | 
							
								    throw new Error('Symmetric key not available.');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if(msg.content === undefined) {
							 | 
						||
| 
								 | 
							
								    var ciph;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    switch(msg.encryptedContent.algorithm) {
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids['aes128-CBC']:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids['aes192-CBC']:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids['aes256-CBC']:
							 | 
						||
| 
								 | 
							
								        ciph = forge.aes.createDecryptionCipher(msg.encryptedContent.key);
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids['desCBC']:
							 | 
						||
| 
								 | 
							
								      case forge.pki.oids['des-EDE3-CBC']:
							 | 
						||
| 
								 | 
							
								        ciph = forge.des.createDecryptionCipher(msg.encryptedContent.key);
							 | 
						||
| 
								 | 
							
								        break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      default:
							 | 
						||
| 
								 | 
							
								        throw new Error('Unsupported symmetric cipher, OID ' +
							 | 
						||
| 
								 | 
							
								          msg.encryptedContent.algorithm);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    ciph.start(msg.encryptedContent.parameter);
							 | 
						||
| 
								 | 
							
								    ciph.update(msg.encryptedContent.content);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if(!ciph.finish()) {
							 | 
						||
| 
								 | 
							
								      throw new Error('Symmetric decryption failed.');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    msg.content = ciph.output;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |