123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- var assert = require('assert-plus');
- var util = require('util');
- var utils = require('./utils');
- var HASH_ALGOS = utils.HASH_ALGOS;
- var PK_ALGOS = utils.PK_ALGOS;
- var HttpSignatureError = utils.HttpSignatureError;
- var InvalidAlgorithmError = utils.InvalidAlgorithmError;
- var validateAlgorithm = utils.validateAlgorithm;
- var State = {
- New: 0,
- Params: 1
- };
- var ParamsState = {
- Name: 0,
- Quote: 1,
- Value: 2,
- Comma: 3
- };
- function ExpiredRequestError(message) {
- HttpSignatureError.call(this, message, ExpiredRequestError);
- }
- util.inherits(ExpiredRequestError, HttpSignatureError);
- function InvalidHeaderError(message) {
- HttpSignatureError.call(this, message, InvalidHeaderError);
- }
- util.inherits(InvalidHeaderError, HttpSignatureError);
- function InvalidParamsError(message) {
- HttpSignatureError.call(this, message, InvalidParamsError);
- }
- util.inherits(InvalidParamsError, HttpSignatureError);
- function MissingHeaderError(message) {
- HttpSignatureError.call(this, message, MissingHeaderError);
- }
- util.inherits(MissingHeaderError, HttpSignatureError);
- function StrictParsingError(message) {
- HttpSignatureError.call(this, message, StrictParsingError);
- }
- util.inherits(StrictParsingError, HttpSignatureError);
- module.exports = {
-
- parseRequest: function parseRequest(request, options) {
- assert.object(request, 'request');
- assert.object(request.headers, 'request.headers');
- if (options === undefined) {
- options = {};
- }
- if (options.headers === undefined) {
- options.headers = [request.headers['x-date'] ? 'x-date' : 'date'];
- }
- assert.object(options, 'options');
- assert.arrayOfString(options.headers, 'options.headers');
- assert.optionalFinite(options.clockSkew, 'options.clockSkew');
- var authzHeaderName = options.authorizationHeaderName || 'authorization';
- if (!request.headers[authzHeaderName]) {
- throw new MissingHeaderError('no ' + authzHeaderName + ' header ' +
- 'present in the request');
- }
- options.clockSkew = options.clockSkew || 300;
- var i = 0;
- var state = State.New;
- var substate = ParamsState.Name;
- var tmpName = '';
- var tmpValue = '';
- var parsed = {
- scheme: '',
- 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);
-
- if ((code >= 0x41 && code <= 0x5a) ||
- (code >= 0x61 && code <= 0x7a)) {
- 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');
- }
- }
- if (!parsed.params.headers || parsed.params.headers === '') {
- if (request.headers['x-date']) {
- parsed.params.headers = ['x-date'];
- } else {
- parsed.params.headers = ['date'];
- }
- } else {
- parsed.params.headers = parsed.params.headers.split(' ');
- }
-
- if (!parsed.scheme || parsed.scheme !== 'Signature')
- throw new InvalidHeaderError('scheme was not "Signature"');
- if (!parsed.params.keyId)
- throw new InvalidHeaderError('keyId was not specified');
- if (!parsed.params.algorithm)
- throw new InvalidHeaderError('algorithm was not specified');
- if (!parsed.params.signature)
- throw new InvalidHeaderError('signature was not specified');
-
- parsed.params.algorithm = parsed.params.algorithm.toLowerCase();
- try {
- validateAlgorithm(parsed.params.algorithm);
- } catch (e) {
- if (e instanceof InvalidAlgorithmError)
- throw (new InvalidParamsError(parsed.params.algorithm + ' is not ' +
- 'supported'));
- else
- throw (e);
- }
-
- for (i = 0; i < parsed.params.headers.length; i++) {
- var h = parsed.params.headers[i].toLowerCase();
- parsed.params.headers[i] = h;
- if (h === 'request-line') {
- if (!options.strict) {
-
- parsed.signingString +=
- request.method + ' ' + request.url + ' HTTP/' + request.httpVersion;
- } else {
-
- throw (new StrictParsingError('request-line is not a valid header ' +
- 'with strict parsing enabled.'));
- }
- } else if (h === '(request-target)') {
- parsed.signingString +=
- '(request-target): ' + request.method.toLowerCase() + ' ' +
- request.url;
- } else {
- var value = request.headers[h];
- if (value === undefined)
- throw new MissingHeaderError(h + ' was not in the request');
- parsed.signingString += h + ': ' + value;
- }
- if ((i + 1) < parsed.params.headers.length)
- parsed.signingString += '\n';
- }
-
- 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);
- }
- 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');
- }
- }
- options.headers.forEach(function (hdr) {
-
-
- if (parsed.params.headers.indexOf(hdr.toLowerCase()) < 0)
- throw new MissingHeaderError(hdr + ' was not a signed header');
- });
- if (options.algorithms) {
- if (options.algorithms.indexOf(parsed.params.algorithm) === -1)
- throw new InvalidParamsError(parsed.params.algorithm +
- ' is not a supported algorithm');
- }
- parsed.algorithm = parsed.params.algorithm.toUpperCase();
- parsed.keyId = parsed.params.keyId;
- return parsed;
- }
- };
|