supercop.ts

cross-compiled javascript implementation of ed25519 based on supercop-ref10
git clone git://git.finwo.net/lib/supercop.ts
Log | Files | Refs | README | LICENSE

commit a44709b5372375d18a77524d3779f739ef59efd7
parent 84d677e19f28f50100e7712fe4bcaeaa1b38967e
Author: finwo <finwo@pm.me>
Date:   Wed, 19 Jun 2019 12:30:26 +0200

Helper functions are now tested as well

Diffstat:
Mindex.js | 62+++++++++++++++++++++++++++++++++++++++++++++++---------------
Mtest.js | 130++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 146 insertions(+), 46 deletions(-)

diff --git a/index.js b/index.js @@ -5,14 +5,48 @@ function randomBytes(length) { return Buffer.from(new Array(length).fill(0).map(()=>Math.floor(Math.random()*256))); } +function checkArguments( namedArguments, callback ) { + callback = callback || function( err ) { + if (!err) return; + if (err instanceof Error) throw err; + throw new Error(err); + }; + + if ('object' !== typeof namedArguments) return callback('Expected object, ' + (typeof namedArguments) + ' given'); + if (!namedArguments) return callback('Expected object, null given'); + if ( 'seed' in namedArguments ) { + if (!isBuffer(namedArguments.seed) ) return callback('Seed is not a buffer'); + if (namedArguments.seed.length !== 32) return callback('Seed must be 32 bytes'); + } + if ( 'signature' in namedArguments ) { + if (!isBuffer(namedArguments.signature)) return callback('Signature is not a buffer'); + if (namedArguments.signature.length !== 64) return callback('Signature must be 64 bytes'); + } + if ( 'message' in namedArguments ) { + if (!isBuffer(namedArguments.message)) return callback('Message is not a buffer'); + } + if ( 'publicKey' in namedArguments ) { + if (!isBuffer(namedArguments.publicKey) ) return callback('Public key is not a buffer'); + if (namedArguments.publicKey.length !== 32) return callback('Public key must be 32 bytes'); + } + if ( 'secretKey' in namedArguments ) { + if (!isBuffer(namedArguments.secretKey) ) return callback('Secret key is not a buffer'); + if (namedArguments.secretKey.length !== 64) return callback('Secret key must be 64 bytes'); + } + + return callback(); +} + +// Export helpers +exports._checkArguments = checkArguments; +exports._randomBytes = randomBytes; + exports.createSeed = function(){ return randomBytes(32); }; exports.createKeyPair = function(seed) { - if(!Buffer.isBuffer(seed)){ - throw new Error('not buffers!'); - } + checkArguments({seed}); var seedPtr = Module._malloc(32); var seedBuf = new Uint8Array(Module.HEAPU8.buffer, seedPtr, 32); var publicKeyPtr = Module._malloc(32); @@ -31,9 +65,8 @@ exports.createKeyPair = function(seed) { }; exports.sign = function(message, publicKey, secretKey){ - if(!Buffer.isBuffer(message) || !Buffer.isBuffer(publicKey) || !Buffer.isBuffer(secretKey)){ - throw new Error('not buffers!'); - } + if ('string' === typeof message) message = Buffer.from(message); + checkArguments({message,publicKey,secretKey}); var messageLen = message.length; var messageArrPtr = Module._malloc(messageLen); var messageArr = new Uint8Array(Module.HEAPU8.buffer, messageArrPtr, messageLen); @@ -54,23 +87,22 @@ exports.sign = function(message, publicKey, secretKey){ return new Buffer(sig); }; -exports.verify = function(sig, message, publicKey){ - if(!Buffer.isBuffer(message) || !Buffer.isBuffer(sig) || !Buffer.isBuffer(publicKey)){ - throw new Error('not buffers!'); - } +exports.verify = function(signature, message, publicKey){ + if ('string' === typeof message) message = Buffer.from(message); + checkArguments({signature,message,publicKey}); var messageLen = message.length; var messageArrPtr = Module._malloc(messageLen); var messageArr = new Uint8Array(Module.HEAPU8.buffer, messageArrPtr, messageLen); - var sigArrPtr = Module._malloc(64); - var sigArr = new Uint8Array(Module.HEAPU8.buffer, sigArrPtr, 64); + var signatureArrPtr = Module._malloc(64); + var signatureArr = new Uint8Array(Module.HEAPU8.buffer, signatureArrPtr, 64); var publicKeyArrPtr = Module._malloc(32); var publicKeyArr = new Uint8Array(Module.HEAPU8.buffer, publicKeyArrPtr, 32); messageArr.set(message); - sigArr.set(sig); + signatureArr.set(signature); publicKeyArr.set(publicKey); - var res = Module._verify(sigArrPtr, messageArrPtr, messageLen, publicKeyArrPtr) === 1; + var res = Module._verify(signatureArrPtr, messageArrPtr, messageLen, publicKeyArrPtr) === 1; Module._free(messageArrPtr); - Module._free(sigArrPtr); + Module._free(signatureArrPtr); Module._free(publicKeyArrPtr); return res; }; diff --git a/test.js b/test.js @@ -1,41 +1,109 @@ -var test = require('tape'); -var lib = require('./index.js'); -var crypto = require('crypto'); +const isBuffer = require('is-buffer'); +const test = require('tape'); +const lib = require('./index.js'); -test('key generation', function(t){ +test('Type checks', t => { + t.plan(7); + t.is((!!lib) && (typeof lib) , 'object' , 'supercop is an object' ); + t.is(typeof lib._randomBytes , 'function', 'export helper: _randomBytes' ); + t.is(typeof lib._checkArguments, 'function', 'export helper: _checkArguments'); + t.is(typeof lib.createSeed , 'function', 'export method: createSeed' ); + t.is(typeof lib.createKeyPair , 'function', 'export method: createKeyPair' ); + t.is(typeof lib.sign , 'function', 'export method: sign' ); + t.is(typeof lib.verify , 'function', 'export method: verify' ); +}); + +test('Random bytes generation', t => { + t.plan(4); + + const randomBytes32 = lib._randomBytes(32); + const randomBytes64 = lib._randomBytes(64); + + t.is(isBuffer(randomBytes32), true, 'Random32 is a buffer'); + t.is(isBuffer(randomBytes64), true, 'Random64 is a buffer'); + t.is(randomBytes32.length , 32 , 'Random32 is 32 bytes long'); + t.is(randomBytes64.length , 64 , 'Random64 is 64 bytes long'); +}); + +test('Argument validation', t => { + t.plan(14); + + // We'll not throw in this test + const callback = function(err) { + if (!err) return false; + if (err instanceof Error) return err.message; + return err; + }; + + // Variables to test with + const undef = undefined; + const rightSeed = lib._randomBytes(32); + const wrongSeed = lib._randomBytes(33); + const rightPublicKey = lib._randomBytes(32); + const wrongPublicKey = lib._randomBytes(33); + const rightSecretKey = lib._randomBytes(64); + const wrongSecretKey = lib._randomBytes(65); + const randomMessage = lib._randomBytes(1024); + + t.is(lib._checkArguments(null , callback), 'Expected object, null given' , 'Failure on null argument'); + t.is(lib._checkArguments('some string' , callback), 'Expected object, string given' , 'Failure on string argument'); + t.is(lib._checkArguments(callback , callback), 'Expected object, function given', 'Failure on function argument'); + t.is(lib._checkArguments({ seed : undef }, callback), 'Seed is not a buffer' , 'Failure on undefined seed'); + t.is(lib._checkArguments({ seed : rightSeed }, callback), false , 'Success on 32-byte seed'); + t.is(lib._checkArguments({ seed : wrongSeed }, callback), 'Seed must be 32 bytes' , 'Failure on 33-byte seed'); + t.is(lib._checkArguments({ publicKey: undef }, callback), 'Public key is not a buffer' , 'Failure on undefined public key'); + t.is(lib._checkArguments({ publicKey: rightPublicKey }, callback), false , 'Success on 32-byte public key'); + t.is(lib._checkArguments({ publicKey: wrongPublicKey }, callback), 'Public key must be 32 bytes' , 'Failure on 33-byte public key'); + t.is(lib._checkArguments({ secretKey: undef }, callback), 'Secret key is not a buffer' , 'Failure on undefined secret key'); + t.is(lib._checkArguments({ secretKey: rightSecretKey }, callback), false , 'Success on 64-byte secret key'); + t.is(lib._checkArguments({ secretKey: wrongSecretKey }, callback), 'Secret key must be 64 bytes' , 'Failure on 65-byte secret key'); + t.is(lib._checkArguments({ message : undef }, callback), 'Message is not a buffer' , 'Failure on undefined message'); + t.is(lib._checkArguments({ message : randomMessage }, callback), false , 'Success on buffer message'); +}); + +test('Key generation', t => { t.plan(6); - var seed = lib.createSeed(); - t.is(Buffer.isBuffer(seed), true, 'seed is a buffer'); - t.is(seed.length, 32, 'seed\'s length is 32'); - var keys = lib.createKeyPair(seed); - t.is(Buffer.isBuffer(keys.publicKey), true, 'pub key is a buffer'); - t.is(keys.publicKey.length, 32, 'pub key\'s length is 32'); - t.is(Buffer.isBuffer(keys.secretKey), true, 'priv key is a buffer'); - t.is(keys.secretKey.length, 64, 'priv key\'s length is 64'); + const seed = lib.createSeed(); + t.is(isBuffer(seed), true, 'Seed is a buffer' ); + t.is(seed.length , 32 , 'Seed\'s length is 32'); + + const keys = lib.createKeyPair(seed); + t.is(isBuffer(keys.publicKey), true, 'Public key is a buffer' ); + t.is(keys.publicKey.length , 32 , 'Public key\'s length is 32'); + t.is(isBuffer(keys.secretKey), true, 'Secret key is a buffer' ); + t.is(keys.secretKey.length , 64 , 'Secret key\'s length is 64'); }); -test('signatures', function(t){ +test('Signatures', t => { t.plan(2); - var seed = lib.createSeed(); - var keys = lib.createKeyPair(seed); - var sig = lib.sign(new Buffer('hello there m8'), keys.publicKey, keys.secretKey); - t.is(Buffer.isBuffer(sig), true, 'is signature buffer'); - t.is(sig.length, 64, 'is signature\'s length 64'); + const seed = lib.createSeed(); + const keys = lib.createKeyPair(seed); + const signature = lib.sign(new Buffer('hello there m8'), keys.publicKey, keys.secretKey); + + t.is(isBuffer(signature), true, 'Signature is a buffer'); + t.is(signature.length , 64 , 'Signature\'s length is 64 bytes'); }); -test('verify', function(t){ - t.plan(3); - - var seed = lib.createSeed(); - var keys = lib.createKeyPair(seed); - var msg = new Buffer('hello there m8'); - var wrongMsg = crypto.randomBytes(msg.length); - var sig = lib.sign(msg, keys.publicKey, keys.secretKey); - var wrongSeed = lib.createSeed(); - var wrongKeys = lib.createKeyPair(wrongSeed); - t.is(lib.verify(sig, msg, keys.publicKey), true, 'right stuff verifies correctly'); - t.is(lib.verify(sig, wrongMsg, keys.publicKey), false, 'wrong message is incorrect'); - t.is(lib.verify(sig, msg, wrongKeys.publicKey), false, 'wrong key is incorrect'); +test('Verify', t => { + t.plan(4); + + const seed = lib.createSeed(); + const keys = lib.createKeyPair(seed); + const messageBuf = new Buffer('hello there m8'); + const messageStr = 'hello there m8'; + const wrongMessage = lib._randomBytes(messageBuf.length); + + const signatureBuf = lib.sign(messageBuf, keys.publicKey, keys.secretKey); + const signatureStr = lib.sign(messageStr, keys.publicKey, keys.secretKey); + + t.is(Buffer.compare(signatureBuf, signatureStr), 0, 'String and buffer sourced signature match'); + + const wrongSeed = lib.createSeed(); + const wrongKeys = lib.createKeyPair(wrongSeed); + + t.is(lib.verify(signatureBuf, messageBuf, keys.publicKey) , true , 'Signature verified message'); + t.is(lib.verify(signatureBuf, wrongMessage, keys.publicKey) , false, 'Different messaged does not verify'); + t.is(lib.verify(signatureBuf, messageBuf, wrongKeys.publicKey), false, 'Different public key does not verify'); });