diff --git a/lib/PayPro.js b/lib/PayPro.js index 493ad4b..d454710 100644 --- a/lib/PayPro.js +++ b/lib/PayPro.js @@ -145,270 +145,7 @@ PayPro.prototype.x509Verify = function() { // Handle Cert Extensions // http://tools.ietf.org/html/rfc5280#section-4.2 // - var ext; - var eid; - var extensions = { - authorityKeyIdentifier: null, - subjectKeyIdentifier: null, - keyUsage: null, - certificatePolicies: null, - policyMappings: null, - subjectAlternativeName: null, - issuerAlternativeName: null, - subjectDirectoryAttributes: null, - basicConstraints: null, - nameConstraints: null, - policyConstraints: null, - extendedKeyUsage: null, - CRLDistributionPoints: null, - inhibitAnyPolicy: null, - freshestCRL: null, - authorityInformationAccess: null, - subjectInformationAccess: null, - standardUnknown: [], - unknown: [], - }; - - for (var i = 0; i < c.tbsCertificate.extensions.length; i++) { - ext = c.tbsCertificate.extensions[i]; - eid = ext.extnID; - - // id-ce extensions - Standard Extensions - if (eid.length === 4 && eid[0] === 2 && eid[1] === 5 && eid[2] === 29) { - switch (eid[3]) { - // Authority Key Identifier - case 35: - print('Authority Key Identifier:'); - print(ext.extnValue); - extensions.authorityKeyIdentifier = ext.extnValue; - // parse - extensions.authorityKeyIdentifier = rfc5280.AuthorityKeyIdentifier.decode( - extensions.authorityKeyIdentifier, - 'der', { partial: false }); - print(extensions.authorityKeyIdentifier); - break; - // Subject Key Identifier - case 14: // VERY IMPORTANT, especially is cA (basic constraints) is true (it is) - print('Subject Key Identifier:'); - print(ext.extnValue); - extensions.subjectKeyIdentifier = ext.extnValue; - // parse - extensions.subjectKeyIdentifier = rfc5280.SubjectKeyIdentifier.decode( - extensions.subjectKeyIdentifier, - 'der', { partial: false }); - print(extensions.subjectKeyIdentifier); - break; - // Key Usage - case 15: - print('Key Usage:'); - print(ext.extnValue); - extensions.keyUsage = ext.extnValue; - // parse - data = rfc5280.KeyUsage.decode( - extensions.keyUsage, - 'der').data[0]; - extensions.keyUsage = { - digitalSignature: !!((data >> 0) & 1), - nonRepudiation: !!((data >> 1) & 1), - // nonRepudiation renamed to contentCommitment: - contentCommitment: !!((data >> 1) & 1), - keyEncipherment: !!((data >> 2) & 1), - dataEncipherment: !!((data >> 3) & 1), - keyAgreement: !!((data >> 4) & 1), - keyCertSign: !!((data >> 5) & 1), - cRLSign: !!((data >> 6) & 1), - encipherOnly: !!((data >> 7) & 1), - decipherOnly: !!((data >> 8) & 1) - }; - print(extensions.keyUsage); - break; - // Certificate Policies - case 32: - print('Certificate Policies:'); - print(ext.extnValue); - extensions.certificatePolicies = ext.extnValue; - // parse - extensions.certificatePolicies = rfc5280.CertificatePolicies.decode( - extensions.certificatePolicies, - 'der', { partial: false }); - print(extensions.certificatePolicies); - break; - // Policy Mappings - case 33: - print('Policy Mappings:'); - print(ext.extnValue); - extensions.policyMappings = ext.extnValue; - // parse - extensions.policyMappings = rfc5280.PolicyMappings.decode( - extensions.policyMappings, - 'der', { partial: false }); - print(extensions.policyMappings); - break; - // Subject Alternative Name - case 17: - print('Subject Alternative Name:'); - print(ext.extnValue); - extensions.subjectAlternativeName = ext.extnValue; - // parse - extensions.subjectAlternativeName = rfc5280.SubjectAlternativeName.decode( - extensions.subjectAlternativeName, - 'der', { partial: false }); - print(extensions.subjectAlternativeName); - break; - // Issuer Alternative Name - case 18: - print('Issuer Alternative Name:'); - print(ext.extnValue); - extensions.issuerAlternativeName = ext.extnValue; - // parse - extensions.issuerAlternativeName = rfc5280.IssuerAlternativeName.decode( - extensions.issuerAlternativeName, - 'der', { partial: false }); - print(extensions.issuerAlternativeName); - break; - // Subject Directory Attributes - case 9: - print('Subject Directory Attributes:'); - print(ext.extnValue); - extensions.subjectDirectoryAttributes = ext.extnValue; - // parse - extensions.subjectDirectoryAttributes = rfc5280.SubjectDirectoryAttributes.decode( - extensions.subjectDirectoryAttributes, - 'der', { partial: false }); - print(extensions.subjectDirectoryAttributes); - break; - // Basic Constraints - case 19: - print('Basic Constraints:'); - print(ext.extnValue); - extensions.basicConstraints = ext.extnValue; - // parse - extensions.basicConstraints = rfc5280.BasicConstraints.decode( - extensions.basicConstraints, - 'der', { partial: false }); - print(extensions.basicConstraints); - break; - // Name Constraints - case 30: - print('Name Constraints:'); - print(ext.extnValue); - extensions.nameConstraints = ext.extnValue; - // parse - extensions.nameConstraints = rfc5280.NameConstraints.decode( - extensions.nameConstraints, - 'der', { partial: false }); - print(extensions.nameConstraints); - break; - // Policy Constraints - case 36: - print('Policy Constraints:'); - print(ext.extnValue); - extensions.policyConstraints = ext.extnValue; - // parse - extensions.policyConstraints = rfc5280.PolicyConstraints.decode( - extensions.policyConstraints, - 'der', { partial: false }); - print(extensions.policyConstraints); - break; - // Extended Key Usage - case 37: - print('Extended Key Usage'); - print(ext.extnValue); - extensions.extendedKeyUsage = ext.extnValue; - // parse - extensions.extendedKeyUsage = rfc5280.ExtendedKeyUsage.decode( - extensions.extendedKeyUsage, - 'der', { partial: false }); - print(extensions.extendedKeyUsage); - break; - // CRL Distribution Points - case 31: - print('CRL Distribution Points:'); - print(ext.extnValue); - extensions.CRLDistributionPoints = ext.extnValue; - // parse - extensions.CRLDistributionPoints = rfc5280.CRLDistributionPoints.decode( - extensions.CRLDistributionPoints, - 'der', { partial: false }); - print(extensions.CRLDistributionPoints); - break; - // Inhibit anyPolicy - case 54: - print('Inhibit Any Policy:'); - print(ext.extnValue); - extensions.inhibitAnyPolicy = ext.extnValue; - // parse - extensions.inhibitAnyPolicy = rfc5280.InhibitAnyPolicy.decode( - extensions.inhibitAnyPolicy, - 'der', { partial: false }); - print(extensions.inhibitAnyPolicy); - break; - // Freshest CRL - case 46: - print('Freshest CRL:'); - print(ext.extnValue); - extensions.freshestCRL = ext.extnValue; - // parse - extensions.freshestCRL = rfc5280.FreshestCRL.decode( - extensions.freshestCRL, - 'der', { partial: false }); - print(extensions.freshestCRL); - break; - // Unknown Extension (not documented anywhere, probably non-standard) - default: - extensions.unknown.push(ext); - extensions.standardUnknown.push(ext); - break; - } - continue; - } - - // id-pe extensions - Private Internet Extensions - if (eid.length === 8 - && eid[0] === 1 - && eid[1] === 3 - && eid[2] === 6 - && eid[3] === 1 - && eid[4] === 5 - && eid[5] === 5 - && eid[6] === 7) { - switch (eid[3]) { - // Authority Information Access - // id-pe: - case 1: - print('Authority Information Access:'); - print(ext.extnValue); - extensions.authorityInformationAccess = ext.extnValue; - // parse - extensions.authorityInformationAccess = rfc5280.AuthorityInformationAccess.decode( - extensions.authorityInformationAccess, - 'der'); - print(extensions.freshestCRL); - break; - // Subject Information Access - // id-pe: - case 11: - print('Subject Information Access:'); - print(ext.extnValue); - extensions.subjectInformationAccess = ext.extnValue; - // parse - extensions.subjectInformationAccess = rfc5280.SubjectInformationAccess.decode( - extensions.subjectInformationAccess, - 'der'); - print(extensions.subjectInformationAccess); - break; - // Unknown Extension (not documented anywhere, probably non-standard) - default: - extensions.unknown.push(ext); - extensions.standardUnknown.push(ext); - break; - } - continue; - } - - extensions.unknown.push(ext); - } - + var extensions = rfc5280.parseExtensions(c.tbsCertificate, { partial: false }); var extensionsVerified = !extensions.unknown.filter(function(ext) { return ext.critical; }).length; @@ -1115,6 +852,151 @@ rfc5280.SubjectInformationAccess = asn1.define('SubjectInformationAccess', funct this.seqof(AccessDescription); }); +rfc5280.extensions = { + standard: { + // id-ce extensions - Standard Extensions + prefix: [2, 5, 29], + 35: 'Authority Key Identifier', + 14: 'Subject Key Identifier', + // VERY IMPORTANT, especially is cA (basic constraints) is true (it is) + 15: { + name: 'Key Usage', + parse: function(data, decoded, ext) { + data = data[0]; + return { + digitalSignature: !!((data >> 0) & 1), + nonRepudiation: !!((data >> 1) & 1), + // nonRepudiation renamed to contentCommitment: + contentCommitment: !!((data >> 1) & 1), + keyEncipherment: !!((data >> 2) & 1), + dataEncipherment: !!((data >> 3) & 1), + keyAgreement: !!((data >> 4) & 1), + keyCertSign: !!((data >> 5) & 1), + cRLSign: !!((data >> 6) & 1), + encipherOnly: !!((data >> 7) & 1), + decipherOnly: !!((data >> 8) & 1) + }; + } + }, + 32: 'Certificate Policies', + 33: 'Policy Mappings', + 17: 'Subject Alternative Name', + 18: 'Issuer Alternative Name', + 9: 'Subject Directory Attributes', + 19: 'Basic Constraints', + 30: 'Name Constraints', + 36: 'Policy Constraints', + 37: 'Extended Key Usage', + 31: 'CRL Distribution Points', + 54: 'Inhibit anyPolicy', + 46: 'Freshest CRL', + // Unknown Extension (not documented anywhere, probably non-standard) + _: 'Unknown Extension' + }, + // id-pe extensions - Private Internet Extensions + priv: { + prefix: [1, 3, 6, 1, 5, 5, 7], + 1: 'Authority Information Access', + 11: 'Subject Information Access', + // Unknown Extension (not documented anywhere, probably non-standard) + _: 'Unknown Extension' + } +}; + +Object.keys(rfc5280.extensions).forEach(function(typeName) { + var type = rfc5280.extensions[typeName]; + Object.keys(type).forEach(function(suffix) { + var id, prop, schemaName, schema, parse; + + if (suffix === 'prefix') + return; + + var prefix = type.prefix; + var name = type[suffix]; + + if (typeof name === 'object') { + var obj = name; + name = obj.name; + parse = obj.parse; + } + + id = prefix.concat(suffix).join('.'); + + if (/^[A-Z]+ /.test(name)) { + // CRL Distribution Points - > CRLDistributionPoints + prop = name.replace(/ /g, ''); + } else { + prop = (name[0].toLowerCase()) + name.substring(1).replace(/ /g, ''); + } + + schemaName = name.replace(/ /g, ''); + schema = rfc5280[schemaName]; + + rfc5280.extensions[id] = { + typeName: typeName, + prefix: prefix, + suffix: suffix, + id: id, + name: name, + prop: prop, + schemaName: schemaName, + schema: schema, + parse: parse + }; + }); +}); + +rfc5280.parseExtensions = function(tbsCertificate, options) { + var edata, eid, ext, decoded, data; + + var output = {}; + var unknown = 0; + + for (var i = 0; i < tbsCertificate.extensions.length; i++) { + edata = tbsCertificate.extensions[i]; + eid = edata.extnID.join('.'); + + if (ext = rfc5280.extensions[eid]) { + // Parse Extension + decoded = ext.schema.decode(edata.extnValue, 'der', options); + + // If the Extension needs extra parsing (i.e. bitstrs) + data = ext.parse + ? ext.parse(decoded.data, decoded, ext) + : decoded; + + // Tack on some useful info + + // Comment for debugging: + // data._edata = edata; + // data._ext = ext; + + if (ext.parse) { + data._decoded = decoded; + } + + // Add our decoded extension to the output + output[ext.prop] = data; + + // XXX Debug + print('------------'); + print('%s (%s):', ext.name, ext.id); + print('Buffer:'); + print(edata.extnValue); + print('Extension:'); + print(data); + } else { + // Add unknown extension: + output['Unkown_' + unknown] = edata; + + // XXX Debug + print('Unknown extension: %s', eid); + } + } + + return output; +}; + /** * Debug */