diff --git a/lib/PayPro.js b/lib/PayPro.js index d454710..c0a52a2 100644 --- a/lib/PayPro.js +++ b/lib/PayPro.js @@ -145,11 +145,89 @@ PayPro.prototype.x509Verify = function() { // Handle Cert Extensions // http://tools.ietf.org/html/rfc5280#section-4.2 // - var extensions = rfc5280.parseExtensions(c.tbsCertificate, { partial: false }); + var extensions = rfc5280.decodeExtensions(c, { partial: false }); var extensionsVerified = !extensions.unknown.filter(function(ext) { return ext.critical; }).length; + // Object.keys(extensions).forEach(function(key) { + // if (extensions[key].execute) { + // c = extensions[key].execute(c); + // } + // }); + + if (extensions.subjectDirectoryAttributes) { + ; + } + + if (extensions.subjectKeyIdentifier) { + ; + } + + if (extensions.keyUsage) { + ; + } + + if (extensions.subjectAlternativeName) { + ; + } + + if (extensions.issuerAlternativeName) { + ; + } + + if (extensions.basicConstraints) { + ; + } + + if (extensions.nameConstraints) { + ; + } + + if (extensions.CRLDistributionPoints) { + ; + } + + if (extensions.certificatePolicies) { + ; + } + + if (extensions.policyMappings) { + ; + } + + if (extensions.authorityKeyIdentifier) { + ; + } + + if (extensions.policyConstraints) { + ; + } + + if (extensions.extendedKeyUsage) { + ; + } + + if (extensions.freshestCRL) { + ; + } + + if (extensions.inhibitanyPolicy) { + ; + } + + if (extensions.unknownExtension) { + ; + } + + if (extensions.authorityInformationAccess) { + ; + } + + if (extensions.subjectInformationAccess) { + ; + } + // // Verify current certificate signature // @@ -861,8 +939,8 @@ rfc5280.extensions = { // VERY IMPORTANT, especially is cA (basic constraints) is true (it is) 15: { name: 'Key Usage', - parse: function(data, decoded, ext) { - data = data[0]; + parse: function(decoded, cert, ext, edata) { + var data = decoded.data[0]; return { digitalSignature: !!((data >> 0) & 1), nonRepudiation: !!((data >> 1) & 1), @@ -876,6 +954,9 @@ rfc5280.extensions = { encipherOnly: !!((data >> 7) & 1), decipherOnly: !!((data >> 8) & 1) }; + }, + execute: function(cert) { + return cert; } }, 32: 'Certificate Policies', @@ -887,7 +968,35 @@ rfc5280.extensions = { 30: 'Name Constraints', 36: 'Policy Constraints', 37: 'Extended Key Usage', - 31: 'CRL Distribution Points', + 31: { + name: 'CRL Distribution Points', + parse: function(decoded, cert, ext, edata) { + // XXX Find the bitstr: ReasonFlags + console.log('###########################'); + console.log(decoded); + console.log(cert); + console.log(ext); + console.log(edata); + console.log('###########################'); + // XXX Find the bitstr: ReasonFlags + // var data = CRLDistributionPoints.DistributionPoint.reasons; + // return { + // unused: !!((data >> 0) & 1), + // keyCompromise: !!((data >> 1) & 1), + // cACompromise: !!((data >> 2) & 1), + // affiliationChanged: !!((data >> 3) & 1), + // superseded: !!((data >> 4) & 1), + // cessationOfOperation: !!((data >> 5) & 1), + // certificateHold: !!((data >> 6) & 1), + // privilegeWithdrawn: !!((data >> 7) & 1), + // aACompromise: !!((data >> 8) & 1) + // }; + return decoded; + }, + execute: function(cert) { + return cert; + } + }, 54: 'Inhibit anyPolicy', 46: 'Freshest CRL', // Unknown Extension (not documented anywhere, probably non-standard) @@ -906,7 +1015,7 @@ rfc5280.extensions = { Object.keys(rfc5280.extensions).forEach(function(typeName) { var type = rfc5280.extensions[typeName]; Object.keys(type).forEach(function(suffix) { - var id, prop, schemaName, schema, parse; + var id, prop, schemaName, schema, parse, execute; if (suffix === 'prefix') return; @@ -918,6 +1027,7 @@ Object.keys(rfc5280.extensions).forEach(function(typeName) { var obj = name; name = obj.name; parse = obj.parse; + execute = obj.execute; } id = prefix.concat(suffix).join('.'); @@ -941,16 +1051,24 @@ Object.keys(rfc5280.extensions).forEach(function(typeName) { prop: prop, schemaName: schemaName, schema: schema, - parse: parse + parse: parse, + execute: execute }; }); }); -rfc5280.parseExtensions = function(tbsCertificate, options) { - var edata, eid, ext, decoded, data; +rfc5280.decodeExtensions = function(cert, options) { + var tbsCertificate = cert.tbsCertificate; + + if (!tbsCertificate) { + tbsCertificate = cert; + cert = null; + } + + var edata, eid, ext, decoded, errors, data; var output = {}; - var unknown = 0; + output.unknown = []; for (var i = 0; i < tbsCertificate.extensions.length; i++) { edata = tbsCertificate.extensions[i]; @@ -960,19 +1078,42 @@ rfc5280.parseExtensions = function(tbsCertificate, options) { // Parse Extension decoded = ext.schema.decode(edata.extnValue, 'der', options); + if (options.partial && decoded.result) { + errors = decoded.errors; + decoded = decoded.result; + if (Array.isArray(decoded)) { + decoded = decoded.map(function(decoded) { + decoded.errors.forEach(function(error) { + errors.push(error); + }); + return decoded.result; + }); + } + } + // If the Extension needs extra parsing (i.e. bitstrs) data = ext.parse - ? ext.parse(decoded.data, decoded, ext) + ? ext.parse(decoded, cert, ext, edata) : decoded; // Tack on some useful info // Comment for debugging: - // data._edata = edata; - // data._ext = ext; - + // data.edata = edata; + // data.ext = ext; + data.errors = errors; if (ext.parse) { - data._decoded = decoded; + data.decoded = decoded; + } + + // Execute Behavior for Cert + if (ext.execute) { + data.execute = ext.execute; + } + + // Add errors for partial: true + if (options.partial && errors) { + data.errors = errors; } // Add our decoded extension to the output @@ -987,7 +1128,8 @@ rfc5280.parseExtensions = function(tbsCertificate, options) { print(data); } else { // Add unknown extension: - output['Unkown_' + unknown] = edata; + output.unknown.push(edata); + output['unkown_' + (output.unknown.length - 1)] = edata; // XXX Debug print('Unknown extension: %s', eid);