| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 | // Load modulesvar Url = require('url');var Hoek = require('hoek');var Cryptiles = require('cryptiles');var Crypto = require('./crypto');var Utils = require('./utils');// Declare internalsvar internals = {};// Generate an Authorization header for a given request/*    uri: 'http://example.com/resource?a=b' or object from Url.parse()    method: HTTP verb (e.g. 'GET', 'POST')    options: {        // Required        credentials: {            id: 'dh37fgj492je',            key: 'aoijedoaijsdlaksjdl',            algorithm: 'sha256'                                 // 'sha1', 'sha256'        },        // Optional        ext: 'application-specific',                        // Application specific data sent via the ext attribute        timestamp: Date.now(),                              // A pre-calculated timestamp        nonce: '2334f34f',                                  // A pre-generated nonce        localtimeOffsetMsec: 400,                           // Time offset to sync with server time (ignored if timestamp provided)        payload: '{"some":"payload"}',                      // UTF-8 encoded string for body hash generation (ignored if hash provided)        contentType: 'application/json',                    // Payload content-type (ignored if hash provided)        hash: 'U4MKKSmiVxk37JCCrAVIjV=',                    // Pre-calculated payload hash        app: '24s23423f34dx',                               // Oz application id        dlg: '234sz34tww3sd'                                // Oz delegated-by application id    }*/exports.header = function (uri, method, options) {    var result = {        field: '',        artifacts: {}    };    // Validate inputs    if (!uri || (typeof uri !== 'string' && typeof uri !== 'object') ||        !method || typeof method !== 'string' ||        !options || typeof options !== 'object') {        result.err = 'Invalid argument type';        return result;    }    // Application time    var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);    // Validate credentials    var credentials = options.credentials;    if (!credentials ||        !credentials.id ||        !credentials.key ||        !credentials.algorithm) {        result.err = 'Invalid credential object';        return result;    }    if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {        result.err = 'Unknown algorithm';        return result;    }    // Parse URI    if (typeof uri === 'string') {        uri = Url.parse(uri);    }    // Calculate signature    var artifacts = {        ts: timestamp,        nonce: options.nonce || Cryptiles.randomString(6),        method: method,        resource: uri.pathname + (uri.search || ''),                            // Maintain trailing '?'        host: uri.hostname,        port: uri.port || (uri.protocol === 'http:' ? 80 : 443),        hash: options.hash,        ext: options.ext,        app: options.app,        dlg: options.dlg    };    result.artifacts = artifacts;    // Calculate payload hash    if (!artifacts.hash &&        (options.payload || options.payload === '')) {        artifacts.hash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);    }    var mac = Crypto.calculateMac('header', credentials, artifacts);    // Construct header    var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== '';       // Other falsey values allowed    var header = 'Hawk id="' + credentials.id +                 '", ts="' + artifacts.ts +                 '", nonce="' + artifacts.nonce +                 (artifacts.hash ? '", hash="' + artifacts.hash : '') +                 (hasExt ? '", ext="' + Hoek.escapeHeaderAttribute(artifacts.ext) : '') +                 '", mac="' + mac + '"';    if (artifacts.app) {        header += ', app="' + artifacts.app +                  (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';    }    result.field = header;    return result;};// Validate server response/*    res:        node's response object    artifacts:  object received from header().artifacts    options: {        payload:    optional payload received        required:   specifies if a Server-Authorization header is required. Defaults to 'false'    }*/exports.authenticate = function (res, credentials, artifacts, options) {    artifacts = Hoek.clone(artifacts);    options = options || {};    if (res.headers['www-authenticate']) {        // Parse HTTP WWW-Authenticate header        var wwwAttributes = Utils.parseAuthorizationHeader(res.headers['www-authenticate'], ['ts', 'tsm', 'error']);        if (wwwAttributes instanceof Error) {            return false;        }        // Validate server timestamp (not used to update clock since it is done via the SNPT client)        if (wwwAttributes.ts) {            var tsm = Crypto.calculateTsMac(wwwAttributes.ts, credentials);            if (tsm !== wwwAttributes.tsm) {                return false;            }        }    }    // Parse HTTP Server-Authorization header    if (!res.headers['server-authorization'] &&        !options.required) {        return true;    }    var attributes = Utils.parseAuthorizationHeader(res.headers['server-authorization'], ['mac', 'ext', 'hash']);    if (attributes instanceof Error) {        return false;    }    artifacts.ext = attributes.ext;    artifacts.hash = attributes.hash;    var mac = Crypto.calculateMac('response', credentials, artifacts);    if (mac !== attributes.mac) {        return false;    }    if (!options.payload &&        options.payload !== '') {        return true;    }    if (!attributes.hash) {        return false;    }    var calculatedHash = Crypto.calculatePayloadHash(options.payload, credentials.algorithm, res.headers['content-type']);    return (calculatedHash === attributes.hash);};// Generate a bewit value for a given URI/*    uri: 'http://example.com/resource?a=b' or object from Url.parse()    options: {        // Required        credentials: {            id: 'dh37fgj492je',            key: 'aoijedoaijsdlaksjdl',            algorithm: 'sha256'                             // 'sha1', 'sha256'        },        ttlSec: 60 * 60,                                    // TTL in seconds        // Optional        ext: 'application-specific',                        // Application specific data sent via the ext attribute        localtimeOffsetMsec: 400                            // Time offset to sync with server time    };*/exports.getBewit = function (uri, options) {    // Validate inputs    if (!uri ||        (typeof uri !== 'string' && typeof uri !== 'object') ||        !options ||        typeof options !== 'object' ||        !options.ttlSec) {        return '';    }    options.ext = (options.ext === null || options.ext === undefined ? '' : options.ext);       // Zero is valid value    // Application time    var now = Utils.now(options.localtimeOffsetMsec);    // Validate credentials    var credentials = options.credentials;    if (!credentials ||        !credentials.id ||        !credentials.key ||        !credentials.algorithm) {        return '';    }    if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {        return '';    }    // Parse URI    if (typeof uri === 'string') {        uri = Url.parse(uri);    }    // Calculate signature    var exp = Math.floor(now / 1000) + options.ttlSec;    var mac = Crypto.calculateMac('bewit', credentials, {        ts: exp,        nonce: '',        method: 'GET',        resource: uri.pathname + (uri.search || ''),                            // Maintain trailing '?'        host: uri.hostname,        port: uri.port || (uri.protocol === 'http:' ? 80 : 443),        ext: options.ext    });    // Construct bewit: id\exp\mac\ext    var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;    return Hoek.base64urlEncode(bewit);};// Generate an authorization string for a message/*    host: 'example.com',    port: 8000,    message: '{"some":"payload"}',                          // UTF-8 encoded string for body hash generation    options: {        // Required        credentials: {            id: 'dh37fgj492je',            key: 'aoijedoaijsdlaksjdl',            algorithm: 'sha256'                             // 'sha1', 'sha256'        },        // Optional        timestamp: Date.now(),                              // A pre-calculated timestamp        nonce: '2334f34f',                                  // A pre-generated nonce        localtimeOffsetMsec: 400,                           // Time offset to sync with server time (ignored if timestamp provided)    }*/exports.message = function (host, port, message, options) {    // Validate inputs    if (!host || typeof host !== 'string' ||        !port || typeof port !== 'number' ||        message === null || message === undefined || typeof message !== 'string' ||        !options || typeof options !== 'object') {        return null;    }    // Application time    var timestamp = options.timestamp || Utils.nowSecs(options.localtimeOffsetMsec);    // Validate credentials    var credentials = options.credentials;    if (!credentials ||        !credentials.id ||        !credentials.key ||        !credentials.algorithm) {        // Invalid credential object        return null;    }    if (Crypto.algorithms.indexOf(credentials.algorithm) === -1) {        return null;    }    // Calculate signature    var artifacts = {        ts: timestamp,        nonce: options.nonce || Cryptiles.randomString(6),        host: host,        port: port,        hash: Crypto.calculatePayloadHash(message, credentials.algorithm)    };    // Construct authorization    var result = {        id: credentials.id,        ts: artifacts.ts,        nonce: artifacts.nonce,        hash: artifacts.hash,        mac: Crypto.calculateMac('message', credentials, artifacts)    };    return result;};
 |