mirror of https://gitee.com/bigwinds/arangodb
290 lines
9.2 KiB
JavaScript
290 lines
9.2 KiB
JavaScript
/*jshint strict: false */
|
|
/*global Buffer */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Some crypto functions
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Jan Steemann
|
|
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var internal = require("internal");
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief generate a random number
|
|
///
|
|
/// the value returned might be positive or negative or even 0.
|
|
/// if random number generation fails, then undefined is returned
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.rand = function () {
|
|
return internal.rand();
|
|
};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an HMAC
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.hmac = function (key, message, algorithm) {
|
|
return internal.hmac(key, message, algorithm);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply a PBKDF2
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.pbkdf2 = function (salt, password, iterations, keyLength) {
|
|
return internal.pbkdf2(salt, password, iterations, keyLength);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an MD5 hash
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.md5 = function (value) {
|
|
return internal.md5(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an SHA 512 hash
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.sha512 = function (value) {
|
|
return internal.sha512(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an SHA 384 hash
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.sha384 = function (value) {
|
|
return internal.sha384(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an SHA 256 hash
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.sha256 = function (value) {
|
|
return internal.sha256(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an SHA 224 hash
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.sha224 = function (value) {
|
|
return internal.sha224(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief apply an SHA 1 hash
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.sha1 = function (value) {
|
|
return internal.sha1(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Generates a string of a given length containing numbers.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.genRandomNumbers = function (value) {
|
|
return internal.genRandomNumbers(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Generates a string of a given length containing numbers and characters.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.genRandomAlphaNumbers = function (value) {
|
|
return internal.genRandomAlphaNumbers(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Generates a string of a given length containing ASCII characters.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.genRandomSalt = function (value) {
|
|
return internal.genRandomSalt(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Generates a base64 encoded nonce string.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.createNonce = function () {
|
|
return internal.createNonce();
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Checks and marks a nonce
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.checkAndMarkNonce = function (value) {
|
|
return internal.checkAndMarkNonce(value);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Compares two strings in constant time
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
exports.constantEquals = function (a, b) {
|
|
'use strict';
|
|
var length, result, i;
|
|
length = a.length > b.length ? a.length : b.length;
|
|
result = true;
|
|
for (i = 0; i < length; i++) {
|
|
if (a.charCodeAt(i) !== b.charCodeAt(i)) {
|
|
result = false;
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Encodes JSON Web Token
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function jwtUrlEncode(str) {
|
|
'use strict';
|
|
return str.replace(/[+]/g, '-').replace(/[\/]/g, '_').replace(/[=]/g, '');
|
|
}
|
|
|
|
function jwtHmacSigner(algorithm) {
|
|
'use strict';
|
|
return function (key, segments) {
|
|
return new Buffer(exports.hmac(key, segments.join('.'), algorithm), 'hex').toString('base64');
|
|
};
|
|
}
|
|
|
|
function jwtHmacVerifier(algorithm) {
|
|
'use strict';
|
|
return function (key, segments) {
|
|
return exports.constantEquals(
|
|
exports.hmac(key, segments.slice(0, 2).join('.'), algorithm),
|
|
segments[2]
|
|
);
|
|
};
|
|
}
|
|
|
|
exports.jwtAlgorithms = {
|
|
HS256: {
|
|
sign: jwtHmacSigner('sha256'),
|
|
verify: jwtHmacVerifier('sha256')
|
|
},
|
|
HS384: {
|
|
sign: jwtHmacSigner('sha384'),
|
|
verify: jwtHmacVerifier('sha384')
|
|
},
|
|
HS512: {
|
|
sign: jwtHmacSigner('sha512'),
|
|
verify: jwtHmacVerifier('sha512')
|
|
},
|
|
none: {
|
|
sign: function (key) {
|
|
'use strict';
|
|
if (key) {
|
|
throw new Error('Can not sign message with key using algorithm "none"!');
|
|
}
|
|
return '';
|
|
},
|
|
verify: function (key) {
|
|
'use strict';
|
|
// see https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
|
|
return !key;
|
|
}
|
|
}
|
|
};
|
|
|
|
exports.jwtCanonicalAlgorithmName = function (algorithm) {
|
|
'use strict';
|
|
if (algorithm && typeof algorithm === 'string') {
|
|
if (exports.jwtAlgorithms.hasOwnProperty(algorithm.toLowerCase())) {
|
|
return algorithm.toLowerCase();
|
|
}
|
|
if (exports.jwtAlgorithms.hasOwnProperty(algorithm.toUpperCase())) {
|
|
return algorithm.toUpperCase();
|
|
}
|
|
}
|
|
throw new Error(
|
|
'Unknown algorithm "'
|
|
+ algorithm
|
|
+ '". Only the following algorithms are supported at this time: '
|
|
+ Object.keys(exports.jwtAlgorithms).join(', ')
|
|
);
|
|
};
|
|
|
|
exports.jwtEncode = function (key, message, algorithm) {
|
|
'use strict';
|
|
algorithm = algorithm ? exports.jwtCanonicalAlgorithmName(algorithm) : 'HS256';
|
|
var header = {typ: 'JWT', alg: algorithm}, segments = [];
|
|
segments.push(jwtUrlEncode(new Buffer(JSON.stringify(header)).toString('base64')));
|
|
segments.push(jwtUrlEncode(new Buffer(JSON.stringify(message)).toString('base64')));
|
|
segments.push(jwtUrlEncode(exports.jwtAlgorithms[algorithm].sign(key, segments)));
|
|
return segments.join('.');
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Decodes JSON Web Token
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function jwtUrlDecode(str) {
|
|
'use strict';
|
|
while ((str.length % 4) !== 0) {
|
|
str += '=';
|
|
}
|
|
return str.replace(/\-/g, '+').replace(/_/g, '/');
|
|
}
|
|
|
|
exports.jwtDecode = function (key, token, noVerify) {
|
|
'use strict';
|
|
if (!token) {
|
|
return null;
|
|
}
|
|
|
|
var segments = token.split('.');
|
|
if (segments.length !== 3) {
|
|
throw new Error('Wrong number of JWT segments!');
|
|
}
|
|
var headerSeg = new Buffer(jwtUrlDecode(segments[0]), 'base64').toString();
|
|
var messageSeg = new Buffer(jwtUrlDecode(segments[1]), 'base64').toString();
|
|
segments[2] = new Buffer(jwtUrlDecode(segments[2]), 'base64').toString('hex');
|
|
|
|
if (!noVerify) {
|
|
var header = JSON.parse(headerSeg);
|
|
header.alg = exports.jwtCanonicalAlgorithmName(header.alg);
|
|
if (!exports.jwtAlgorithms[header.alg].verify(key, segments)) {
|
|
throw new Error('Signature verification failed!');
|
|
}
|
|
}
|
|
|
|
return JSON.parse(messageSeg);
|
|
};
|
|
|
|
|