Timo Hocker
ecd0195aee
All checks were successful
continuous-integration/drone/push Build is passing
127 lines
3.4 KiB
JavaScript
127 lines
3.4 KiB
JavaScript
'use strict';
|
|
|
|
const encoding = require ('@sapphirecode/encoding-helper');
|
|
const { hash_sha512 } = require ('./hashing');
|
|
const { asym_sign, asym_verify } = require ('./rsa');
|
|
|
|
/**
|
|
* sign an object
|
|
*
|
|
* @param {any} obj object to sign
|
|
* @param {string} key key to use
|
|
* @param {string|Object} key_info key identifier
|
|
* @returns {string} signed object
|
|
*/
|
|
function sign_object (obj, key, key_info = null) {
|
|
const payload = {
|
|
iat: Date.now (),
|
|
obj,
|
|
...(typeof key_info === 'object' ? key_info : { key_info })
|
|
};
|
|
const str = encoding.to_b58 (JSON.stringify (payload));
|
|
const is_rsa = (/^-----BEGIN PRIVATE KEY-----/u).test (key);
|
|
const signature = is_rsa ? asym_sign (str, key) : hash_sha512 (str, key);
|
|
const token = encoding.to_b58 (signature, 'hex');
|
|
const res = `${str}.${token}.2`;
|
|
return res;
|
|
}
|
|
|
|
function parse_signature (str, key = null) {
|
|
let dec = str.split ('.');
|
|
const version = dec[2];
|
|
const res = {};
|
|
switch (version) {
|
|
case '2':
|
|
res.json = JSON.parse (encoding.to_utf8 (dec[0], 'base58'));
|
|
res.token = encoding.to_hex (dec[1], 'base58');
|
|
break;
|
|
default:
|
|
dec = decodeURIComponent (str)
|
|
.split ('.');
|
|
res.json = JSON.parse (
|
|
encoding.to_utf8 (dec[0], 'base64')
|
|
);
|
|
res.token = encoding.to_hex (dec[1], 'base64');
|
|
break;
|
|
}
|
|
|
|
if (key !== null) {
|
|
const string_key = typeof key === 'string' ? key : key (res.json);
|
|
res.is_rsa = (/^-----BEGIN RSA PUBLIC KEY-----/u).test (string_key);
|
|
res.hash = res.is_rsa
|
|
? asym_verify (dec[0], string_key, res.token)
|
|
: hash_sha512 (dec[0], string_key);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* verify a signed object and return its info and contents
|
|
*
|
|
* @param {string} str string to verify
|
|
* @param {string|(Object)=>string} key used key
|
|
* @param {number|(Object)=>number} timeout timeout (optional)
|
|
* @returns {any} returns object if successful, else null
|
|
*/
|
|
function verify_signature_get_info (str, key, timeout = 0) {
|
|
if (typeof str !== 'string')
|
|
return null;
|
|
const { json, token, hash, is_rsa } = parse_signature (str, key);
|
|
if (is_rsa ? !hash : (token !== hash))
|
|
return null;
|
|
const time = Date.now () - json.iat;
|
|
const num_timeout = typeof timeout === 'number' ? timeout : timeout (json);
|
|
if (num_timeout === 0 || time <= num_timeout)
|
|
return json;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* verify a signed object and return its contents
|
|
*
|
|
* @param {string} str string to verify
|
|
* @param {string|(Object)=>string} key used key
|
|
* @param {number|(Object)=>number} timeout timeout (optional)
|
|
* @returns {any} returns object if successful, else null
|
|
*/
|
|
function verify_signature (str, key, timeout = 0) {
|
|
const res = verify_signature_get_info (str, key, timeout);
|
|
if (res === null)
|
|
return null;
|
|
return res.obj;
|
|
}
|
|
|
|
/**
|
|
* get a signed object info and data
|
|
*
|
|
* @param {string} str string to decode
|
|
* @returns {any} data
|
|
*/
|
|
function get_signature_info (str) {
|
|
if (typeof str !== 'string')
|
|
return null;
|
|
const { json } = parse_signature (str);
|
|
return json;
|
|
}
|
|
|
|
/**
|
|
* decode a signed object without verifying the signature
|
|
*
|
|
* @param {string} str string to decode
|
|
* @returns {any} object
|
|
*/
|
|
function decode_signed (str) {
|
|
const info = get_signature_info (str);
|
|
if (info)
|
|
return info.obj;
|
|
return null;
|
|
}
|
|
|
|
module.exports = {
|
|
decode_signed,
|
|
get_signature_info,
|
|
sign_object,
|
|
verify_signature,
|
|
verify_signature_get_info
|
|
};
|