GithubHelp home page GithubHelp logo

guyht / notp Goto Github PK

View Code? Open in Web Editor NEW
684.0 18.0 64.0 352 KB

Node One Time Password library, supports HOTP, TOTP and works with Google Authenticator

Home Page: https://github.com/guyht/notp

License: MIT License

JavaScript 100.00%

notp's Introduction

Build Status

Node One Time Password library

Simple to use, fast, and with zero dependencies. The Node One Time Password library is fully compliant with HOTP (counter based one time passwords) and TOTP (time based one time passwords). It can be used in conjunction with the Google Authenticator which has free apps for iOS, Android and BlackBerry.

Installation

npm install notp

Usage

var notp = require('notp');

//.... some initial login code, that receives the user details and TOTP / HOTP token

var key = 'secret key for user... could be stored in DB';
var token = 'user supplied one time use token';

// Check TOTP is correct (HOTP if hotp pass type)
var login = notp.totp.verify(token, key);

// invalid token if login is null
if (!login) {
    return console.log('Token invalid');
}

// valid token
console.log('Token valid, sync value is %s', login.delta);

Google Authenticator

Google authenticator requires that keys be base32 encoded before being used. This includes manual entry into the app as well as preparing a QR code URI.

To base32 encode a utf8 key you can use the thirty-two module.

var base32 = require('thirty-two');

var key = 'secret key for the user';

// encoded will be the secret key, base32 encoded
var encoded = base32.encode(key);

// Google authenticator doesn't like equal signs
var encodedForGoogle = encoded.toString().replace(/=/g,'');

// to create a URI for a qr code (change totp to hotp if using hotp)
var uri = 'otpauth://totp/somelabel?secret=' + encodedForGoogle;

Note: If your label has spaces or other invalid uri characters you will need to encode it accordingly using encodeURIComponent More details about the uri key format can be found on the google auth wiki

API

hotp.verify(token, key, opt)

Check a counter based one time password for validity.

Returns null if token is not valid for given key and options.

