зеркало из https://github.com/mozilla/hawk.git
Add normalized string header
This commit is contained in:
Родитель
893ae24248
Коммит
39b5ae2c17
|
@ -3,7 +3,7 @@
|
|||
<img align="right" src="https://raw.github.com/hueniverse/hawk/master/images/logo.png" /> **Hawk** is an HTTP authentication scheme using a message authentication code (MAC) algorithm to provide partial
|
||||
HTTP request cryptographic verification. For more complex use cases such as access delegation, see [Oz](/hueniverse/oz).
|
||||
|
||||
Current version: **0.3.0**
|
||||
Current version: **0.4.0**
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk)
|
||||
|
||||
|
@ -180,6 +180,7 @@ The client generates the authentication header by calculating a timestamp (e.g.
|
|||
1970 00:00:00 GMT), generates a nonce, and constructs the normalized request string (newline separated values):
|
||||
|
||||
```
|
||||
core.1
|
||||
1353832234
|
||||
j4h3g2
|
||||
GET
|
||||
|
|
|
@ -9,6 +9,11 @@ var Url = require('url');
|
|||
var internals = {};
|
||||
|
||||
|
||||
// MAC normalization format version
|
||||
|
||||
exports.headerVersion = '1'; // Prevent comparison of mac values generated with different normalized string formats
|
||||
|
||||
|
||||
// Supported MAC algorithms
|
||||
|
||||
exports.algorithms = ['hmac-sha-1', 'hmac-sha-256'];
|
||||
|
@ -16,26 +21,42 @@ exports.algorithms = ['hmac-sha-1', 'hmac-sha-256'];
|
|||
|
||||
// Calculate the request MAC
|
||||
|
||||
exports.calculateMAC = function (key, algorithm, timestamp, nonce, method, uri, host, port, ext) {
|
||||
/*
|
||||
options = {
|
||||
header: 'core', // 'core', 'bewit'
|
||||
key: 'aoijedoaijsdlaksjdl',
|
||||
algorithm: 'hmac-sha-256', // 'hmac-sha-1', 'hmac-sha-256'
|
||||
timestamp: 1357718381034,
|
||||
nonce: 'd3d345f',
|
||||
method: 'GET',
|
||||
uri: '/resource?a=1&b=2',
|
||||
host: 'example.com',
|
||||
port: 8080,
|
||||
ext: 'app-specific-data'
|
||||
};
|
||||
*/
|
||||
|
||||
exports.calculateMAC = function (options) {
|
||||
|
||||
// Parse request URI
|
||||
|
||||
var url = Url.parse(uri);
|
||||
var url = Url.parse(options.uri);
|
||||
|
||||
// Construct normalized req string
|
||||
|
||||
var normalized = timestamp + '\n' +
|
||||
nonce + '\n' +
|
||||
method.toUpperCase() + '\n' +
|
||||
var normalized = options.header + '.' + exports.headerVersion + '\n' +
|
||||
options.timestamp + '\n' +
|
||||
options.nonce + '\n' +
|
||||
options.method.toUpperCase() + '\n' +
|
||||
url.pathname + (url.search || '') + '\n' +
|
||||
host.toLowerCase() + '\n' +
|
||||
port + '\n' +
|
||||
(ext || '') + '\n';
|
||||
options.host.toLowerCase() + '\n' +
|
||||
options.port + '\n' +
|
||||
(options.ext || '') + '\n';
|
||||
|
||||
// Lookup hash function
|
||||
|
||||
var hashMethod = '';
|
||||
switch (algorithm) {
|
||||
switch (options.algorithm) {
|
||||
|
||||
case 'hmac-sha-1': hashMethod = 'sha1'; break;
|
||||
case 'hmac-sha-256': hashMethod = 'sha256'; break;
|
||||
|
@ -44,7 +65,7 @@ exports.calculateMAC = function (key, algorithm, timestamp, nonce, method, uri,
|
|||
|
||||
// MAC normalized req string
|
||||
|
||||
var hmac = Crypto.createHmac(hashMethod, key).update(normalized);
|
||||
var hmac = Crypto.createHmac(hashMethod, options.key).update(normalized);
|
||||
var digest = hmac.digest('base64');
|
||||
return digest;
|
||||
};
|
||||
|
|
33
lib/index.js
33
lib/index.js
|
@ -183,7 +183,19 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
|
|||
|
||||
// Calculate MAC
|
||||
|
||||
var mac = Crypto.calculateMAC(credentials.key, credentials.algorithm, attributes.ts, attributes.nonce, req.method, req.url, host.name, host.port, attributes.ext);
|
||||
var mac = Crypto.calculateMAC({
|
||||
header: 'core',
|
||||
key: credentials.key,
|
||||
algorithm: credentials.algorithm,
|
||||
timestamp: attributes.ts,
|
||||
nonce: attributes.nonce,
|
||||
method: req.method,
|
||||
uri: req.url,
|
||||
host: host.name,
|
||||
port: host.port,
|
||||
ext: attributes.ext
|
||||
});
|
||||
|
||||
if (!Utils.fixedTimeComparison(mac, attributes.mac)) {
|
||||
return callback(Err.unauthorized('Bad mac'), credentials, attributes.ext);
|
||||
}
|
||||
|
@ -232,9 +244,20 @@ exports.getAuthorizationHeader = function (credentials, method, uri, host, port,
|
|||
|
||||
// Calculate signature
|
||||
|
||||
var timestamp = options.timestamp || Math.floor(now / 1000);
|
||||
var nonce = options.nonce || Utils.randomString(6);
|
||||
var mac = Crypto.calculateMAC(credentials.key, credentials.algorithm, timestamp, nonce, method, uri, host, port, options.ext);
|
||||
var artifacts = {
|
||||
header: 'core',
|
||||
key: credentials.key,
|
||||
algorithm: credentials.algorithm,
|
||||
timestamp: options.timestamp || Math.floor(now / 1000),
|
||||
nonce: options.nonce || Utils.randomString(6),
|
||||
method: method,
|
||||
uri: uri,
|
||||
host: host,
|
||||
port: port,
|
||||
ext: options.ext
|
||||
};
|
||||
|
||||
var mac = Crypto.calculateMAC(artifacts);
|
||||
|
||||
if (!mac) {
|
||||
return '';
|
||||
|
@ -242,7 +265,7 @@ exports.getAuthorizationHeader = function (credentials, method, uri, host, port,
|
|||
|
||||
// Construct header
|
||||
|
||||
var header = 'Hawk id="' + credentials.id + '", ts="' + timestamp + '", nonce="' + nonce + (options.ext ? '", ext="' + Utils.escapeHeaderAttribute (options.ext) : '') + '", mac="' + mac + '"';
|
||||
var header = 'Hawk id="' + credentials.id + '", ts="' + artifacts.timestamp + '", nonce="' + artifacts.nonce + (options.ext ? '", ext="' + Utils.escapeHeaderAttribute(options.ext) : '') + '", mac="' + mac + '"';
|
||||
return header;
|
||||
};
|
||||
|
||||
|
|
27
lib/uri.js
27
lib/uri.js
|
@ -114,7 +114,19 @@ exports.authenticate = function (req, credentialsFunc, options, callback) {
|
|||
|
||||
// Calculate MAC
|
||||
|
||||
var mac = Crypto.calculateMAC(credentials.key, credentials.algorithm, bewit.exp, '', 'GET', url, host.name, host.port, bewit.ext);
|
||||
var mac = Crypto.calculateMAC({
|
||||
header: 'bewit',
|
||||
key: credentials.key,
|
||||
algorithm: credentials.algorithm,
|
||||
timestamp: bewit.exp,
|
||||
nonce: '',
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
host: host.name,
|
||||
port: host.port,
|
||||
ext: bewit.ext
|
||||
});
|
||||
|
||||
if (!Utils.fixedTimeComparison(mac, bewit.mac)) {
|
||||
return callback(Err.unauthorized('Bad mac'), credentials, bewit.ext);
|
||||
}
|
||||
|
@ -155,7 +167,18 @@ exports.getBewit = function (credentials, uri, host, port, ttlSec, options) {
|
|||
// Calculate signature
|
||||
|
||||
var exp = Math.floor(now / 1000) + ttlSec;
|
||||
var mac = Crypto.calculateMAC(credentials.key, credentials.algorithm, exp, '', 'GET', uri, host, port, options.ext);
|
||||
var mac = Crypto.calculateMAC({
|
||||
header: 'bewit',
|
||||
key: credentials.key,
|
||||
algorithm: credentials.algorithm,
|
||||
timestamp: exp,
|
||||
nonce: '',
|
||||
method: 'GET',
|
||||
uri: uri,
|
||||
host: host,
|
||||
port: port,
|
||||
ext: options.ext
|
||||
});
|
||||
|
||||
if (!mac) {
|
||||
return '';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "hawk",
|
||||
"description": "HTTP Hawk Authentication Scheme",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)",
|
||||
"contributors": [],
|
||||
"repository": "git://github.com/hueniverse/hawk",
|
||||
|
|
|
@ -22,7 +22,18 @@ describe('Hawk', function () {
|
|||
|
||||
it('should return an empty value on unknown algorithm', function (done) {
|
||||
|
||||
expect(Hawk.crypto.calculateMAC('dasdfasdf', 'hmac-sha-0', Date.now() / 1000, 'k3k4j5', 'GET', '/resource/something', 'example.com', 8080)).to.equal('');
|
||||
expect(Hawk.crypto.calculateMAC({
|
||||
header: 'core',
|
||||
key: 'dasdfasdf',
|
||||
algorithm: 'hmac-sha-0',
|
||||
timestamp: Date.now() / 1000,
|
||||
nonce: 'k3k4j5',
|
||||
method: 'GET',
|
||||
uri: '/resource/something',
|
||||
host: 'example.com',
|
||||
port: 8080
|
||||
})).to.equal('');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -82,7 +82,7 @@ describe('Hawk', function () {
|
|||
|
||||
var req = {
|
||||
headers: {
|
||||
authorization: 'Hawk id="1", ts="1353788437", nonce="k3j4h2", mac="qrP6b5tiS2CO330rpjUEym/USBM=", ext="hello"',
|
||||
authorization: 'Hawk id="1", ts="1353788437", nonce="k3j4h2", mac="qk3onE3/tko0WylI4lQHMAra98A=", ext="hello"',
|
||||
host: 'example.com:8080'
|
||||
},
|
||||
method: 'GET',
|
||||
|
@ -101,7 +101,7 @@ describe('Hawk', function () {
|
|||
|
||||
var req = {
|
||||
headers: {
|
||||
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="hpf5lg0G0rtKrT04CiRf0Q+IDjkGkyvKdMjtqu1XV/s=", ext="some-app-data"',
|
||||
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="/4mn/w+FiIx5GO0TtwLmseu2YQc3xUqRV9sJmNHqcG0=", ext="some-app-data"',
|
||||
host: 'example.com:8000'
|
||||
},
|
||||
method: 'GET',
|
||||
|
@ -120,7 +120,7 @@ describe('Hawk', function () {
|
|||
|
||||
var req = {
|
||||
headers: {
|
||||
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="hpf5lg0G0rtKrT04CiRf0Q+IDjkGkyvKdMjtqu1XV/s=", ext="some-app-data"',
|
||||
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="/4mn/w+FiIx5GO0TtwLmseu2YQc3xUqRV9sJmNHqcG0=", ext="some-app-data"',
|
||||
host: 'example.com:8000'
|
||||
},
|
||||
method: 'GET',
|
||||
|
@ -143,7 +143,7 @@ describe('Hawk', function () {
|
|||
|
||||
var req = {
|
||||
headers: {
|
||||
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="ZPa2zWC3WUAYXrwPzJ3DpF54xjQ2ZDLe8GF1ny6JJFI=", ext="hello"',
|
||||
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="WBOsuQGaxVMdLd3Gzz5WqsS2AB0l4VkpsvRWbfWzYK4=", ext="hello"',
|
||||
host: 'example.com:8080'
|
||||
},
|
||||
method: 'GET',
|
||||
|
@ -615,7 +615,7 @@ describe('Hawk', function () {
|
|||
};
|
||||
|
||||
var header = Hawk.getAuthorizationHeader(credentials, 'POST', '/somewhere/over/the/rainbow', 'example.net', 443, { ext: 'Bazinga!', timestamp: 1353809207, nonce: 'Ygvqdz' });
|
||||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ext="Bazinga!", mac="qSK1cZEkqPwE2ttBX8QSXxO+NE3epFMu4tyVpGKjdnU="');
|
||||
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ext="Bazinga!", mac="VW+mnJDkcWbM+fgBBeJzfUkrKReXmcl9bRH9WY6qjPg="');
|
||||
done();
|
||||
});
|
||||
|
||||
|
|
22
test/uri.js
22
test/uri.js
|
@ -62,7 +62,7 @@ describe('Hawk', function () {
|
|||
host: 'example.com:8080'
|
||||
},
|
||||
method: 'GET',
|
||||
url: '/resource/4?a=1&b=2&bewit=MTIzNDU2XDQ1MDk5OTE1ODJcRUQ0ZHJtYytVQzAvaFpYQWR0QzVYOFlaU1NHc2pLYWhjSDVDdEhYaFJZUT1cc29tZS1hcHAtZGF0YQ'
|
||||
url: '/resource/4?a=1&b=2&bewit=MTIzNDU2XDQ1MTEzNDU4OTBcSTdWQWJqMWVtOG1vdmZLV1pLUVlvbEVlM2tsYkRlRU9yVllCYVdWMGdXRT1cc29tZS1hcHAtZGF0YQ'
|
||||
};
|
||||
|
||||
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {
|
||||
|
@ -81,7 +81,7 @@ describe('Hawk', function () {
|
|||
host: 'example.com:8080'
|
||||
},
|
||||
method: 'GET',
|
||||
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE1ODJcRUQ0ZHJtYytVQzAvaFpYQWR0QzVYOFlaU1NHc2pLYWhjSDVDdEhYaFJZUT1cc29tZS1hcHAtZGF0YQ&a=1&b=2'
|
||||
url: '/resource/4?bewit=MTIzNDU2XDQ1MTEzNDU4OTBcSTdWQWJqMWVtOG1vdmZLV1pLUVlvbEVlM2tsYkRlRU9yVllCYVdWMGdXRT1cc29tZS1hcHAtZGF0YQ&a=1&b=2'
|
||||
};
|
||||
|
||||
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {
|
||||
|
@ -100,7 +100,7 @@ describe('Hawk', function () {
|
|||
host: 'example.com:8080'
|
||||
},
|
||||
method: 'GET',
|
||||
url: '/resource/4?bewit=MTIzNDU2XDQ1MDk5OTE3MTlcTUE2eWkwRWRwR0pEcWRwb0JkYVdvVDJrL0hDSzA1T0Y3MkhuZlVmVy96Zz1cc29tZS1hcHAtZGF0YQ'
|
||||
url: '/resource/4?bewit=MTIzNDU2XDQ1MTEzNDU5NjBcZlE5ejBiZUpzelIxMnkwR0tTYTFITEtKVm9OWVA3S0JLOVl2VXI5S0FvST1cc29tZS1hcHAtZGF0YQ'
|
||||
};
|
||||
|
||||
Hawk.uri.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {
|
||||
|
@ -126,7 +126,19 @@ describe('Hawk', function () {
|
|||
|
||||
var exp = Math.floor(Date.now() / 1000) + 60;
|
||||
var ext = 'some-app-data';
|
||||
var mac = Hawk.crypto.calculateMAC(credentials.key, credentials.algorithm, exp, '', 'POST', req.url, 'example.com', 8080, ext);
|
||||
var mac = Hawk.crypto.calculateMAC({
|
||||
header: 'bewit',
|
||||
key: credentials.key,
|
||||
algorithm: credentials.algorithm,
|
||||
timestamp: exp,
|
||||
nonce: '',
|
||||
method: 'POST',
|
||||
uri: req.url,
|
||||
host: 'example.com',
|
||||
port: 8080,
|
||||
ext: ext
|
||||
});
|
||||
|
||||
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + ext;
|
||||
|
||||
req.url += '&bewit=' + Hawk.utils.base64urlEncode(bewit);
|
||||
|
@ -368,7 +380,7 @@ describe('Hawk', function () {
|
|||
};
|
||||
|
||||
var bewit = Hawk.uri.getBewit(credentials, '/somewhere/over/the/rainbow', 'example.com', 443, 300, { localtimeOffsetMsec: 1356420407232 - Date.now(), ext: 'xandyandz' });
|
||||
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcT2U3TzF4ZXNSTE5GTEphODBEdGRsdlVGbURzc0RnQ0gwUDRsWWxSWWloWT1ceGFuZHlhbmR6');
|
||||
expect(bewit).to.equal('MTIzNDU2XDEzNTY0MjA3MDdcTWEveU4wU2dsWWNBWmJCTEk2cDNvZEppWlVKR2VDTWcyZ043MkF3aVNwZz1ceGFuZHlhbmR6');
|
||||
done();
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче