Skip to content

Commit

Permalink
formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
mreinstein committed May 4, 2018
1 parent d87a399 commit f77b68c
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 87 deletions.
4 changes: 2 additions & 2 deletions fetch-cert.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var https = require('https')

var globalCache = {} // default in-memory cache for downloaded certificates

module.exports = function fetchCert(options, callback) {
module.exports = function fetchCert (options, callback) {
var url = options.url
var cache = options.cache || globalCache
var cachedResponse = cache[url.href]
Expand All @@ -18,7 +18,7 @@ module.exports = function fetchCert(options, callback) {

var body = ''

https.get(url.href, function(response) {
https.get(url.href, function (response) {
var statusCode

if (!response || 200 !== response.statusCode) {
Expand Down
53 changes: 23 additions & 30 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,27 @@ var validator = require('validator')
var TIMESTAMP_TOLERANCE = 150
var SIGNATURE_FORMAT = 'base64'

function getCert(cert_url, callback) {
function getCert (cert_url, callback) {
var options = { url: url.parse(cert_url) }
var result = validateCertUri(options.url)
if (result !== true) {
if (result !== true)
return process.nextTick(callback, result)
}

fetchCert(options, function(er, pem_cert) {
if (er) {
fetchCert(options, function (er, pem_cert) {
if (er)
return callback(er)
}

er = validateCert(pem_cert)
if (er) {
if (er)
return callback(er)
}

callback(er, pem_cert)
})
}


// returns true if the signature for the request body is valid, false otherwise
function isValidSignature(pem_cert, signature, requestBody) {
function isValidSignature (pem_cert, signature, requestBody) {
var verifier = crypto.createVerify('RSA-SHA1')
verifier.update(requestBody, 'utf8')
return verifier.verify(pem_cert, signature, SIGNATURE_FORMAT)
Expand All @@ -45,57 +42,53 @@ function isValidSignature(pem_cert, signature, requestBody) {
// determine if a timestamp is valid for a given request with a tolerance of
// TIMESTAMP_TOLERANCE seconds
// returns undefined if valid, or an error string otherwise
function validateTimestamp(requestBody) {
function validateTimestamp (requestBody) {
var d, e, error, now, oldestTime, request_json
try {
request_json = JSON.parse(requestBody)
} catch (error) {
e = error
return 'request body invalid json'
}
if (!(request_json.request && request_json.request.timestamp)) {
if (!(request_json.request && request_json.request.timestamp))
return 'Timestamp field not present in request'
}

d = new Date(request_json.request.timestamp)
now = new Date()
oldestTime = now.getTime() - (TIMESTAMP_TOLERANCE * 1000)
if (d.getTime() < oldestTime) {
if (d.getTime() < oldestTime)
return 'Request is from more than ' + TIMESTAMP_TOLERANCE + ' seconds ago'
}
}


// certificate validator express middleware for amazon echo
module.exports = function verifier(cert_url, signature, requestBody, callback) {
module.exports = function verifier (cert_url, signature, requestBody, callback) {
var er

if(!cert_url) {
if(!cert_url)
return process.nextTick(callback, 'missing certificate url')
}

if (!signature) {
if (!signature)
return process.nextTick(callback, 'missing signature')
}
if (!requestBody) {

if (!requestBody)
return process.nextTick(callback, 'missing request (certificate) body')
}

if (!validator.isBase64(signature)) {
if (!validator.isBase64(signature))
return process.nextTick(callback, 'invalid signature (not base64 encoded)')
}

er = validateTimestamp(requestBody)

if (er) {
if (er)
return process.nextTick(callback, er)
}

getCert(cert_url, function(er, pem_cert) {
if (er) {
getCert(cert_url, function (er, pem_cert) {
if (er)
return callback(er)
}
if (!isValidSignature(pem_cert, signature, requestBody)) {

if (!isValidSignature(pem_cert, signature, requestBody))
return callback('invalid signature')
}

callback()
})
}
65 changes: 33 additions & 32 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var sinon = require('sinon')

var cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-5.pem' // latest valid cert

test('handle missing cert_url parameter', function(t) {
test('handle missing cert_url parameter', function (t) {
var body, now, signature
signature = 'JbWZ4iO5ogpq1NhsOqyqq/QRrvc1/XyDwjcBO9wWSk//c11+gImmtWzMG9tDEW40t0Xwt1cnGU93DwUZQzMyzJ5CMi+09qVQUSIHiSmPekKaQRxS0Ibu7l7cXXuCcOBupbkheD/Dsd897Bm5SQwd1cFKRv+PJlpmGKimgh2QmbivogsEkFl8b9SW48kjKWazwj/XP2SrHY0bTvwMTVu7zvTcp0ZenEGlY2DNr5zSd1n6lmS6rgAt1IPwhBzqI0PVMngaM0DQhB0wUPj3QoIUh0IyMVAQzRFbQpS4UGrA4M9a5a+AGy0jCQKiRCI+Yi9iZYEVYvfafF/lyOUHHYcpOg=='
now = new Date()
Expand All @@ -17,14 +17,14 @@ test('handle missing cert_url parameter', function(t) {
timestamp: now.getTime()
}
}
verifier(undefined, signature, JSON.stringify(body), function(er) {
verifier(undefined, signature, JSON.stringify(body), function (er) {
t.equal(er, 'missing certificate url')
t.end()
})
})


test('handle invalid cert_url parameter', function(t) {
test('handle invalid cert_url parameter', function (t) {
var body, now, signature
signature = 'JbWZ4iO5ogpq1NhsOqyqq/QRrvc1/XyDwjcBO9wWSk//c11+gImmtWzMG9tDEW40t0Xwt1cnGU93DwUZQzMyzJ5CMi+09qVQUSIHiSmPekKaQRxS0Ibu7l7cXXuCcOBupbkheD/Dsd897Bm5SQwd1cFKRv+PJlpmGKimgh2QmbivogsEkFl8b9SW48kjKWazwj/XP2SrHY0bTvwMTVu7zvTcp0ZenEGlY2DNr5zSd1n6lmS6rgAt1IPwhBzqI0PVMngaM0DQhB0wUPj3QoIUh0IyMVAQzRFbQpS4UGrA4M9a5a+AGy0jCQKiRCI+Yi9iZYEVYvfafF/lyOUHHYcpOg=='
now = new Date()
Expand All @@ -33,14 +33,14 @@ test('handle invalid cert_url parameter', function(t) {
timestamp: now.getTime()
}
}
verifier('http://someinsecureurl', signature, JSON.stringify(body), function(er) {
verifier('http://someinsecureurl', signature, JSON.stringify(body), function (er) {
t.equal(er.indexOf('Certificate URI MUST be https'), 0)
t.end()
})
})


test('handle invalid body json', function(t) {
test('handle invalid body json', function (t) {
var signature = 'JbWZ4iO5ogpq1NhsOqyqq/QRrvc1/XyDwjcBO9wWSk//c11+gImmtWzMG9tDEW40t0Xwt1cnGU93DwUZQzMyzJ5CMi+09qVQUSIHiSmPekKaQRxS0Ibu7l7cXXuCcOBupbkheD/Dsd897Bm5SQwd1cFKRv+PJlpmGKimgh2QmbivogsEkFl8b9SW48kjKWazwj/XP2SrHY0bTvwMTVu7zvTcp0ZenEGlY2DNr5zSd1n6lmS6rgAt1IPwhBzqI0PVMngaM0DQhB0wUPj3QoIUh0IyMVAQzRFbQpS4UGrA4M9a5a+AGy0jCQKiRCI+Yi9iZYEVYvfafF/lyOUHHYcpOg=='
verifier(cert_url, signature, '', function(er) {
t.equal(er, 'missing request (certificate) body')
Expand All @@ -49,7 +49,7 @@ test('handle invalid body json', function(t) {
})


test('handle missing timestamp field', function(t) {
test('handle missing timestamp field', function (t) {
var signature = 'JbWZ4iO5ogpq1NhsOqyqq/QRrvc1/XyDwjcBO9wWSk//c11+gImmtWzMG9tDEW40t0Xwt1cnGU93DwUZQzMyzJ5CMi+09qVQUSIHiSmPekKaQRxS0Ibu7l7cXXuCcOBupbkheD/Dsd897Bm5SQwd1cFKRv+PJlpmGKimgh2QmbivogsEkFl8b9SW48kjKWazwj/XP2SrHY0bTvwMTVu7zvTcp0ZenEGlY2DNr5zSd1n6lmS6rgAt1IPwhBzqI0PVMngaM0DQhB0wUPj3QoIUh0IyMVAQzRFbQpS4UGrA4M9a5a+AGy0jCQKiRCI+Yi9iZYEVYvfafF/lyOUHHYcpOg=='
verifier(cert_url, signature, '{}', function(er) {
t.equal(er, 'Timestamp field not present in request')
Expand All @@ -58,7 +58,7 @@ test('handle missing timestamp field', function(t) {
})


test('handle outdated timestamp field', function(t) {
test('handle outdated timestamp field', function (t) {
var body, now, signature
signature = 'JbWZ4iO5ogpq1NhsOqyqq/QRrvc1/XyDwjcBO9wWSk//c11+gImmtWzMG9tDEW40t0Xwt1cnGU93DwUZQzMyzJ5CMi+09qVQUSIHiSmPekKaQRxS0Ibu7l7cXXuCcOBupbkheD/Dsd897Bm5SQwd1cFKRv+PJlpmGKimgh2QmbivogsEkFl8b9SW48kjKWazwj/XP2SrHY0bTvwMTVu7zvTcp0ZenEGlY2DNr5zSd1n6lmS6rgAt1IPwhBzqI0PVMngaM0DQhB0wUPj3QoIUh0IyMVAQzRFbQpS4UGrA4M9a5a+AGy0jCQKiRCI+Yi9iZYEVYvfafF/lyOUHHYcpOg=='
now = new Date()
Expand All @@ -74,15 +74,15 @@ test('handle outdated timestamp field', function(t) {
})


test('handle missing signature parameter', function(t) {
test('handle missing signature parameter', function (t) {
var body, now
now = new Date()
body = {
request: {
timestamp: now.getTime()
}
}
verifier(cert_url, undefined, JSON.stringify(body), function(er) {
verifier(cert_url, undefined, JSON.stringify(body), function (er) {
t.equal(er, 'missing signature')
t.end()
})
Expand All @@ -97,32 +97,32 @@ test('handle invalid signature parameter', function(t) {
timestamp: now.getTime()
}
}
verifier(cert_url, '....$#%@$se', JSON.stringify(body), function(er) {
verifier(cert_url, '....$#%@$se', JSON.stringify(body), function (er) {
t.equal(er, 'invalid signature (not base64 encoded)')
t.end()
})
})

test('handle invalid base64-encoded signature parameter', function(t) {
test('handle invalid base64-encoded signature parameter', function (t) {
var body, now
now = new Date()
body = {
request: {
timestamp: now.getTime()
}
}
verifier(cert_url, 'aGVsbG8NCg==', JSON.stringify(body), function(er) {
verifier(cert_url, 'aGVsbG8NCg==', JSON.stringify(body), function (er) {
t.equal(er, 'invalid signature')
t.end()
})
})

test('handle valid signature', function(t) {
var ts = '2017-02-10T07:27:59Z';
var now = new Date(ts);
var clock = sinon.useFakeTimers(now.getTime());
test('handle valid signature', function (t) {
var ts = '2017-02-10T07:27:59Z'
var now = new Date(ts)
var clock = sinon.useFakeTimers(now.getTime())
var cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem'
var signature = 'Qc8OuaGEHWeL/39XTEDYFbOCufYWpwi45rqmM2R4WaSEYcSXq+hUko/88wv48+6SPUiEddWSEEINJFAFV5auYZsnBzqCK+SO8mGNOGHmLYpcFuSEHI3eA3nDIEARrXTivqqbH/LCPJHc0tqNYr3yPZRIR2mYFndJOxgDNSOooZX+tp2GafHHsjjShCjmePaLxJiGG1DmrL6fyOJoLrzc0olUxLmnJviS6Q5wBir899TMEZ/zX+aiBTt/khVvwIh+hI/PZsRq/pQw4WAvQz1bcnGNamvMA/TKSJtR0elJP+TgCqbVoYisDgQXkhi8/wonkLhs68pN+TurbR7GyC1vxw==';
var signature = 'Qc8OuaGEHWeL/39XTEDYFbOCufYWpwi45rqmM2R4WaSEYcSXq+hUko/88wv48+6SPUiEddWSEEINJFAFV5auYZsnBzqCK+SO8mGNOGHmLYpcFuSEHI3eA3nDIEARrXTivqqbH/LCPJHc0tqNYr3yPZRIR2mYFndJOxgDNSOooZX+tp2GafHHsjjShCjmePaLxJiGG1DmrL6fyOJoLrzc0olUxLmnJviS6Q5wBir899TMEZ/zX+aiBTt/khVvwIh+hI/PZsRq/pQw4WAvQz1bcnGNamvMA/TKSJtR0elJP+TgCqbVoYisDgQXkhi8/wonkLhs68pN+TurbR7GyC1vxw=='
var body = {
"version": "1.0",
"session": {
Expand All @@ -146,19 +146,20 @@ test('handle valid signature', function(t) {
},
"inDialog": false
}
};
verifier(cert_url, signature, JSON.stringify(body), function(er) {
t.equal(er, undefined);
clock.restore();
}

verifier(cert_url, signature, JSON.stringify(body), function (er) {
t.equal(er, undefined)
clock.restore()
t.end()
});
})
})
test('handle valid signature with double byte utf8 encodings', function(t) {
var ts = '2017-04-05T12:02:36Z';
var now = new Date(ts);
var clock = sinon.useFakeTimers(now.getTime());
test('handle valid signature with double byte utf8 encodings', function (t) {
var ts = '2017-04-05T12:02:36Z'
var now = new Date(ts)
var clock = sinon.useFakeTimers(now.getTime())
var cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem'
var signature = 'WLShxe8KMwHUt8hVD5+iE4tDO+J8Li21yocDWnq8LVRpE2PMMWCxjQzOCzyoFm4i/yW07UKtKQxcnzB44ZEdP6e6HelwBwEdP4lb8jQcc5knk8SuUth4N7cu6Em8FPOdOJdd9idHbO/p8BTb14wgua5n+1SDKHm+wPikOVsfCMYsXcwRWx5FsgP1wVPrDsCHN/ISiCXz+UuMnd6H0uRNdLZ/x/ikPkknh+P1kuFa2a2LN4r57IwBDAxkdf9MzXEexSOO0nWLnyJY2VAFB+O7JKE39CwMJ1+YDOwTTTLjilkCnSlfnr6DP4HPGHnYhh2HQZle8UBrSDm4ntflErpISQ==';
var signature = 'WLShxe8KMwHUt8hVD5+iE4tDO+J8Li21yocDWnq8LVRpE2PMMWCxjQzOCzyoFm4i/yW07UKtKQxcnzB44ZEdP6e6HelwBwEdP4lb8jQcc5knk8SuUth4N7cu6Em8FPOdOJdd9idHbO/p8BTb14wgua5n+1SDKHm+wPikOVsfCMYsXcwRWx5FsgP1wVPrDsCHN/ISiCXz+UuMnd6H0uRNdLZ/x/ikPkknh+P1kuFa2a2LN4r57IwBDAxkdf9MzXEexSOO0nWLnyJY2VAFB+O7JKE39CwMJ1+YDOwTTTLjilkCnSlfnr6DP4HPGHnYhh2HQZle8UBrSDm4ntflErpISQ=='
var body = {
"version":"1.0",
"session":{
Expand Down Expand Up @@ -189,10 +190,10 @@ test('handle valid signature with double byte utf8 encodings', function(t) {
}
}
}
};
verifier(cert_url, signature, JSON.stringify(body), function(er) {
t.equal(er, undefined);
clock.restore();
}
verifier(cert_url, signature, JSON.stringify(body), function (er) {
t.equal(er, undefined)
clock.restore()
t.end()
});
})
})
2 changes: 1 addition & 1 deletion test/validate-cert-uri.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var validateCertUri = require('../validate-cert-uri')
unroll.use(test)

unroll('verifier.validateCertUri should be #valid for #url',
function(t, testArgs) {
function (t, testArgs) {
var cert_uri = url.parse(testArgs['url'])
var result = validateCertUri(cert_uri)
var valid = testArgs['valid']
Expand Down
10 changes: 5 additions & 5 deletions test/validate-cert.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var url = require('url')
var validate = require('../validate-cert')


function createInvalidCert() {
function createInvalidCert () {
var keys = pki.rsa.generateKeyPair(512)
var cert = pki.createCertificate()
cert.publicKey = keys.publicKey
Expand Down Expand Up @@ -85,24 +85,24 @@ function createInvalidCert() {
return pki.certificateToPem(cert)
}

test('fails on invalid pem cert parameter', function(t) {
test('fails on invalid pem cert parameter', function (t) {
t.assert(validate(undefined) !== undefined, 'Error should have been thrown')
t.end()
})

test('fails on non amazon subject alt name', function(t) {
test('fails on non amazon subject alt name', function (t) {
var pem = createInvalidCert()
t.assert(validate(pem) === 'invalid certificate validity (correct domain not found in subject alternative names)', 'Certificate must be from amazon')
t.end()
})

test('fails on expired certificate (Not After)', function(t) {
test('fails on expired certificate (Not After)', function (t) {
var pem = fs.readFileSync(__dirname + '/cert-expired.pem')
t.assert(validate(pem) === 'invalid certificate validity (past expired date)')
t.end()
})

test('approves valid certifcate', function(t) {
test('approves valid certifcate', function (t) {
var pem = fs.readFileSync(__dirname + '/cert-latest.pem')
t.assert(validate(pem) === undefined, 'Certificate should be valid')
t.end()
Expand Down
16 changes: 8 additions & 8 deletions validate-cert-uri.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ var VALID_CERT_PORT = '443'

// parse a certificate and check it's contents for validity
module.exports = function validateCertUri(cert_uri) {
if (cert_uri.protocol !== 'https:') {
if (cert_uri.protocol !== 'https:')
return 'Certificate URI MUST be https: ' + cert_uri
}
if (cert_uri.port && (cert_uri.port !== VALID_CERT_PORT)) {

if (cert_uri.port && (cert_uri.port !== VALID_CERT_PORT))
return 'Certificate URI port MUST be ' + VALID_CERT_PORT + ', was: ' + cert_uri.port
}
if (cert_uri.hostname !== VALID_CERT_HOSTNAME) {

if (cert_uri.hostname !== VALID_CERT_HOSTNAME)
return 'Certificate URI hostname must be ' + VALID_CERT_HOSTNAME + ': ' + cert_uri.hostname
}
if (cert_uri.path.indexOf(VALID_CERT_PATH_START) !== 0) {

if (cert_uri.path.indexOf(VALID_CERT_PATH_START) !== 0)
return 'Certificate URI path must start with ' + VALID_CERT_PATH_START + ': ' + cert_uri
}

return true
}
Loading

0 comments on commit f77b68c

Please sign in to comment.