refactoring, asymmetric signatures and encryption
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
612686a224
commit
ecd0195aee
@ -1,5 +1,11 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 1.3.0
|
||||
|
||||
asymmetric encryption and signatures
|
||||
|
||||
object signing: ability to use rsa keys
|
||||
|
||||
## 1.2.0
|
||||
|
||||
object signing:
|
||||
|
27
README.md
27
README.md
@ -1,6 +1,6 @@
|
||||
# @sapphirecode/crypto-helper
|
||||
|
||||
version: 1.2.x
|
||||
version: 1.3.x
|
||||
|
||||
simple functions for cryptography
|
||||
|
||||
@ -16,6 +16,8 @@ yarn:
|
||||
|
||||
## Usage
|
||||
|
||||
### Examples
|
||||
|
||||
```js
|
||||
const crypto = require('@sapphirecode/crypto-helper');
|
||||
|
||||
@ -31,13 +33,34 @@ const info = crypto.get_signature_info(signed); // returns an object with iat (i
|
||||
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
|
||||
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');
|
||||
const dec = crypto.decrypt_aes(enc, 'bar');
|
||||
|
||||
// asymmetric encryption and signatures
|
||||
const keys = await crypto.generate_keypair(2048); // generate private and public key (length is optional and 2048 by default)
|
||||
|
||||
const aenc = crypto.asym_encrypt('foo', keys.public_key); // encrypt
|
||||
const adec = crypto.asym_decrypt(aenc, key.private_key); // decrypt
|
||||
|
||||
const asig = crypto.asym_sign('foo', keys.private_key); // create signature
|
||||
const aver = crypto.asym_verify('foo', keys.public_key, asig); // verify signature, returns boolean
|
||||
```
|
||||
|
||||
### Asymmetric signatures on object signing
|
||||
|
||||
the functions `sign_object`, `verify_signature`, ... will automatically detect
|
||||
rsa keys and use them to sign objects asymmetrically. Note that keys have to be
|
||||
provided in the correct order (private key for signing, public key for
|
||||
verifying). Else the keys will just be interpreted as symmetric and verification
|
||||
will fail.
|
||||
|
||||
## License
|
||||
|
||||
MIT © Timo Hocker <timo@scode.ovh>
|
||||
|
297
index.js
297
index.js
@ -8,293 +8,16 @@
|
||||
// @ts-nocheck
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('crypto');
|
||||
const encoding = require ('@sapphirecode/encoding-helper');
|
||||
|
||||
const encryption_mode_cbc_256 = {
|
||||
algorithm: 'aes-256-cbc',
|
||||
nonce_size: 16,
|
||||
key_size: 32,
|
||||
hash: 'sha256',
|
||||
salt_size: 16,
|
||||
iterations: 32767
|
||||
};
|
||||
|
||||
const encryption_mode_cbc_256_quick = {
|
||||
algorithm: 'aes-256-cbc',
|
||||
nonce_size: 16,
|
||||
key_size: 32,
|
||||
hash: 'sha256',
|
||||
salt_size: 16,
|
||||
iterations: 32
|
||||
};
|
||||
|
||||
const encryption_mode_cbc_128 = {
|
||||
algorithm: 'aes-128-cbc',
|
||||
nonce_size: 16,
|
||||
key_size: 16,
|
||||
hash: 'sha256',
|
||||
salt_size: 16,
|
||||
iterations: 40
|
||||
};
|
||||
|
||||
/**
|
||||
* creates a random string
|
||||
*
|
||||
* @param {number} len string length default: 8
|
||||
* @returns {string} random string
|
||||
*/
|
||||
function random_string (len = 8) {
|
||||
if (len < 1)
|
||||
throw new Error ('invalid length');
|
||||
return crypto.randomBytes (Math.ceil (len * 3 / 4))
|
||||
.toString ('base64')
|
||||
.substr (0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a random hexadecimal string
|
||||
*
|
||||
* @param {number} len length
|
||||
* @returns {string} hex string
|
||||
*/
|
||||
function random_hex (len = 8) {
|
||||
if (len < 1)
|
||||
throw new Error ('invalid length');
|
||||
return crypto.randomBytes (Math.ceil (len / 2))
|
||||
.toString ('hex')
|
||||
.substr (0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a 64 character long random hex string
|
||||
*
|
||||
* @returns {string} salt
|
||||
*/
|
||||
function create_salt () {
|
||||
return random_hex (64);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a sha512 hash
|
||||
*
|
||||
* @param {string} str string input
|
||||
* @param {string} salt salt
|
||||
* @returns {string} salt
|
||||
*/
|
||||
function hash_sha512 (str, salt) {
|
||||
const md = crypto.createHash ('sha512');
|
||||
md.update (str);
|
||||
md.update (salt);
|
||||
return md.digest ('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 token = encoding.to_b58 (hash_sha512 (str, key), '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.hash = 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 } = parse_signature (str, key);
|
||||
if (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;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a sha256 hash
|
||||
*
|
||||
* @param {any} data input
|
||||
* @returns {string} hash
|
||||
*/
|
||||
function checksum (data) {
|
||||
const md = crypto.createHash ('sha256');
|
||||
md.update (String (data));
|
||||
return md.digest ('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypt plain text with aes
|
||||
*
|
||||
* @param {string} text plaintext
|
||||
* @param {string} pass password
|
||||
* @param {object} mode encryption mode
|
||||
* @returns {string} encrypted
|
||||
*/
|
||||
function encrypt_aes (text, pass, mode = encryption_mode_cbc_256) {
|
||||
const salt = crypto.randomBytes (mode.salt_size);
|
||||
// eslint-disable-next-line no-sync
|
||||
const key = crypto.pbkdf2Sync (
|
||||
Buffer.from (pass),
|
||||
salt,
|
||||
mode.iterations,
|
||||
mode.key_size,
|
||||
mode.hash
|
||||
);
|
||||
const nonce = crypto.randomBytes (mode.nonce_size);
|
||||
const cipher = crypto.createCipheriv (mode.algorithm, key, nonce);
|
||||
return Buffer.concat ([
|
||||
salt,
|
||||
nonce,
|
||||
cipher.update (Buffer.from (text)),
|
||||
cipher.final ()
|
||||
])
|
||||
.toString ('base64');
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt an aes string
|
||||
*
|
||||
* @param {string} ciphertext encrypted text
|
||||
* @param {string} pass password
|
||||
* @param {object} mode encryption mode
|
||||
* @param {boolean} rethrow rethrow exceptions instead of returning null
|
||||
* @returns {string} plaintext
|
||||
*/
|
||||
function decrypt_aes (
|
||||
ciphertext,
|
||||
pass,
|
||||
mode = encryption_mode_cbc_256,
|
||||
rethrow = false
|
||||
) {
|
||||
try {
|
||||
let buf = Buffer.from (ciphertext, 'base64');
|
||||
const salt = buf.slice (0, mode.salt_size);
|
||||
buf = buf.slice (mode.salt_size);
|
||||
// eslint-disable-next-line no-sync
|
||||
const key = crypto.pbkdf2Sync (
|
||||
Buffer.from (pass),
|
||||
salt,
|
||||
mode.iterations,
|
||||
mode.key_size,
|
||||
mode.hash
|
||||
);
|
||||
const nonce = buf.slice (0, mode.nonce_size);
|
||||
buf = buf.slice (mode.nonce_size);
|
||||
const cipher = crypto.createDecipheriv (mode.algorithm, key, nonce);
|
||||
return Buffer.concat ([
|
||||
cipher.update (buf),
|
||||
cipher.final ()
|
||||
])
|
||||
.toString ();
|
||||
}
|
||||
catch (e) {
|
||||
if (rethrow)
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const encryption = require ('./lib/encryption');
|
||||
const hashing = require ('./lib/hashing');
|
||||
const random = require ('./lib/random');
|
||||
const signatures = require ('./lib/signatures');
|
||||
const rsa = require ('./lib/rsa');
|
||||
|
||||
module.exports = {
|
||||
checksum,
|
||||
create_salt,
|
||||
decode_signed,
|
||||
decrypt_aes,
|
||||
encrypt_aes,
|
||||
encryption_mode_cbc_128,
|
||||
encryption_mode_cbc_256,
|
||||
encryption_mode_cbc_256_quick,
|
||||
get_signature_info,
|
||||
hash_sha512,
|
||||
random_hex,
|
||||
random_string,
|
||||
sign_object,
|
||||
verify_signature,
|
||||
verify_signature_get_info
|
||||
...random,
|
||||
...hashing,
|
||||
...encryption,
|
||||
...signatures,
|
||||
...rsa
|
||||
};
|
||||
|
110
lib/encryption.js
Normal file
110
lib/encryption.js
Normal file
@ -0,0 +1,110 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('crypto');
|
||||
|
||||
const encryption_mode_cbc_256 = {
|
||||
algorithm: 'aes-256-cbc',
|
||||
nonce_size: 16,
|
||||
key_size: 32,
|
||||
hash: 'sha256',
|
||||
salt_size: 16,
|
||||
iterations: 32767
|
||||
};
|
||||
|
||||
const encryption_mode_cbc_256_quick = {
|
||||
algorithm: 'aes-256-cbc',
|
||||
nonce_size: 16,
|
||||
key_size: 32,
|
||||
hash: 'sha256',
|
||||
salt_size: 16,
|
||||
iterations: 32
|
||||
};
|
||||
|
||||
const encryption_mode_cbc_128 = {
|
||||
algorithm: 'aes-128-cbc',
|
||||
nonce_size: 16,
|
||||
key_size: 16,
|
||||
hash: 'sha256',
|
||||
salt_size: 16,
|
||||
iterations: 40
|
||||
};
|
||||
|
||||
/**
|
||||
* encrypt plain text with aes
|
||||
*
|
||||
* @param {string} text plaintext
|
||||
* @param {string} pass password
|
||||
* @param {object} mode encryption mode
|
||||
* @returns {string} encrypted
|
||||
*/
|
||||
function encrypt_aes (text, pass, mode = encryption_mode_cbc_256) {
|
||||
const salt = crypto.randomBytes (mode.salt_size);
|
||||
// eslint-disable-next-line no-sync
|
||||
const key = crypto.pbkdf2Sync (
|
||||
Buffer.from (pass),
|
||||
salt,
|
||||
mode.iterations,
|
||||
mode.key_size,
|
||||
mode.hash
|
||||
);
|
||||
const nonce = crypto.randomBytes (mode.nonce_size);
|
||||
const cipher = crypto.createCipheriv (mode.algorithm, key, nonce);
|
||||
return Buffer.concat ([
|
||||
salt,
|
||||
nonce,
|
||||
cipher.update (Buffer.from (text)),
|
||||
cipher.final ()
|
||||
])
|
||||
.toString ('base64');
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt an aes string
|
||||
*
|
||||
* @param {string} ciphertext encrypted text
|
||||
* @param {string} pass password
|
||||
* @param {object} mode encryption mode
|
||||
* @param {boolean} rethrow rethrow exceptions instead of returning null
|
||||
* @returns {string} plaintext
|
||||
*/
|
||||
function decrypt_aes (
|
||||
ciphertext,
|
||||
pass,
|
||||
mode = encryption_mode_cbc_256,
|
||||
rethrow = false
|
||||
) {
|
||||
try {
|
||||
let buf = Buffer.from (ciphertext, 'base64');
|
||||
const salt = buf.slice (0, mode.salt_size);
|
||||
buf = buf.slice (mode.salt_size);
|
||||
// eslint-disable-next-line no-sync
|
||||
const key = crypto.pbkdf2Sync (
|
||||
Buffer.from (pass),
|
||||
salt,
|
||||
mode.iterations,
|
||||
mode.key_size,
|
||||
mode.hash
|
||||
);
|
||||
const nonce = buf.slice (0, mode.nonce_size);
|
||||
buf = buf.slice (mode.nonce_size);
|
||||
const cipher = crypto.createDecipheriv (mode.algorithm, key, nonce);
|
||||
return Buffer.concat ([
|
||||
cipher.update (buf),
|
||||
cipher.final ()
|
||||
])
|
||||
.toString ();
|
||||
}
|
||||
catch (e) {
|
||||
if (rethrow)
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
decrypt_aes,
|
||||
encrypt_aes,
|
||||
encryption_mode_cbc_128,
|
||||
encryption_mode_cbc_256,
|
||||
encryption_mode_cbc_256_quick
|
||||
};
|
34
lib/hashing.js
Normal file
34
lib/hashing.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('crypto');
|
||||
|
||||
/**
|
||||
* creates a sha256 hash
|
||||
*
|
||||
* @param {any} data input
|
||||
* @returns {string} hash
|
||||
*/
|
||||
function checksum (data) {
|
||||
const md = crypto.createHash ('sha256');
|
||||
md.update (String (data));
|
||||
return md.digest ('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a sha512 hash
|
||||
*
|
||||
* @param {string} str string input
|
||||
* @param {string} salt salt
|
||||
* @returns {string} salt
|
||||
*/
|
||||
function hash_sha512 (str, salt) {
|
||||
const md = crypto.createHash ('sha512');
|
||||
md.update (str);
|
||||
md.update (salt);
|
||||
return md.digest ('hex');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hash_sha512,
|
||||
checksum
|
||||
};
|
46
lib/random.js
Normal file
46
lib/random.js
Normal file
@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('crypto');
|
||||
|
||||
/**
|
||||
* creates a random string
|
||||
*
|
||||
* @param {number} len string length default: 8
|
||||
* @returns {string} random string
|
||||
*/
|
||||
function random_string (len = 8) {
|
||||
if (len < 1)
|
||||
throw new Error ('invalid length');
|
||||
return crypto.randomBytes (Math.ceil (len * 3 / 4))
|
||||
.toString ('base64')
|
||||
.substr (0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a random hexadecimal string
|
||||
*
|
||||
* @param {number} len length
|
||||
* @returns {string} hex string
|
||||
*/
|
||||
function random_hex (len = 8) {
|
||||
if (len < 1)
|
||||
throw new Error ('invalid length');
|
||||
return crypto.randomBytes (Math.ceil (len / 2))
|
||||
.toString ('hex')
|
||||
.substr (0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a 64 character long random hex string
|
||||
*
|
||||
* @returns {string} salt
|
||||
*/
|
||||
function create_salt () {
|
||||
return random_hex (64);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create_salt,
|
||||
random_hex,
|
||||
random_string
|
||||
};
|
98
lib/rsa.js
Normal file
98
lib/rsa.js
Normal file
@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('crypto');
|
||||
|
||||
/**
|
||||
* generate a new rsa keypair
|
||||
*
|
||||
* @param {number} length the key length in bit. default: 2048
|
||||
* @returns {Promise<{public_key: string, private_key: string}>} generated keys
|
||||
*/
|
||||
async function generate_keypair (length = 2048) {
|
||||
const key = await new Promise (
|
||||
(res, rej) => crypto.generateKeyPair (
|
||||
'rsa',
|
||||
{
|
||||
modulusLength: length,
|
||||
publicKeyEncoding: {
|
||||
type: 'pkcs1',
|
||||
format: 'pem'
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: 'pkcs8',
|
||||
format: 'pem'
|
||||
}
|
||||
},
|
||||
(err, public_key, private_key) => {
|
||||
if (err)
|
||||
rej (err);
|
||||
res ({ public_key, private_key });
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypts data using a public key
|
||||
* it can only be decrypted with the corresponding private key
|
||||
*
|
||||
* @param {string} data data to encrypt
|
||||
* @param {string} public_key public key
|
||||
* @returns {string} encrypted data
|
||||
*/
|
||||
function asym_encrypt (data, public_key) {
|
||||
return crypto.publicEncrypt (public_key, Buffer.from (data))
|
||||
.toString ('base64');
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypts data using a private key
|
||||
*
|
||||
* @param {string} data data to decrypt
|
||||
* @param {string} private_key private key
|
||||
* @returns {string} decrypted data
|
||||
*/
|
||||
function asym_decrypt (data, private_key) {
|
||||
return crypto.privateDecrypt (private_key, Buffer.from (data, 'base64'))
|
||||
.toString ();
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a signature using a private key
|
||||
* can later be verified using the corresponding public key
|
||||
*
|
||||
* @param {string} data data to sign
|
||||
* @param {string} private_key private key
|
||||
* @returns {string} signature
|
||||
*/
|
||||
function asym_sign (data, private_key) {
|
||||
const sign = crypto.createSign ('sha256');
|
||||
sign.write (data);
|
||||
sign.end ();
|
||||
return sign.sign (private_key, 'hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* verifies a signature using a public key
|
||||
*
|
||||
* @param {string} data data to verify
|
||||
* @param {string} public_key public key
|
||||
* @param {string} signature signature to verify
|
||||
* @returns {boolean} true if signature is valid
|
||||
*/
|
||||
function asym_verify (data, public_key, signature) {
|
||||
const verify = crypto.createVerify ('sha256');
|
||||
verify.write (data);
|
||||
verify.end ();
|
||||
return verify.verify (public_key, signature, 'hex');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generate_keypair,
|
||||
asym_encrypt,
|
||||
asym_decrypt,
|
||||
asym_sign,
|
||||
asym_verify
|
||||
};
|
126
lib/signatures.js
Normal file
126
lib/signatures.js
Normal file
@ -0,0 +1,126 @@
|
||||
'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
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sapphirecode/crypto-helper",
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0",
|
||||
"main": "index.js",
|
||||
"author": {
|
||||
"name": "Timo Hocker",
|
||||
@ -38,10 +38,12 @@
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"lib/*.js",
|
||||
"lib/*.d.ts",
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
"node": ">=10.12.0"
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,5 @@ module.exports = {
|
||||
testRunner: 'jasmine',
|
||||
jasmineConfigFile: 'jasmine.json',
|
||||
coverageAnalysis: 'perTest',
|
||||
mutate: [ 'index.js' ]
|
||||
mutate: [ 'lib/*.js' ]
|
||||
};
|
||||
|
27
test/spec/hashing.js
Normal file
27
test/spec/hashing.js
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('../../index');
|
||||
|
||||
describe ('hashing', () => {
|
||||
it ('sha512', () => {
|
||||
const hash = crypto.hash_sha512 ('a', 'b');
|
||||
expect (
|
||||
hash
|
||||
)
|
||||
.toEqual (
|
||||
// eslint-disable-next-line max-len
|
||||
'2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d'
|
||||
);
|
||||
});
|
||||
|
||||
it ('checksum', () => {
|
||||
const hash = crypto.checksum ('foo');
|
||||
expect (
|
||||
hash
|
||||
)
|
||||
.toEqual (
|
||||
// eslint-disable-next-line max-len
|
||||
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'
|
||||
);
|
||||
});
|
||||
});
|
72
test/spec/random.js
Normal file
72
test/spec/random.js
Normal file
@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('../../index');
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
describe ('random', () => {
|
||||
it ('random_hex', () => {
|
||||
const hex = crypto.random_hex (16);
|
||||
expect (hex.length)
|
||||
.toEqual (16);
|
||||
expect (hex)
|
||||
.toMatch (/^[0-9a-f]+$/iu);
|
||||
});
|
||||
|
||||
it ('random_hex with default length', () => {
|
||||
const hex = crypto.random_hex ();
|
||||
expect (hex.length)
|
||||
.toEqual (8);
|
||||
expect (hex)
|
||||
.toMatch (/^[0-9a-f]+$/iu);
|
||||
});
|
||||
|
||||
it ('random_hex should refuse length smaller 1', () => {
|
||||
expect (
|
||||
() => (crypto.random_hex (0))
|
||||
)
|
||||
.toThrowError ('invalid length');
|
||||
});
|
||||
|
||||
it ('random_hex should always return correct length', () => {
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const hex = crypto.random_hex (i);
|
||||
expect (hex.length)
|
||||
.toEqual (i);
|
||||
}
|
||||
});
|
||||
|
||||
it ('random_string', () => {
|
||||
const str = crypto.random_string (16);
|
||||
expect (str.length)
|
||||
.toEqual (16);
|
||||
});
|
||||
|
||||
it ('random_string with default length', () => {
|
||||
const str = crypto.random_string ();
|
||||
expect (str.length)
|
||||
.toEqual (8);
|
||||
});
|
||||
|
||||
it ('random_string should refuse length smaller 1', () => {
|
||||
expect (
|
||||
() => (crypto.random_string (0))
|
||||
)
|
||||
.toThrowError ('invalid length');
|
||||
});
|
||||
|
||||
it ('random_string should always return correct length', () => {
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const str = crypto.random_string (i);
|
||||
expect (str.length)
|
||||
.toEqual (i);
|
||||
}
|
||||
});
|
||||
|
||||
it ('create_salt', () => {
|
||||
const salt = crypto.create_salt ();
|
||||
expect (salt.length)
|
||||
.toEqual (64);
|
||||
expect (salt)
|
||||
.toMatch (/^[0-9a-f]+$/iu);
|
||||
});
|
||||
});
|
133
test/spec/rsa.js
Normal file
133
test/spec/rsa.js
Normal file
@ -0,0 +1,133 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('../../index');
|
||||
|
||||
const key_length = 512;
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
describe ('rsa', () => {
|
||||
it ('should create a keypair', async () => {
|
||||
const k = await crypto.generate_keypair ();
|
||||
|
||||
expect (k.private_key)
|
||||
.toMatch (/^-----BEGIN PRIVATE KEY-----.+-----END PRIVATE KEY-----\n$/su);
|
||||
expect (k.public_key)
|
||||
// eslint-disable-next-line max-len
|
||||
.toMatch (/^-----BEGIN RSA PUBLIC KEY-----.+-----END RSA PUBLIC KEY-----\n$/su);
|
||||
});
|
||||
|
||||
it ('should throw on too small key size', async () => {
|
||||
await expectAsync (crypto.generate_keypair (16))
|
||||
.toBeRejectedWithError (
|
||||
'error:0408F078:rsa routines:pkey_rsa_ctrl:key size too small'
|
||||
);
|
||||
});
|
||||
|
||||
it ('should encrypt and decrypt', async () => {
|
||||
const k = await crypto.generate_keypair (key_length);
|
||||
const data = 'foobar';
|
||||
const enc = crypto.asym_encrypt (data, k.public_key);
|
||||
const dec = crypto.asym_decrypt (enc, k.private_key);
|
||||
expect (dec)
|
||||
.toEqual (data);
|
||||
expect (enc).not.toEqual (data);
|
||||
});
|
||||
|
||||
it ('should throw if encryption key is invalid', () => {
|
||||
const key = '-----BEGIN RSA PUBLIC KEY-----\n'
|
||||
+ 'MEgCCQDGvKLaq1SB/BTzocR4ZqGNr8dz1ylxxUpDncCu0C/ayOGPnCilB0LGEdqK\n'
|
||||
+ 'rORlsYpIaDvLv6x6k8iSg6A/TsybAgMBAAE=\n'
|
||||
+ '-----END RSA PUBLIC KEY-----\n';
|
||||
const data = 'foobar';
|
||||
expect (() => crypto.asym_encrypt (data, key))
|
||||
.toThrow ();
|
||||
});
|
||||
|
||||
it ('should throw on invalid decryption key', () => {
|
||||
const data = 'EHsGr2eSVqZKTX1U9Qj4crGRFkk299kmxhiiO3fyaIS'
|
||||
+ 'olB9+UYDdqrwcf/INwNddW4AzjA2Kf4dYaXIRLZsU1Q==';
|
||||
const key = '-----BEGIN PRIVATE KEY-----\n'
|
||||
+ 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAxryi2qtUgfwU86HE\n'
|
||||
+ 'eGahja/Hc9cpccVKQ53ArtXv2sjhj5wopQdCxhHaiqzkZbGKSGg7y7+sepPIkoOg\n'
|
||||
+ 'P07MmwIDAQABAkBKIk3hoi24+07ZfwuqGibDksGlLarxHLZSOMOKsnBXfRRyGqMr\n'
|
||||
+ '/Z+qtQ9VPRWHBzGHZ9rXAVKa8gnRirik+ez5AiEA/XL6tOy92Yvxm46ewswmZ7ab\n'
|
||||
+ 'V1KvChsXziaRj5eLzacCIQDIvLBO8og5Ng4r7E/dOAYrvzOLFqlN5UCuCRZzuFpv\n'
|
||||
+ '7QIgCnj5ywgNQDP8I8Vc4ge1fouZF56fBPfhn+8QDLLiX/kCIHewKd+otJiIJoMB\n'
|
||||
+ '78yTLvq+klkINgKAAsTCHmT5MtMxAiEAkFE70ms8C73JvTkd0znq8H6fBJV0iZxQ\n'
|
||||
+ 'qOXON8bzv8A=\n'
|
||||
+ '-----END PRIVATE KEY-----\n';
|
||||
expect (() => crypto.asym_decrypt (data, key))
|
||||
.toThrow ();
|
||||
});
|
||||
|
||||
it ('should throw on invalid decryption data', () => {
|
||||
const data = 'EHsGr2eSkqZKTX1U9Qj4crGRFkk299kmxhiiO3fyaIS'
|
||||
+ 'olB9+UYDdqrwcf/INwNddW4AzjA2Kf4dYaXIRLZsU1Q==';
|
||||
const key = '-----BEGIN PRIVATE KEY-----\n'
|
||||
+ 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAxryi2qtUgfwU86HE\n'
|
||||
+ 'eGahja/Hc9cpccVKQ53ArtAv2sjhj5wopQdCxhHaiqzkZbGKSGg7y7+sepPIkoOg\n'
|
||||
+ 'P07MmwIDAQABAkBKIk3hoi24+07ZfwuqGibDksGlLarxHLZSOMOKsnBXfRRyGqMr\n'
|
||||
+ '/Z+qtQ9VPRWHBzGHZ9rXAVKa8gnRirik+ez5AiEA/XL6tOy92Yvxm46ewswmZ7ab\n'
|
||||
+ 'V1KvChsXziaRj5eLzacCIQDIvLBO8og5Ng4r7E/dOAYrvzOLFqlN5UCuCRZzuFpv\n'
|
||||
+ '7QIgCnj5ywgNQDP8I8Vc4ge1fouZF56fBPfhn+8QDLLiX/kCIHewKd+otJiIJoMB\n'
|
||||
+ '78yTLvq+klkINgKAAsTCHmT5MtMxAiEAkFE70ms8C73JvTkd0znq8H6fBJV0iZxQ\n'
|
||||
+ 'qOXON8bzv8A=\n'
|
||||
+ '-----END PRIVATE KEY-----\n';
|
||||
expect (() => crypto.asym_decrypt (data, key))
|
||||
.toThrow ();
|
||||
});
|
||||
|
||||
it ('should create a signature and verify it', async () => {
|
||||
const data = 'foobar';
|
||||
const k = await crypto.generate_keypair (key_length);
|
||||
const signature = crypto.asym_sign (data, k.private_key);
|
||||
expect (typeof signature)
|
||||
.toEqual ('string');
|
||||
expect (signature)
|
||||
.toMatch (/^[a-f0-9]+$/ui);
|
||||
expect (crypto.asym_verify (data, k.public_key, signature))
|
||||
.toBeTrue ();
|
||||
});
|
||||
|
||||
it ('should throw on invalid sign key', () => {
|
||||
const key = '-----BEGIN PRIVATE KEY-----\n'
|
||||
+ 'MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAl4RZveQ8IXHPZTUf\n'
|
||||
+ 'djwgDxIB5444yFvhIRasdasdasdApz+FmsXxGCatnPUCIHO9P1EfxasdaIq/Lpng\n'
|
||||
+ 'y2ZQh2IaDYxC7K7tvGZwSY/CEcGfAiAcNgsg5ZMIQOWxn2KbFM81ne3b5FzD3Ozh\n'
|
||||
+ 'GxItcBfKuw==\n'
|
||||
+ '-----END PRIVATE KEY-----\n';
|
||||
const data = 'foobar';
|
||||
expect (() => crypto.asym_sign (data, key))
|
||||
.toThrow ();
|
||||
});
|
||||
|
||||
it ('should throw on invalid verify key', () => {
|
||||
const key = '-----BEGIN RSA PUBLIC KEY-----\n'
|
||||
+ 'MEgCQQCXhFm95DwDcc9lNasdasdasdasdasdHhEqigZupsWUxX/3tKn9tvEKV/\n'
|
||||
+ 'zjoENWN63aorN+O+5MxDZMnEk+z9AgMBAAE=\n'
|
||||
+ '-----END RSA PUBLIC KEY-----\n';
|
||||
const data = 'foobar';
|
||||
const signature = '3433ef165d6be80430e1107be0d7183bee769dbb38ea7d2737'
|
||||
+ '1429c853fcab78bf1d3256fc16ee93a38cfd4ae79a74748e59fe9e9a65400c720d'
|
||||
+ 'adb2dbcc1fa3';
|
||||
expect (() => crypto.asym_verify (data, key, signature))
|
||||
.toThrow ();
|
||||
});
|
||||
|
||||
it ('should not throw on invalid data or signature', () => {
|
||||
const key = '-----BEGIN RSA PUBLIC KEY-----\n'
|
||||
+ 'MEgCQQC+9nw80cG+AsZ2euIZx4ptmUykJgEUgs4JiEvC+IaiIRAd9zGc0TcQAeND\n'
|
||||
+ '171lw77kE02KJ4ARl1hkcLCW2bIrAgMBAAE=\n'
|
||||
+ '-----END RSA PUBLIC KEY-----\n';
|
||||
const data = 'foobar';
|
||||
const signature = '6bae51b0449d71ca2e04099268bd0f6506a5dfc6f810bd72d'
|
||||
+ '47865574a99910e404e5856da650cd45ee88365a2511fcc0866a0b5d1faf15c067'
|
||||
+ 'ab8a4427554bf';
|
||||
expect (crypto.asym_verify (data, key, signature))
|
||||
.toBeTrue ();
|
||||
expect (crypto.asym_verify (data.replace ('b', 'c'), key, signature))
|
||||
.toBeFalse ();
|
||||
expect (crypto.asym_verify (data, key, signature.replace ('f', 'e')))
|
||||
.toBeFalse ();
|
||||
});
|
||||
});
|
@ -1,17 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) Sapphirecode - All Rights Reserved
|
||||
* This file is part of crypto-helper which is released under MIT.
|
||||
* See file 'LICENSE' for full license details.
|
||||
* Created by Timo Hocker <timo@scode.ovh>, May 2020
|
||||
*/
|
||||
|
||||
// @ts-nocheck
|
||||
'use strict';
|
||||
|
||||
const crypto = require ('../../index');
|
||||
const rsa = require ('../../lib/rsa');
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function, max-statements
|
||||
describe ('crypto helper', () => {
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
describe ('signatures', () => {
|
||||
beforeEach (() => {
|
||||
jasmine.clock ()
|
||||
.install ();
|
||||
@ -25,95 +18,6 @@ describe ('crypto helper', () => {
|
||||
.uninstall ();
|
||||
});
|
||||
|
||||
it ('random_hex', () => {
|
||||
const hex = crypto.random_hex (16);
|
||||
expect (hex.length)
|
||||
.toEqual (16);
|
||||
expect (hex)
|
||||
.toMatch (/^[0-9a-f]+$/iu);
|
||||
});
|
||||
|
||||
it ('random_hex with default length', () => {
|
||||
const hex = crypto.random_hex ();
|
||||
expect (hex.length)
|
||||
.toEqual (8);
|
||||
expect (hex)
|
||||
.toMatch (/^[0-9a-f]+$/iu);
|
||||
});
|
||||
|
||||
it ('random_hex should refuse length smaller 1', () => {
|
||||
expect (
|
||||
() => (crypto.random_hex (0))
|
||||
)
|
||||
.toThrowError ('invalid length');
|
||||
});
|
||||
|
||||
it ('random_hex should always return correct length', () => {
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const hex = crypto.random_hex (i);
|
||||
expect (hex.length)
|
||||
.toEqual (i);
|
||||
}
|
||||
});
|
||||
|
||||
it ('random_string', () => {
|
||||
const str = crypto.random_string (16);
|
||||
expect (str.length)
|
||||
.toEqual (16);
|
||||
});
|
||||
|
||||
it ('random_string with default length', () => {
|
||||
const str = crypto.random_string ();
|
||||
expect (str.length)
|
||||
.toEqual (8);
|
||||
});
|
||||
|
||||
it ('random_string should refuse length smaller 1', () => {
|
||||
expect (
|
||||
() => (crypto.random_string (0))
|
||||
)
|
||||
.toThrowError ('invalid length');
|
||||
});
|
||||
|
||||
it ('random_string should always return correct length', () => {
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const str = crypto.random_string (i);
|
||||
expect (str.length)
|
||||
.toEqual (i);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it ('hash_sha512', () => {
|
||||
const hash = crypto.hash_sha512 ('a', 'b');
|
||||
expect (
|
||||
hash
|
||||
)
|
||||
.toEqual (
|
||||
// eslint-disable-next-line max-len
|
||||
'2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d'
|
||||
);
|
||||
});
|
||||
|
||||
it ('checksum', () => {
|
||||
const hash = crypto.checksum ('foo');
|
||||
expect (
|
||||
hash
|
||||
)
|
||||
.toEqual (
|
||||
// eslint-disable-next-line max-len
|
||||
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'
|
||||
);
|
||||
});
|
||||
|
||||
it ('create_salt', () => {
|
||||
const salt = crypto.create_salt ();
|
||||
expect (salt.length)
|
||||
.toEqual (64);
|
||||
expect (salt)
|
||||
.toMatch (/^[0-9a-f]+$/iu);
|
||||
});
|
||||
|
||||
it ('sign_object', () => {
|
||||
const obj = { foo: 'bar' };
|
||||
expect (() => {
|
||||
@ -123,6 +27,59 @@ describe ('crypto helper', () => {
|
||||
}).not.toThrow ();
|
||||
});
|
||||
|
||||
it ('sign_object with rsa key', async () => {
|
||||
const obj = { foo: 'bar' };
|
||||
const k = await rsa.generate_keypair ();
|
||||
expect (() => {
|
||||
const str = crypto.sign_object (obj, k.private_key);
|
||||
expect (typeof str)
|
||||
.toEqual ('string');
|
||||
}).not.toThrow ();
|
||||
});
|
||||
|
||||
it ('verify_signature with rsa key', () => {
|
||||
const obj = { foo: 'bar' };
|
||||
const str = 'U1GcsN3yZzSKxPH8jhCGTKiswfazB9rMfUtE5351LT11t6EmS7xfPjnt'
|
||||
+ '.5ytniC6q2ovoF7ZqbD8qk9r2kjjAcA9EYhLwC3wwJKPPsKdHSTFd7d9TzBP1skQ98X'
|
||||
+ 'LjRUkc2M8M84LmWLg76EvcY2pw6HwsFvCUoZYcKAJp3vkp9MQVrVYdHKMPkBjQKyy2V'
|
||||
+ 'KtEZiBsomBVJd6Hudd1YLMQ4J4s52iHsegDswKE9djYVEmgKkJUAiZJ2viFHw3fBbp2'
|
||||
+ 'Abo2Dm5oqYtw7Nn9RFstW3CcNQHV1PzHDKD56Uw3opuYwVZhQth8ux2CdkC2yMvgVsT'
|
||||
+ 'dUyCuu78ugaGvzsMXCbe2BzaPFDTE9JYtMcDFFP43nUGHNd6cWwzoKTZBX852Exz6Rb'
|
||||
+ 'VjcWUvL81dLPBLJV.2';
|
||||
const key = '-----BEGIN RSA PUBLIC KEY-----\n'
|
||||
+ 'MIIBCgKCAQEA4LCEoJYNwwksuzPESpmPziHp98WhY5Qml6RiN9uxrKGPV6QwwmDQ\n'
|
||||
+ 'ks6C+ZfYbFG9NCx1MEuWL0Tvp/6ZBhMyaJrI5iwo0CmSX3WdFcbXmdl0l6N1+5r7\n'
|
||||
+ 'l3SkKsr/AX4gwcDor4dYuLEv5KawGdfcP0IxsoAcIN1UJ5HJ+eheB3fVcSh/IIBf\n'
|
||||
+ 'O+cL/4Chw8eAaDBG5mZ1Xgd4gIjJGYAxgUNvaShGzs8k1y+jqjD5IkZ1h9dgoGJG\n'
|
||||
+ 'dUmjCLWrOzx8SqdqJYmQJX+6GNswnvVF30bkW+/MJZF/P2jLFtSa24Monh7axIqx\n'
|
||||
+ '8HG0xDw1Z98WV9oQh/vDP/KAs1cPp0AJlwIDAQAB\n'
|
||||
+ '-----END RSA PUBLIC KEY-----\n';
|
||||
const ver = crypto.verify_signature (str, key);
|
||||
expect (ver)
|
||||
.toEqual (obj);
|
||||
});
|
||||
|
||||
it ('verify_signature reject with rsa key', () => {
|
||||
const str = 'U1GcsN3yZzSKxPH8jhCGTKiswfazB9rMfUtE5351LT11t6EmS7xfPjnt'
|
||||
+ '.5ytniC6q2ovoF7ZqbD8qk9r2kjjAcA9EYhLwC3wwJKPPsKdHSTFd7d9TzBP1skQ98X'
|
||||
+ 'LjRUkc2M8M84LmWLg76EvcY2pw6HwsFvCUoZYcKAJp3vkp9MQVrVYdHKMPkBjQKyy2V'
|
||||
+ 'KtEZiBsomBVJd6Hudd1YLMQ4J4s52iHsegDswKE9djYVEmgKkJUAiZJ2viFHw3fBbp2'
|
||||
+ 'Abo2Dm5oqYtw7Nn9RFstW3CcNQHV1PzHDKD56Uw3opuYwVZhQth8ux2CdkC2yMvgVsT'
|
||||
+ 'dUyCuu78ugaGvzsMXCbe2BzaPFDTE9JYtMcDFFP43nUGHNd6cWwzoKTZBX852Exz6Rb'
|
||||
+ 'VjcWUvL81dLPBLJA.2';
|
||||
const key = '-----BEGIN RSA PUBLIC KEY-----\n'
|
||||
+ 'MIIBCgKCAQEA4LCEoJYNwwksuzPESpmPziHp98WhY5Qml6RiN9uxrKGPV6QwwmDQ\n'
|
||||
+ 'ks6C+ZfYbFG9NCx1MEuWL0Tvp/6ZBhMyaJrI5iwo0CmSX3WdFcbXmdl0l6N1+5r7\n'
|
||||
+ 'l3SkKsr/AX4gwcDor4dYuLEv5KawGdfcP0IxsoAcIN1UJ5HJ+eheB3fVcSh/IIBf\n'
|
||||
+ 'O+cL/4Chw8eAaDBG5mZ1Xgd4gIjJGYAxgUNvaShGzs8k1y+jqjD5IkZ1h9dgoGJG\n'
|
||||
+ 'dUmjCLWrOzx8SqdqJYmQJX+6GNswnvVF30bkW+/MJZF/P2jLFtSa24Monh7axIqx\n'
|
||||
+ '8HG0xDw1Z98WV9oQh/vDP/KAs1cPp0AJlwIDAQAB\n'
|
||||
+ '-----END RSA PUBLIC KEY-----\n';
|
||||
const ver = crypto.verify_signature (str, key);
|
||||
expect (ver)
|
||||
.toBeNull ();
|
||||
});
|
||||
|
||||
it ('should sign object with key info', () => {
|
||||
const obj = { foo: 'bar' };
|
||||
expect (() => {
|
Loading…
x
Reference in New Issue
Block a user