From 276bd13e93b9c9b1a0fc031fd6e5002ea1bc8bc4 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Thu, 31 Oct 2019 01:00:24 +0800 Subject: [PATCH] Add Signature header support. (#83) * add signature header parser support * add authorization and signature header name to utils * remove unused require module * add signature header support for set header * fix signatureHeaderName not defined bug --- lib/parser.js | 136 +++++++++++++++++++++++++------------------------- lib/signer.js | 14 +++--- lib/utils.js | 6 +++ 3 files changed, 83 insertions(+), 73 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index 5994a7e..63fe83d 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -26,7 +26,6 @@ var ParamsState = { Comma: 3 }; - ///--- Specific Errors @@ -118,91 +117,94 @@ module.exports = { assert.arrayOfString(options.headers, 'options.headers'); assert.optionalFinite(options.clockSkew, 'options.clockSkew'); - var authzHeaderName = options.authorizationHeaderName || 'authorization'; + var headers = request.headers; + var authzHeaderName = options.authorizationHeaderName; + var authz = headers[authzHeaderName] || headers[utils.HEADER.AUTH] || headers[utils.HEADER.SIG]; + + if (!authz) { + var errHeader = authzHeaderName ? authzHeaderName : utils.HEADER.AUTH + ' or ' + utils.HEADER.SIG; - if (!request.headers[authzHeaderName]) { - throw new MissingHeaderError('no ' + authzHeaderName + ' header ' + - 'present in the request'); + throw new MissingHeaderError('no ' + errHeader + ' header ' + + 'present in the request'); } options.clockSkew = options.clockSkew || 300; var i = 0; - var state = State.New; + var state = authz === headers[utils.HEADER.SIG] ? State.Params : State.New; var substate = ParamsState.Name; var tmpName = ''; var tmpValue = ''; var parsed = { - scheme: '', + scheme: authz === headers[utils.HEADER.SIG] ? 'Signature' : '', params: {}, signingString: '' }; - var authz = request.headers[authzHeaderName]; for (i = 0; i < authz.length; i++) { var c = authz.charAt(i); switch (Number(state)) { - case State.New: - if (c !== ' ') parsed.scheme += c; - else state = State.Params; - break; - - case State.Params: - switch (Number(substate)) { - - case ParamsState.Name: - var code = c.charCodeAt(0); - // restricted name of A-Z / a-z - if ((code >= 0x41 && code <= 0x5a) || // A-Z - (code >= 0x61 && code <= 0x7a)) { // a-z - tmpName += c; - } else if (c === '=') { - if (tmpName.length === 0) - throw new InvalidHeaderError('bad param format'); - substate = ParamsState.Quote; - } else { - throw new InvalidHeaderError('bad param format'); - } - break; - - case ParamsState.Quote: - if (c === '"') { - tmpValue = ''; - substate = ParamsState.Value; - } else { - throw new InvalidHeaderError('bad param format'); - } - break; - - case ParamsState.Value: - if (c === '"') { - parsed.params[tmpName] = tmpValue; - substate = ParamsState.Comma; - } else { - tmpValue += c; - } + case State.New: + if (c !== ' ') parsed.scheme += c; + else state = State.Params; break; - case ParamsState.Comma: - if (c === ',') { - tmpName = ''; - substate = ParamsState.Name; - } else { - throw new InvalidHeaderError('bad param format'); + case State.Params: + switch (Number(substate)) { + + case ParamsState.Name: + var code = c.charCodeAt(0); + // restricted name of A-Z / a-z + if ((code >= 0x41 && code <= 0x5a) || // A-Z + (code >= 0x61 && code <= 0x7a)) { // a-z + tmpName += c; + } else if (c === '=') { + if (tmpName.length === 0) + throw new InvalidHeaderError('bad param format'); + substate = ParamsState.Quote; + } else { + throw new InvalidHeaderError('bad param format'); + } + break; + + case ParamsState.Quote: + if (c === '"') { + tmpValue = ''; + substate = ParamsState.Value; + } else { + throw new InvalidHeaderError('bad param format'); + } + break; + + case ParamsState.Value: + if (c === '"') { + parsed.params[tmpName] = tmpValue; + substate = ParamsState.Comma; + } else { + tmpValue += c; + } + break; + + case ParamsState.Comma: + if (c === ',') { + tmpName = ''; + substate = ParamsState.Name; + } else { + throw new InvalidHeaderError('bad param format'); + } + break; + + default: + throw new Error('Invalid substate'); } break; default: throw new Error('Invalid substate'); - } - break; - - default: - throw new Error('Invalid substate'); } } @@ -278,19 +280,19 @@ module.exports = { // Check against the constraints var date; if (request.headers.date || request.headers['x-date']) { - if (request.headers['x-date']) { - date = new Date(request.headers['x-date']); - } else { - date = new Date(request.headers.date); - } + if (request.headers['x-date']) { + date = new Date(request.headers['x-date']); + } else { + date = new Date(request.headers.date); + } var now = new Date(); var skew = Math.abs(now.getTime() - date.getTime()); if (skew > options.clockSkew * 1000) { throw new ExpiredRequestError('clock skew of ' + - (skew / 1000) + - 's was greater than ' + - options.clockSkew + 's'); + (skew / 1000) + + 's was greater than ' + + options.clockSkew + 's'); } } @@ -304,7 +306,7 @@ module.exports = { if (options.algorithms) { if (options.algorithms.indexOf(parsed.params.algorithm) === -1) throw new InvalidParamsError(parsed.params.algorithm + - ' is not a supported algorithm'); + ' is not a supported algorithm'); } parsed.algorithm = parsed.params.algorithm.toUpperCase(); diff --git a/lib/signer.js b/lib/signer.js index deb5878..9b01142 100644 --- a/lib/signer.js +++ b/lib/signer.js @@ -2,7 +2,6 @@ var assert = require('assert-plus'); var crypto = require('crypto'); -var http = require('http'); var util = require('util'); var sshpk = require('sshpk'); var jsprim = require('jsprim'); @@ -21,6 +20,8 @@ var validateAlgorithm = utils.validateAlgorithm; var AUTHZ_FMT = 'Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"'; +var SIGNATURE_FMT = 'keyId="%s",algorithm="%s",headers="%s",signature="%s"'; + ///--- Specific Errors function MissingHeaderError(message) { @@ -389,11 +390,12 @@ module.exports = { var authzHeaderName = options.authorizationHeaderName || 'Authorization'; - request.setHeader(authzHeaderName, sprintf(AUTHZ_FMT, - options.keyId, - options.algorithm, - options.headers.join(' '), - signature)); + var FMT = authzHeaderName.toLowerCase() === utils.HEADER.SIG ? SIGNATURE_FMT : AUTHZ_FMT; + request.setHeader(authzHeaderName, sprintf(FMT, + options.keyId, + options.algorithm, + options.headers.join(' '), + signature)); return true; } diff --git a/lib/utils.js b/lib/utils.js index bbf2aa8..87bcb22 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -16,6 +16,11 @@ var PK_ALGOS = { 'ecdsa': true }; +var HEADER = { + AUTH: 'authorization', + SIG: 'signature' +}; + function HttpSignatureError(message, caller) { if (Error.captureStackTrace) Error.captureStackTrace(this, caller || HttpSignatureError); @@ -54,6 +59,7 @@ function validateAlgorithm(algorithm) { ///--- API module.exports = { + HEADER, HASH_ALGOS: HASH_ALGOS, PK_ALGOS: PK_ALGOS,