mirror of
https://github.com/moparisthebest/mail
synced 2024-08-13 16:43:47 -04:00
400 lines
11 KiB
JavaScript
400 lines
11 KiB
JavaScript
/**
|
|
* Javascript implementation of PKCS#7 v1.5. Currently only certain parts of
|
|
* PKCS#7 are implemented, especially the enveloped-data content type.
|
|
*
|
|
* @author Stefan Siegl
|
|
*
|
|
* Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
|
|
*
|
|
* The ASN.1 representation of PKCS#7 is as follows
|
|
* (see RFC #2315 for details, http://www.ietf.org/rfc/rfc2315.txt):
|
|
*
|
|
* A PKCS#7 message consists of a ContentInfo on root level, which may
|
|
* contain any number of further ContentInfo nested into it.
|
|
*
|
|
* ContentInfo ::= SEQUENCE {
|
|
* contentType ContentType,
|
|
* content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
|
|
* }
|
|
*
|
|
* ContentType ::= OBJECT IDENTIFIER
|
|
*
|
|
* EnvelopedData ::= SEQUENCE {
|
|
* version Version,
|
|
* recipientInfos RecipientInfos,
|
|
* encryptedContentInfo EncryptedContentInfo
|
|
* }
|
|
*
|
|
* EncryptedData ::= SEQUENCE {
|
|
* version Version,
|
|
* encryptedContentInfo EncryptedContentInfo
|
|
* }
|
|
*
|
|
* Version ::= INTEGER
|
|
*
|
|
* RecipientInfos ::= SET OF RecipientInfo
|
|
*
|
|
* EncryptedContentInfo ::= SEQUENCE {
|
|
* contentType ContentType,
|
|
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
|
|
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
|
|
* }
|
|
*
|
|
* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
|
*
|
|
* The AlgorithmIdentifier contains an Object Identifier (OID) and parameters
|
|
* for the algorithm, if any. In the case of AES and DES3, there is only one,
|
|
* the IV.
|
|
*
|
|
* AlgorithmIdentifer ::= SEQUENCE {
|
|
* algorithm OBJECT IDENTIFIER,
|
|
* parameters ANY DEFINED BY algorithm OPTIONAL
|
|
* }
|
|
*
|
|
* EncryptedContent ::= OCTET STRING
|
|
*
|
|
* RecipientInfo ::= SEQUENCE {
|
|
* version Version,
|
|
* issuerAndSerialNumber IssuerAndSerialNumber,
|
|
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
|
|
* encryptedKey EncryptedKey
|
|
* }
|
|
*
|
|
* IssuerAndSerialNumber ::= SEQUENCE {
|
|
* issuer Name,
|
|
* serialNumber CertificateSerialNumber
|
|
* }
|
|
*
|
|
* CertificateSerialNumber ::= INTEGER
|
|
*
|
|
* KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
|
*
|
|
* EncryptedKey ::= OCTET STRING
|
|
*/
|
|
(function() {
|
|
/* ########## Begin module implementation ########## */
|
|
function initModule(forge) {
|
|
|
|
// shortcut for ASN.1 API
|
|
var asn1 = forge.asn1;
|
|
|
|
// shortcut for PKCS#7 API
|
|
var p7v = forge.pkcs7asn1 = forge.pkcs7asn1 || {};
|
|
forge.pkcs7 = forge.pkcs7 || {};
|
|
forge.pkcs7.asn1 = p7v;
|
|
|
|
var contentInfoValidator = {
|
|
name: 'ContentInfo',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'ContentInfo.ContentType',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.OID,
|
|
constructed: false,
|
|
capture: 'contentType'
|
|
}, {
|
|
name: 'ContentInfo.content',
|
|
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
type: 0,
|
|
constructed: true,
|
|
optional: true,
|
|
captureAsn1: 'content'
|
|
}]
|
|
};
|
|
p7v.contentInfoValidator = contentInfoValidator;
|
|
|
|
var encryptedContentInfoValidator = {
|
|
name: 'EncryptedContentInfo',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'EncryptedContentInfo.contentType',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.OID,
|
|
constructed: false,
|
|
capture: 'contentType'
|
|
}, {
|
|
name: 'EncryptedContentInfo.contentEncryptionAlgorithm',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'EncryptedContentInfo.contentEncryptionAlgorithm.algorithm',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.OID,
|
|
constructed: false,
|
|
capture: 'encAlgorithm'
|
|
}, {
|
|
name: 'EncryptedContentInfo.contentEncryptionAlgorithm.parameter',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
captureAsn1: 'encParameter'
|
|
}]
|
|
}, {
|
|
name: 'EncryptedContentInfo.encryptedContent',
|
|
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
type: 0,
|
|
/* The PKCS#7 structure output by OpenSSL somewhat differs from what
|
|
* other implementations do generate.
|
|
*
|
|
* OpenSSL generates a structure like this:
|
|
* SEQUENCE {
|
|
* ...
|
|
* [0]
|
|
* 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
|
|
* C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
|
|
* ...
|
|
* }
|
|
*
|
|
* Whereas other implementations (and this PKCS#7 module) generate:
|
|
* SEQUENCE {
|
|
* ...
|
|
* [0] {
|
|
* OCTET STRING
|
|
* 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38
|
|
* C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45
|
|
* ...
|
|
* }
|
|
* }
|
|
*
|
|
* In order to support both, we just capture the context specific
|
|
* field here. The OCTET STRING bit is removed below.
|
|
*/
|
|
capture: 'encryptedContent'
|
|
}]
|
|
};
|
|
|
|
p7v.envelopedDataValidator = {
|
|
name: 'EnvelopedData',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'EnvelopedData.Version',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.INTEGER,
|
|
constructed: false,
|
|
capture: 'version'
|
|
}, {
|
|
name: 'EnvelopedData.RecipientInfos',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SET,
|
|
constructed: true,
|
|
captureAsn1: 'recipientInfos'
|
|
}].concat(encryptedContentInfoValidator)
|
|
};
|
|
|
|
p7v.encryptedDataValidator = {
|
|
name: 'EncryptedData',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'EncryptedData.Version',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.INTEGER,
|
|
constructed: false,
|
|
capture: 'version'
|
|
}].concat(encryptedContentInfoValidator)
|
|
};
|
|
|
|
var signerValidator = {
|
|
name: 'SignerInfo',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'SignerInfo.Version',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.INTEGER,
|
|
constructed: false
|
|
}, {
|
|
name: 'SignerInfo.IssuerAndSerialNumber',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true
|
|
}, {
|
|
name: 'SignerInfo.DigestAlgorithm',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true
|
|
}, {
|
|
name: 'SignerInfo.AuthenticatedAttributes',
|
|
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
type: 0,
|
|
constructed: true,
|
|
optional: true,
|
|
capture: 'authenticatedAttributes'
|
|
}, {
|
|
name: 'SignerInfo.DigestEncryptionAlgorithm',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true
|
|
}, {
|
|
name: 'SignerInfo.EncryptedDigest',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.OCTETSTRING,
|
|
constructed: false,
|
|
capture: 'signature'
|
|
}, {
|
|
name: 'SignerInfo.UnauthenticatedAttributes',
|
|
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
type: 1,
|
|
constructed: true,
|
|
optional: true
|
|
}]
|
|
};
|
|
|
|
p7v.signedDataValidator = {
|
|
name: 'SignedData',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'SignedData.Version',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.INTEGER,
|
|
constructed: false,
|
|
capture: 'version'
|
|
}, {
|
|
name: 'SignedData.DigestAlgorithms',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SET,
|
|
constructed: true,
|
|
captureAsn1: 'digestAlgorithms'
|
|
},
|
|
contentInfoValidator,
|
|
{
|
|
name: 'SignedData.Certificates',
|
|
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
type: 0,
|
|
optional: true,
|
|
captureAsn1: 'certificates'
|
|
}, {
|
|
name: 'SignedData.CertificateRevocationLists',
|
|
tagClass: asn1.Class.CONTEXT_SPECIFIC,
|
|
type: 1,
|
|
optional: true,
|
|
captureAsn1: 'crls'
|
|
}, {
|
|
name: 'SignedData.SignerInfos',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SET,
|
|
capture: 'signerInfos',
|
|
optional: true,
|
|
value: [signerValidator]
|
|
}]
|
|
};
|
|
|
|
p7v.recipientInfoValidator = {
|
|
name: 'RecipientInfo',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'RecipientInfo.version',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.INTEGER,
|
|
constructed: false,
|
|
capture: 'version'
|
|
}, {
|
|
name: 'RecipientInfo.issuerAndSerial',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'RecipientInfo.issuerAndSerial.issuer',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
captureAsn1: 'issuer'
|
|
}, {
|
|
name: 'RecipientInfo.issuerAndSerial.serialNumber',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.INTEGER,
|
|
constructed: false,
|
|
capture: 'serial'
|
|
}]
|
|
}, {
|
|
name: 'RecipientInfo.keyEncryptionAlgorithm',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.SEQUENCE,
|
|
constructed: true,
|
|
value: [{
|
|
name: 'RecipientInfo.keyEncryptionAlgorithm.algorithm',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.OID,
|
|
constructed: false,
|
|
capture: 'encAlgorithm'
|
|
}, {
|
|
name: 'RecipientInfo.keyEncryptionAlgorithm.parameter',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
constructed: false,
|
|
captureAsn1: 'encParameter'
|
|
}]
|
|
}, {
|
|
name: 'RecipientInfo.encryptedKey',
|
|
tagClass: asn1.Class.UNIVERSAL,
|
|
type: asn1.Type.OCTETSTRING,
|
|
constructed: false,
|
|
capture: 'encKey'
|
|
}]
|
|
};
|
|
|
|
} // end module implementation
|
|
|
|
/* ########## Begin module wrapper ########## */
|
|
var name = 'pkcs7asn1';
|
|
if(typeof define !== 'function') {
|
|
// NodeJS -> AMD
|
|
if(typeof module === 'object' && module.exports) {
|
|
var nodeJS = true;
|
|
define = function(ids, factory) {
|
|
factory(require, module);
|
|
};
|
|
}
|
|
// <script>
|
|
else {
|
|
if(typeof forge === 'undefined') {
|
|
forge = {};
|
|
}
|
|
return initModule(forge);
|
|
}
|
|
}
|
|
// AMD
|
|
var deps;
|
|
var defineFunc = function(require, module) {
|
|
module.exports = function(forge) {
|
|
var mods = deps.map(function(dep) {
|
|
return require(dep);
|
|
}).concat(initModule);
|
|
// handle circular dependencies
|
|
forge = forge || {};
|
|
forge.defined = forge.defined || {};
|
|
if(forge.defined[name]) {
|
|
return forge[name];
|
|
}
|
|
forge.defined[name] = true;
|
|
for(var i = 0; i < mods.length; ++i) {
|
|
mods[i](forge);
|
|
}
|
|
return forge[name];
|
|
};
|
|
};
|
|
var tmpDefine = define;
|
|
define = function(ids, factory) {
|
|
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
|
|
if(nodeJS) {
|
|
delete define;
|
|
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
}
|
|
define = tmpDefine;
|
|
return define.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
};
|
|
define(['require', 'module', './asn1', './util'], function() {
|
|
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
});
|
|
})();
|