diff --git a/README.md b/README.md index ec351703..d485e3c1 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,12 @@ $ npm install @types/node ## Upgrading -This library follows `semver`. As such, major version bumps usually mean API changes or behavior changes. Please check [upgrade notes](https://github.com/yeojz/otplib/wiki/upgrade-notes) for more information, especially before making any major upgrades. +This library follows `semver`. As such, major version bumps usually mean API changes or behavior changes. +Please check [upgrade notes](https://github.com/yeojz/otplib/wiki/upgrade-notes) for more information, +especially before making any major upgrades. -You might also want to check out the release notes associated with each tagged versions in the [releases](https://github.com/yeojz/otplib/releases) page. +You might also want to check out the release notes associated with each tagged versions +in the [releases](https://github.com/yeojz/otplib/releases) page. ## Getting Started @@ -112,7 +115,7 @@ import totp from 'otplib/totp'; import authenticator from 'otplib/authenticator'; ``` -**Note**: If you import the libraries directly, you'll have to provide a crypto +**Note**: If you import the libraries directly, you will have to provide a crypto solution (this is to allow custom crypto solutions), as long as they implement `createHmac` and `randomBytes`. Take a look at the [browser implementation](https://github.com/yeojz/otplib/blob/master/packages/otplib-browser) of this package as an example. @@ -148,6 +151,12 @@ const TOTP = totp.TOTP; import authenticator from 'otplib/authenticator'; const Authenticator = authenticator.Authenticator; // const inst = new Authenticator(); + +// Alternatively, you can get it from the default module as well +import otplib from 'otplib'; +const HOTP = otplib.hotp.HOTP +const TOTP = otplib.totp.TOTP +const Authenticator = otplib.authenticator.Authenticator ``` **Example Usage** @@ -227,12 +236,15 @@ Ihis library been split and classified into 6 core files with other specific env ### Other Bundles -| file | description | -| ---------------------------------------------------------------------------------------- | --------------------------------------------- | -| [otplib-browser.js](https://yeojz.github.io/otplib/docs/module-otplib-browser.html.html) | Browser compatible package built with webpack | +| file | description | +| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [otplib-browser.js](https://yeojz.github.io/otplib/docs/module-otplib-browser.html) | Browser compatible package built with webpack | +| [otplib-expo.js](https://yeojz.github.io/otplib/docs/module-otplib-expo.html) (alpha) | Package modified for `expo.io`, built with webpack ([read more](https://github.com/yeojz/otplib/pull/18)).
_Note_: I have **not** personally tested the `otplib-expo` module.
If the main library works in newer expo releases, I would recommend that. | For more information about the functions, check out the [documentation][project-docs]. + + ## Notes ### Setting Custom Options @@ -262,22 +274,16 @@ otplib.authenticator.resetOptions(); #### Available Options -| Option | Type | Defaults | Description | -| ---------------- | ---------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| algorithm | string | 'sha1' | Algorithm used for HMAC | -| createHmacSecret | function | hotpSecret, totpSecret | Transforms the secret and applies any modifications like padding to it. | -| crypto | object | node crypto | Crypto module to use. | -| digits | integer | 6 | The length of the token | -| encoding | string | 'ascii' ('hex' for Authenticator) | The encoding of secret which is given to digest | -| epoch (totp) | integer | null | Starting time since the UNIX epoch (seconds). | -| step (totp) | integer | 30 | Time step (seconds) | -| window (totp) | integer or array | 0 | Tokens in the previous and future x-windows that should be considered valid. If integer, same value will be used for both. Alternatively, define array: `[previous, future]` | - -_Note 1_: epoch format is non-javascript. i.e. `Date.now() / 1000` - -_Note 2_: non "totp" label applies to all - -_Note 3_: "totp" applies to authenticator as well +| Option | Type | Defaults | Description | +| ---------------------------- | ---------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| algorithm | string | 'sha1' | Algorithm used for HMAC | +| createHmacSecret | function | hotpSecret, totpSecret | Transforms the secret and applies any modifications like padding to it. | +| crypto | object | node crypto | Crypto module to use. | +| digits | integer | 6 | The length of the token | +| encoding | string | 'ascii' ('hex' for authenticator) | The encoding of secret which is given to digest | +| epoch (totp, authenticator) | integer | null | Starting time since the UNIX epoch (seconds).
epoch format is non-javascript. i.e. `Date.now() / 1000` | +| step (totp, authenticator) | integer | 30 | Time step (seconds) | +| window (totp, authenticator) | integer or array | 0 | Tokens in the previous and future x-windows that should be considered valid.
If integer, same value will be used for both.
Alternatively, define array: `[previous, future]` | ### Seed / secret length @@ -289,7 +295,7 @@ HMAC-SHA256 - 32 bytes HMAC-SHA512 - 64 bytes ``` -As such, the length of the secret is padded and sliced according to the expected length for respective algrorithms. +As such, the length of the secret is padded and sliced according to the expected length for respective algorithms. ### Google Authenticator @@ -302,7 +308,8 @@ The default encoding option has been set to `hex` (Authenticator) instead of `as Google Authenticator requires keys to be base32 encoded. It also requires the base32 encoder to be [RFC 3548][rfc-3548] compliant. -OTP calculation will still work should you want to use other base32 encoding methods (like Crockford's Base 32) but it will NOT be compatible with Google Authenticator. +OTP calculation will still work should you want to use other base32 encoding methods (like Crockford's Base 32) +but it will NOT be compatible with Google Authenticator. ```js import authenticator from 'otplib/authenticator'; diff --git a/build.config.js b/build.config.js index 73eb3e14..07e1919b 100644 --- a/build.config.js +++ b/build.config.js @@ -4,6 +4,12 @@ const browsers = { } }; +const nodejs = { + targets: { + node: '6' + } +}; + module.exports = { otplib: { bundler: 'rollup', @@ -42,5 +48,11 @@ module.exports = { babel: { presets: [['env', browsers]] } + }, + 'otplib-expo': { + bundler: 'webpack', + babel: { + presets: [['env', nodejs]] + } } }; diff --git a/packages/otplib-browser/randomBytes.js b/packages/otplib-browser/randomBytes.js index 91675b65..71c01d36 100644 --- a/packages/otplib-browser/randomBytes.js +++ b/packages/otplib-browser/randomBytes.js @@ -7,7 +7,7 @@ * * @module otplib-browser/randomBytes * @param {string} size - the size - * @return {string} + * @return {Buffer} */ function randomBytes(size) { const crypto = window.crypto || window.msCrypto; diff --git a/packages/otplib-expo/crypto.js b/packages/otplib-expo/crypto.js new file mode 100644 index 00000000..00badbfd --- /dev/null +++ b/packages/otplib-expo/crypto.js @@ -0,0 +1,12 @@ +import crypto from 'crypto'; +import randomBytes from './randomBytes'; + +/** + * Crypto replacement for expo + * + * @module otplib-expo/crypto + */ +export default { + createHmac: crypto.createHmac, + randomBytes +}; diff --git a/packages/otplib-expo/crypto.spec.js b/packages/otplib-expo/crypto.spec.js new file mode 100644 index 00000000..8a69e541 --- /dev/null +++ b/packages/otplib-expo/crypto.spec.js @@ -0,0 +1,9 @@ +import crypto from './crypto'; + +describe('crypto', () => { + it('should expose an object with a used method', () => { + expect(typeof crypto).toBe('object'); + expect(typeof crypto.createHmac).toBe('function'); + expect(typeof crypto.randomBytes).toBe('function'); + }); +}); diff --git a/packages/otplib-expo/index.js b/packages/otplib-expo/index.js new file mode 100644 index 00000000..f5606888 --- /dev/null +++ b/packages/otplib-expo/index.js @@ -0,0 +1,22 @@ +import hotp from 'otplib-hotp'; +import totp from 'otplib-totp'; +import authenticator from 'otplib-authenticator'; +import crypto from './crypto'; + +/** + * otplib-expo + * + * One-Time Password Library for use with https://expo.io + * + * @module otplib-expo + * @since 10.0.0 + */ +authenticator.options = { crypto }; +hotp.options = { crypto }; +totp.options = { crypto }; + +module.exports = { + authenticator, + hotp, + totp +}; diff --git a/packages/otplib-expo/randomBytes.js b/packages/otplib-expo/randomBytes.js new file mode 100644 index 00000000..3db0b8dc --- /dev/null +++ b/packages/otplib-expo/randomBytes.js @@ -0,0 +1,31 @@ +/** + * randomBytes naive implementation. + * + * Required for expo applications that don't offer a native + * secure random implementaton + * + * @module otplib-expo/randomBytes + * @param {string} size - the size + * @return {Buffer} + */ +function randomBytes(size) { + if (size > 65536) { + throw new Error('Requested size of random bytes is too large'); + } + + if (size < 1) { + throw new Error('Requested size must be more than 0'); + } + + const rawBytes = new Uint8Array(size).map(() => randomUint8()); + + return new Buffer(rawBytes.buffer); +} + +function randomUint8() { + const low = 0; + const high = 255; + return Math.floor(Math.random() * (high - low + 1) + low); +} + +export default randomBytes; diff --git a/packages/otplib-expo/randomBytes.spec.js b/packages/otplib-expo/randomBytes.spec.js new file mode 100644 index 00000000..91ad3b38 --- /dev/null +++ b/packages/otplib-expo/randomBytes.spec.js @@ -0,0 +1,20 @@ +import randomBytes from './randomBytes'; + +describe('randomBytes', () => { + const tooLarge = 'Requested size of random bytes is too large'; + const wrongSize = 'Requested size must be more than 0'; + + it('should return a buffer', () => { + const result = randomBytes(10); + expect(result).toBeInstanceOf(Buffer); + expect(result).toHaveLength(10); + }); + + it('should throw when size is too big', () => { + expect(() => randomBytes(65537)).toThrowError(tooLarge); + }); + + it('should throw when size is < 1', () => { + expect(() => randomBytes(0)).toThrowError(wrongSize); + }); +});