From 4d6e028fca8f2af9566b1d02217f695911013978 Mon Sep 17 00:00:00 2001 From: Timo Hocker Date: Sun, 13 Dec 2020 13:09:34 +0100 Subject: [PATCH] object signature improvements --- .gitignore | 1 + CHANGELOG.md | 7 +++++++ README.md | 4 +++- index.js | 37 +++++++++++++++++++++++++++-------- package.json | 7 +++++-- test/spec/index.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index b5f61ac..e882b3c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ # stryker temp files .stryker-tmp *.log +*.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 24900a4..ee286d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## 1.2.0 + +object signing: + +- custom properties +- keys retrievable using functions + ## 1.1.0 aes encryption functions diff --git a/README.md b/README.md index 4293f42..ad0f763 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @sapphirecode/crypto-helper -version: 1.1.x +version: 1.2.x simple functions for cryptography @@ -30,6 +30,8 @@ const signed = crypto.sign_object({foo: 'bar'}, 'secret'); const info = crypto.get_signature_info(signed); // returns an object with iat (issued at), key_info and data const dec = crypto.decode_signed(signed); // decode a signed object without verifying the signature const ver = crypto.verify_signature(signed, 'secret', 10000); // verifies the signature and returns the contents. the timeout is in milliseconds and optional, timing will be ignored if omitted. +const ver_info = crypto.verify_signature_get_info(signed, 'secret', 10000); // verify a signature and get signature information like iat and key_info +const ver_func = crypto.verify_signature(signed, (signature_info)=>'secret', 10000); // verify a signature, retrieve the key using the signature info // encryption const enc = crypto.encrypt_aes('foo', 'bar'); diff --git a/index.js b/index.js index 50a1f45..eb94b4b 100644 --- a/index.js +++ b/index.js @@ -94,11 +94,15 @@ function hash_sha512 (str, salt) { * * @param {any} obj object to sign * @param {string} key key to use - * @param {string} key_info key identifier + * @param {string|Object} key_info key identifier * @returns {string} signed object */ function sign_object (obj, key, key_info = null) { - const payload = { iat: Date.now (), key_info, obj }; + const payload = { + iat: Date.now (), + obj, + ...(typeof key_info === 'object' ? key_info : { key_info }) + }; const str = encoding.to_b64 (JSON.stringify (payload)); const token = encoding.to_b64 (hash_sha512 (str, key), 'hex'); const res = `${str}.${token}`; @@ -106,27 +110,43 @@ function sign_object (obj, key, key_info = null) { } /** - * verify a signed object and return its contents + * verify a signed object and return its info and contents * * @param {string} str string to verify - * @param {string} key used key + * @param {string|(Object)=>string} key used key * @param {number} timeout timeout (optional) * @returns {any} returns object if successful, else null */ -function verify_signature (str, key, timeout = 0) { +function verify_signature_get_info (str, key, timeout = 0) { if (typeof str !== 'string') return null; const dec = decodeURIComponent (str) .split ('.'); const json = JSON.parse (encoding.to_utf8 (dec[0], 'base64')); const token = encoding.to_hex (dec[1], 'base64'); - const verify_token = hash_sha512 (dec[0], key); + const string_key = typeof key === 'string' ? key : key (json); + const verify_token = hash_sha512 (dec[0], string_key); if (token !== verify_token) return null; const time = Date.now () - json.iat; if (timeout !== 0 && time > timeout) return null; - return json.obj; + return json; +} + +/** + * verify a signed object and return its contents + * + * @param {string} str string to verify + * @param {string|(Object)=>string} key used key + * @param {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; } /** @@ -255,6 +275,7 @@ module.exports = { random_hex, random_string, sign_object, - verify_signature + verify_signature, + verify_signature_get_info }; diff --git a/package.json b/package.json index 8d17f2d..864d38c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sapphirecode/crypto-helper", - "version": "1.1.62", + "version": "1.2.0", "main": "index.js", "author": { "name": "Timo Hocker", @@ -40,5 +40,8 @@ "LICENSE", "index.js", "index.d.ts" - ] + ], + "engines": { + "node": ">=10.0.0" + } } diff --git a/test/spec/index.js b/test/spec/index.js index e2e8cae..f216f50 100644 --- a/test/spec/index.js +++ b/test/spec/index.js @@ -110,6 +110,36 @@ describe ('crypto helper', () => { }).not.toThrow (); }); + it ('should sign object with key info', () => { + const obj = { foo: 'bar' }; + expect (() => { + const str = crypto.sign_object (obj, 'baz', 'baz'); + const res = crypto.get_signature_info (str); + expect (res.key_info) + .toEqual ('baz'); + }).not.toThrow (); + }); + + it ('should sign object with custom properties', () => { + const obj = { foo: 'bar' }; + expect (() => { + const str = crypto.sign_object (obj, 'baz', { bar: 'baz' }); + const res = crypto.get_signature_info (str); + expect (res.bar) + .toEqual ('baz'); + }).not.toThrow (); + }); + + it ('should sign object with custom override properties', () => { + const obj = { foo: 'bar' }; + expect (() => { + const str = crypto.sign_object (obj, 'baz', { iat: 'baz' }); + const res = crypto.get_signature_info (str); + expect (res.iat) + .toEqual ('baz'); + }).not.toThrow (); + }); + it ('decode_signed', () => { const obj = { foo: 'bar' }; const str = crypto.sign_object (obj, 'baz'); @@ -126,6 +156,24 @@ describe ('crypto helper', () => { .toEqual (dec); }); + it ('should verify and return all info', () => { + const obj = { foo: 'bar' }; + const str = crypto.sign_object (obj, 'baz', { iat: 'baz' }); + const dec = crypto.verify_signature_get_info (str, 'baz'); + expect (dec.obj) + .toEqual (obj); + expect (dec.iat) + .toEqual ('baz'); + }); + + it ('should verify signature using function retrieved key', () => { + const obj = { foo: 'bar' }; + const str = crypto.sign_object (obj, 'baz'); + const dec = crypto.verify_signature (str, () => 'baz'); + expect (obj) + .toEqual (dec); + }); + it ('should reject tampered signatures', () => { const obj = { foo: 'bar' }; const str = crypto.sign_object (obj, 'baz');