pax_global_header 0000666 0000000 0000000 00000000064 15116526455 0014524 g ustar 00root root 0000000 0000000 52 comment=4454f9805562fbc4b3d3c697aaddafc8100b4ca5
hapijs-cryptiles-39293a6/ 0000775 0000000 0000000 00000000000 15116526455 0015300 5 ustar 00root root 0000000 0000000 hapijs-cryptiles-39293a6/.github/ 0000775 0000000 0000000 00000000000 15116526455 0016640 5 ustar 00root root 0000000 0000000 hapijs-cryptiles-39293a6/.github/workflows/ 0000775 0000000 0000000 00000000000 15116526455 0020675 5 ustar 00root root 0000000 0000000 hapijs-cryptiles-39293a6/.github/workflows/ci-module.yml 0000664 0000000 0000000 00000000314 15116526455 0023274 0 ustar 00root root 0000000 0000000 name: ci
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
jobs:
test:
uses: hapijs/.github/.github/workflows/ci-module.yml@master
with:
min-node-version: 14
hapijs-cryptiles-39293a6/.gitignore 0000664 0000000 0000000 00000000154 15116526455 0017270 0 ustar 00root root 0000000 0000000 **/node_modules
**/package-lock.json
coverage.*
**/.DS_Store
**/._*
**/*.pem
**/.vs
**/.vscode
**/.idea
hapijs-cryptiles-39293a6/API.md 0000775 0000000 0000000 00000001674 15116526455 0016246 0 ustar 00root root 0000000 0000000
## Methods
### `randomString(size: number): string`
Returns a cryptographically strong pseudo-random data string. Takes a size argument for the length of the string.
### `randomAlphanumString(size: number): string`
Returns a cryptographically strong pseudo-random alphanumeric data string. Takes a size argument for the length of the string.
### `randomDigits(size: number): string`
Returns a cryptographically strong pseudo-random data string consisting of only numerical digits (0-9). Takes a size argument for the length of the string.
### `randomBits(bits: number): Buffer`
Returns a Buffer of cryptographically strong pseudo-random bits. Takes a bits argument for the number of bits to generate.
### `fixedTimeComparison(a: string, b: string): boolean`
Performs a constant-time comparison of two strings to prevent timing attacks. Returns `true` if the strings are equal, `false` otherwise. Safe to use with strings of different lengths.
hapijs-cryptiles-39293a6/LICENSE.md 0000775 0000000 0000000 00000002717 15116526455 0016716 0 ustar 00root root 0000000 0000000 Copyright (c) 2014-2022, Project contributors
Copyright (c) 2014-2020, Sideway Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
hapijs-cryptiles-39293a6/README.md 0000775 0000000 0000000 00000002023 15116526455 0016557 0 ustar 00root root 0000000 0000000
# @hapi/cryptiles
#### General purpose crypto utilities.
**cryptiles** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together.
### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support
## Useful resources
- [Documentation and API](https://hapi.dev/family/cryptiles/)
- [Versions status](https://hapi.dev/resources/status/#cryptiles) (builds, dependencies, node versions, licenses, eol)
- [Changelog](https://hapi.dev/family/cryptiles/changelog/)
- [Project policies](https://hapi.dev/policies/)
- [Free and commercial support options](https://hapi.dev/support/)
hapijs-cryptiles-39293a6/lib/ 0000775 0000000 0000000 00000000000 15116526455 0016046 5 ustar 00root root 0000000 0000000 hapijs-cryptiles-39293a6/lib/index.d.ts 0000775 0000000 0000000 00000001746 15116526455 0017762 0 ustar 00root root 0000000 0000000 /**
Generate a cryptographically strong pseudo-random data
@param size - Size of the string
@returns A cryptographically strong pseudo-random data
*/
export function randomString(size: number): string;
/**
Generate a cryptographically strong pseudo-random alphanumeric data
@param size - Size of the string
@returns A cryptographically strong pseudo-random alphanumeric data
*/
export function randomAlphanumString(size: number): string;
/**
Return a random string of digits
@param size - Size of the digits
@returns A random string of digits
*/
export function randomDigits(size: number): string;
/**
Generate a buffer of random bits
@param bits - Number of bits
@returns A buffer of random bits
*/
export function randomBits(bits: number): Buffer;
/**
Generate a buffer of random bits
@param a - Data to compare
@param b - Data to compare
@returns A boolean comparing a and b
*/
export function fixedTimeComparison(a: string | Array, b: string | Array): boolean;
hapijs-cryptiles-39293a6/lib/index.js 0000775 0000000 0000000 00000003656 15116526455 0017530 0 ustar 00root root 0000000 0000000 'use strict';
const Crypto = require('crypto');
const Boom = require('@hapi/boom');
const internals = {};
// Generate a cryptographically strong pseudo-random data
exports.randomString = function (size) {
const buffer = exports.randomBits((size + 1) * 6);
const string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
return string.slice(0, size);
};
// Generate a cryptographically strong pseudo-random alphanum data
exports.randomAlphanumString = function (size) {
let result = '';
while (result.length < size) {
const buffer = exports.randomBits((size + 1) * 6);
result += buffer.toString('base64').replace(/[^a-zA-Z0-9]/g, '');
}
return result.slice(0, size);
};
// Return a random string of digits
exports.randomDigits = function (size) {
const digits = [];
let buffer = internals.random(size * 2); // Provision twice the amount of bytes needed to increase chance of single pass
let pos = 0;
while (digits.length < size) {
if (pos >= buffer.length) {
buffer = internals.random(size * 2);
pos = 0;
}
if (buffer[pos] < 250) {
digits.push(buffer[pos] % 10);
}
++pos;
}
return digits.join('');
};
// Generate a buffer of random bits
exports.randomBits = function (bits) {
if (!bits ||
bits < 0) {
throw Boom.internal('Invalid random bits count');
}
const bytes = Math.ceil(bits / 8);
return internals.random(bytes);
};
exports.fixedTimeComparison = function (a, b) {
try {
return Crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
}
catch (err) {
return false;
}
};
internals.random = function (bytes) {
try {
return Crypto.randomBytes(bytes);
}
catch (err) {
throw Boom.internal('Failed generating random bits: ' + err.message);
}
};
hapijs-cryptiles-39293a6/package.json 0000775 0000000 0000000 00000001505 15116526455 0017572 0 ustar 00root root 0000000 0000000 {
"name": "@hapi/cryptiles",
"description": "General purpose crypto utilities",
"version": "6.0.3",
"repository": "git://github.com/hapijs/cryptiles",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"engines": {
"node": ">=14.0.0"
},
"files": [
"lib"
],
"keywords": [
"cryptography",
"security",
"utilites"
],
"eslintConfig": {
"extends": [
"plugin:@hapi/module"
]
},
"dependencies": {
"@hapi/boom": "^10.0.1"
},
"devDependencies": {
"@hapi/code": "^9.0.3",
"@hapi/eslint-plugin": "^6.0.0",
"@hapi/lab": "^25.1.2",
"@types/node": "^17.0.31",
"typescript": "~4.6.4"
},
"scripts": {
"test": "lab -a @hapi/code -t 100 -L -Y",
"test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html"
},
"license": "BSD-3-Clause"
}
hapijs-cryptiles-39293a6/test/ 0000775 0000000 0000000 00000000000 15116526455 0016257 5 ustar 00root root 0000000 0000000 hapijs-cryptiles-39293a6/test/index.js 0000775 0000000 0000000 00000011116 15116526455 0017727 0 ustar 00root root 0000000 0000000 'use strict';
const Code = require('@hapi/code');
const Cryptiles = require('..');
const Lab = require('@hapi/lab');
const internals = {};
const { describe, it } = exports.lab = Lab.script();
const expect = Code.expect;
describe('randomString()', () => {
it('should generate the right length string', () => {
for (let i = 1; i <= 1000; ++i) {
expect(Cryptiles.randomString(i).length).to.equal(i);
}
});
it('returns an error on invalid bits size', () => {
expect(() => Cryptiles.randomString(99999999999999999999)).to.throw(/Failed generating random bits/);
});
});
describe('randomAlphanumString()', () => {
it('should generate the right length string', () => {
for (let i = 1; i <= 1000; ++i) {
const string = Cryptiles.randomAlphanumString(i);
expect(string.length).to.equal(i);
expect(string).to.match(/^[a-zA-Z0-9]+$/);
}
});
it('returns an error on invalid bits size', () => {
expect(() => Cryptiles.randomAlphanumString(99999999999999999999)).to.throw(/Failed generating random bits/);
});
});
describe('randomDigits()', () => {
it('should generate the right length string', () => {
for (let i = 1; i <= 1000; ++i) {
const string = Cryptiles.randomDigits(i);
expect(string.length).to.equal(i);
expect(string).to.match(/^\d+$/);
}
});
it('returns an error on invalid bits size', () => {
expect(() => Cryptiles.randomDigits(99999999999999999999)).to.throw(/Failed generating random bits/);
});
it('generates equal digits distribution', { timeout: 30000 }, () => {
const digits = { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0 };
for (let i = 0; i < 1000000; ++i) {
digits[Cryptiles.randomDigits(1)] += 1;
}
for (const digit in digits) {
expect(digits[digit]).to.be.between(99000, 101000);
}
});
});
describe('randomBits()', () => {
it('returns an error on invalid input', () => {
expect(() => Cryptiles.randomBits(0)).to.throw('Invalid random bits count');
expect(() => Cryptiles.randomBits(-1)).to.throw('Invalid random bits count');
});
});
describe('fixedTimeComparison()', () => {
it('validates strings', () => {
expect(Cryptiles.fixedTimeComparison('asdasd', 'asdasd')).to.be.true();
expect(Cryptiles.fixedTimeComparison('', '')).to.be.true();
expect(Cryptiles.fixedTimeComparison('asdas', 'asdasd')).to.be.false();
});
it('should not throw if buffer size differs', () => {
expect(() => Cryptiles.fixedTimeComparison('a', 'ab')).to.not.throw();
expect(() => Cryptiles.fixedTimeComparison('abc', 'a')).to.not.throw();
expect(() => Cryptiles.fixedTimeComparison('', 'a')).to.not.throw();
expect(() => Cryptiles.fixedTimeComparison('a', '')).to.not.throw();
});
it('should provide constant time regardless of the size of the right-most argument', { timeout: 10000 }, () => {
// Test that comparison time is based on left argument, not right
// When lengths differ, we compare left to itself (constant time based on left)
const largeLeft = 'a'.repeat(100000);
const smallLeft = 'b'.repeat(10);
const smallRight = 'x'.repeat(10);
const largeRight = 'y'.repeat(100000);
const iterations = 10000;
// Warm up
for (let i = 0; i < 1000; ++i) {
Cryptiles.fixedTimeComparison(largeLeft, smallRight);
Cryptiles.fixedTimeComparison(smallLeft, largeRight);
}
// Measure large left + small right (timing should be based on large left)
const startLargeLeft = process.hrtime.bigint();
for (let i = 0; i < iterations; ++i) {
Cryptiles.fixedTimeComparison(largeLeft, smallRight);
}
const endLargeLeft = process.hrtime.bigint();
const largeLeftTime = Number(endLargeLeft - startLargeLeft);
// Measure small left + large right (timing should be based on small left)
const startSmallLeft = process.hrtime.bigint();
for (let i = 0; i < iterations; ++i) {
Cryptiles.fixedTimeComparison(smallLeft, largeRight);
}
const endSmallLeft = process.hrtime.bigint();
const smallLeftTime = Number(endSmallLeft - startSmallLeft);
// Large left should take longer than small left, proving timing is based on left
// Even though small left has a much larger right argument
expect(largeLeftTime).to.be.above(smallLeftTime);
});
});
hapijs-cryptiles-39293a6/test/index.ts 0000775 0000000 0000000 00000003512 15116526455 0017742 0 ustar 00root root 0000000 0000000 import * as Cryptiles from '..';
import * as Lab from '@hapi/lab';
const { expect } = Lab.types;
// randomString()
Cryptiles.randomString(256);
Cryptiles.randomString(5 * 5);
expect.type(Cryptiles.randomString(128))
expect.error(Cryptiles.randomString('some'));
expect.error(Cryptiles.randomString(true));
expect.error(Cryptiles.randomString({ foo: true }));
expect.error(Cryptiles.randomString(128, 256));
// randomAlphanumString()
Cryptiles.randomAlphanumString(256);
Cryptiles.randomAlphanumString(5 * 5);
expect.type(Cryptiles.randomAlphanumString(128))
expect.error(Cryptiles.randomAlphanumString('some'));
expect.error(Cryptiles.randomAlphanumString(true));
expect.error(Cryptiles.randomAlphanumString({ foo: true }));
expect.error(Cryptiles.randomAlphanumString(128, 256));
// randomDigits()
Cryptiles.randomDigits(256);
Cryptiles.randomDigits(5 * 5);
expect.type(Cryptiles.randomDigits(128))
expect.error(Cryptiles.randomDigits('some'));
expect.error(Cryptiles.randomDigits(true));
expect.error(Cryptiles.randomDigits({ foo: true }));
expect.error(Cryptiles.randomDigits(128, 256));
// randomBits()
Cryptiles.randomBits(256);
Cryptiles.randomBits(5 * 5);
expect.type(Cryptiles.randomBits(128))
expect.error(Cryptiles.randomBits('some'));
expect.error(Cryptiles.randomBits(true));
expect.error(Cryptiles.randomBits({ foo: true }));
expect.error(Cryptiles.randomBits(128, 256));
// fixedTimeComparison()
Cryptiles.fixedTimeComparison(["foo"], ["bar"]);
Cryptiles.fixedTimeComparison("foo", "bar");
Cryptiles.fixedTimeComparison("foo", ["foo"]);
expect.type(Cryptiles.fixedTimeComparison("foo", "foo"))
expect.error(Cryptiles.fixedTimeComparison('foo', 24));
expect.error(Cryptiles.fixedTimeComparison({ foo: "bar" }, "foo"));
expect.error(Cryptiles.fixedTimeComparison("foo", "bar", "foo"));