Returns an object {delta: #} if the token is valid. delta is the count skew between client and server.

opt

window

The allowable margin for the counter. The function will check window codes in the future against the provided token. i.e. if window = 100 and counter = 5 all tokens between 5 and 105 will be checked against the supplied token Default - 50

counter

Counter value. This should be stored by the application on a per user basis. It is up to the application to track and increment this value as needed. It is also up to the application to increment this value if there is a skew between the client and server (delta)

totp.verify(token, key, opt)

Check a time based one time password for validity

Returns null if token is not valid for given key and options.

Returns an object {delta: #} if the token is valid. delta is the count skew between client and server.

opt

window

The allowable margin for the counter. The function will check window codes in the future against the provided token. i.e. if window = 5 and counter = 1000 all tokens between 995 and 1005 will be checked against the supplied token Default - 6

time

The time step of the counter. This must be the same for every request and is used to calculate C. Default - 30

hotp.gen(key, opt)

Return a counter based one time password

opt

counter

Counter value. This should be stored by the application, must be user specific, and be incremented for each request.

totp.gen(key, opt)

Return a time based one time password

opt

time

The time step of the counter. This must be the same for every request and is used to calculate C. Default - 30

Migrating from 1.x to 2.x

Removed

The encBase32 and decBase32 methods have been removed. If you wish to encode/decode base32 you should install a module to do so. We recommend the thirty-two npm module.

Changed

All of the APIs have been changed to return values directly instead of using callbacks. This reflects the fact that the functions are actually synchronous and perform no I/O.

Some of the required arguments to the functions have also been removed from the args parameter and are passed as separate function parameters. See the above API docs for details.

  • notp.checkHOTP(args, err, cb) -> notp.hotp.verify(token, key, opt)
  • notp.checkTOTP(args, err, cb) -> notp.totp.verify(token, key, opt)
  • notp.getHOTP(args, err, cb) -> notp.gotp.gen(key, opt)
  • notp.getTOTP(args, err, cb) -> notp.totp.gen(key, opt)

Args

The argument names have also changed to better describe the purpose of the argument.

  • K -> no longer in args/opt but passed directly as a function argument
  • P -> no longer in args/opt but passed directly as a function argument
  • W -> window
  • C -> counter
  • T -> time

notp's People

Contributors

aw avatar damianb avatar defunctzombie avatar freewil avatar fuzzie360 avatar guyht avatar homakov avatar i5ting avatar martinvl avatar sdenel avatar tonylukasavage avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notp's Issues

totp verification always fails.

I am using the notp.totp.verify method to verify a token for a base32 secret, but it fails for every token. The token is generated on the authenticator app, I tested with both Google's and Microsoft's.

I used the secret to generate totp with notp.totp.gen, but it generates different token than the ones generated by the google's and microsoft's authenticator app.

I'm guessing that's why the verification fails as well, since for a given time step the token doesn't match.

i'm using default time step of 30 seconds.

browser version

I created a browser-friendly fork:

https://github.com/daplie/botp

The main changes are that I had to make a few calls asynchronous (as per previous issue) and shim out a single sha1-hmac function.

For browsers that support WebCrypto, there are no dependencies.

For old IE (pre-Edge) and old Android (pre 4.x) there's a fair bit of the forge library that needs to be included.

If you're interested in working jointly on an notp version 3 that supports these changes let me know. I tried to keep my changes as minimal as possible.

I also built fully-functional Authenticator apps on top of botp and notp:

HOTP counter not working as expected

notp/index.js

Lines 10 to 19 in bbdf82a

function intToBytes(num) {
var bytes = [];
for(var i=7 ; i>=0 ; --i) {
bytes[i] = num & (255);
num = num >> 8;
}
return bytes;
}

intToBytes(9999999999999) will give [0, 0, 0, 0, 78, 114, 159, 255] which is <Buffer 00 00 00 00 4e 72 9f ff>

But 9999999999999 decimal to hexadecimal must return 00 00 09 18 4E 72 9F FF

Same secret with Google Authenticator and NOTP, different tokens

Hey, I have obtained a TOTP shared secret key from GitHub and I have manually inserted the secret to both Google Authenticator and NOTP and verified that the values are correct. I did this twice manually and once using the QR code from GitHub to set up Google Authenticator.

Here's my NOTP code, I am using literally just this line:

console.log(notp.totp.gen('<the secret>'));

The secret is a string in the format of 16 lowercase letters and numbers as provided by GitHub.

Google Authenticator and NOTP give me totally different code. I have tried to cross the time window boundary to check if maybe NOTP was giving me a token one window too old or too new, but they just seems to be completely unrelated. Needless to say GitHub won't accept my TOTP token, but will Google Authenticator's.

Do I miss options which I should be using? According to the READM, the only relevant option is time which I think the defaults cover and match what Google Authenticator is doing, so I am confused as to why the difference exists.

Steps to Reproduce:

  • Go through the GitHub flow's for reconfiguring 2FA
  • On the QR code page, do not use the QA code but click for text code and copy that
  • Insert the secret manually into Google Authenticator and triple check it
  • Insert it also into the code above and quadruple check it
  • Confirm they do not match

notp doesn't work with binary keys

Lots of sites generate a random binary string as the key, and base32 encode it.

Your code:
var hmac = crypto.createHmac('SHA1', new Buffer(key));

takes the binary key, but treats it as utf8 encoded.

new Buffer(key, 'binary') would be better, but http://nodejs.org/api/buffer.html says that the 'binary' encoding " is deprecated and should be avoided in favor of Buffer objects where possible. This encoding will be removed in future versions of Node.".

So what really needs to happen is for the base32 decode function to decode the string into a buffer rather than a string.

As a test case, try the base32-encoded secret 'ZZZZZZZZZZZZZZZZ' on notp and on google authenticator, and see that they get different results.

JS TOTP Libraries

It's rather a curious question. I have noted, mostly TOTP JS libraries are not being maintained for years. What's stopping devs to maintain these libraries.

documentation incorrect

I might be misunderstanding something but I think there's at least one error in the HTOP.verify doc:

window - The allowable margin for the counter. The function will check

  •     'W' codes in the future against the provided passcode.  Note,
    
  •     it is the calling applications responsibility to keep track of
    
  •     'W' and increment it for each password check, and also to adjust
    
  •     it accordingly in the case where the client and server become
    
  •     out of sync (second argument returns non zero).
    
  •     E.g. if W = 100, and C = 5, this function will check the passcode
    
  •     against all One Time Passcodes between 5 and 105.
    

Code:

for(var i = counter - window; i <=  counter + window; ++i) {
        opt.counter = i;
        if(this.gen(key, opt) === token) {
            // We have found a matching code, trigger callback
            // and pass offset
            return { delta: i - counter };
        }
    }

Note that the for loop goes from counter - window, to counter + window. So if W was 100, it would be checking 200 values, not 100. Assuming that C means opt.counter in the docs, it would really be counting from -95 to 105.

Type Definitions

Hi, I love this library because it is super minimal and gets the job done. The only problem is it doesn't have any type definitions for Typescript. Was wondering if there were any plans on creating type definitions otherwise I will just create one on DefinetlyTyped @types repo.

example in doc is older

const result = notp.totp.verify(token, key)

Here result is a object any way,But token is valid would return { delta: 0 }

image

Allow TOTP generation in the future

For my use case (TOTP codes valid for 24 hours that I need to know the day before) I need to generate TOTP codes in the future, not only in the present, but totp.gen() explicitly forbids this possibility.

Is there a reason for this limitation? Would it be possible to allow changing _t just like it is possible to change time?

Hotp verify

As the Readme says, The allowable margin for the counter. The function will check window codes in the future against the provided token. i.e. if window = 100 and counter = 5 all tokens between 5 and 105 will be checked against the supplied token Default - 50 for HOTP code verification.
As I can see, the library now implies another logic (issue #21 says the same).
I have made a PR#39. Please, take a look if you'll have a chance.

hotp.verify scans before counter

This is a security issue. Once used, OTP should not be verified as success.
But right now it goes this way:
for(var i = counter - window; i <= counter + window; ++i)

should be:
for(var i = counter; i <= counter + window; ++i)

Why check process.env.NODE_ENV?

I'd like to be able to casually run my own tests and examples without explicitly setting NODE_ENV.

I don't see a security benefit to this.

If the user of this library is somehow exposing the options object to a client they can already arbitrary adjust the window size to something like 100,000 which is just as insecure, so there's no security benefit.

In fact, I just tested with a window of 100,000 and an arbitrary token 957 124 and in in 5 out of 10 trials each taking about 2 seconds I was able to verify.

hotp.gen (and hence totp.gen) should be asynchronous in next major version.

Node's crypto module began development before node's architecture was finalized.

What is now written as

crypto.createHmac('sha1').update(bytes).digest('hex');

will someday be written more like

var cryptoStream = crypto.create('hmac', 'sha1');
byteStream.pipe(cryptoStream);
cryptoStream.on('end', function (digest) {
  console.log(digest.toString('hex'))
});

Otherwise, the update function becomes blocking.

Although node will probably always be backwards compatible, I was porting this to the browser - which is just a few lines of changes thanks to your clean and excellent code - and the WebCrypto API is already asynchronous (Promise, not callback) so the port is not 1:1.

Required opts object

The documentation shows var login = notp.totp.verify(token, key); under Usage, but it throws trying to get opt.time if you actually do that. I'd be happy to PR if someone weighs in on whether fixing the docs or opt ||= {} would be preferred.

Zero-size Hotp window

For now I can't pass 0 as window size because of that check: var window = opt.window || 50;
I assume that comparing with umdefined should be performed in spite of the notp library is kind of low-level one.
See the PR #41.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.