speakeasy icon indicating copy to clipboard operation
speakeasy copied to clipboard

"list" argument must be an Array of Buffers

Open amreladawy opened this issue 7 years ago • 11 comments

The following code is throwing an error "list" argument must be an Array of Buffers

speakeasy.hotp({ secret: 'ir6jqgomwnldmrhn', encoding: 'base32', counter: speakeasy._counter({}) })

I am doing this in an angular 5 app.

amreladawy avatar Feb 19 '18 15:02 amreladawy

Having the same exact error in a React app on the verify.

ghost avatar Mar 07 '18 14:03 ghost

I got the same issue when I used 'base32' as encoding. Use 'ascii' or any other encoding. This is issue is caused because of this code:

if (!Buffer.isBuffer(secret)) {
   secret = encoding === 'base32' ? base32.decode(secret)
     : new Buffer(secret, encoding);
 }

which makes the key string to buffer for further processing(creating hmac which needs 'secret'/'key' to be buffer ). As of now, I would suggest trying other encoding options.

blackhawkbigpunch avatar Mar 09 '18 06:03 blackhawkbigpunch

Hi @blackhawkbigpunch , The point is that the key is generated at the server side and it is base32. The client must generate the correct topt and send it to the server for validation. I have to use base32 encoding.

amreladawy avatar Mar 10 '18 05:03 amreladawy

I had the same issue but resolved it by using master. The last released version is over 2 years old.

mhingston avatar Mar 20 '18 22:03 mhingston

Is speakeasy not supported anymore. Last update was a while ago

Sir-Chasington avatar Apr 02 '18 17:04 Sir-Chasington

The point is that the key is generated at the server side and it is base32. The client must generate the correct topt and send it to the server for validation. I have to use base32 encoding.

@amreladawy you don't. It's just an encoding. You can let the server create a hex version of the same secret and use that on the client. I just had to do that to use the same secret in cypress testing code that runs in the browser.

On the server:

require('base32.js').decode('ir6jqgomwnldmrhn').toString('hex')
'447c9819ccb3563644ed'

Now you can do that on the client

speakeasy.hotp({
  secret: '447c9819ccb3563644ed',
  encoding: 'hex',
  counter: speakeasy._counter({})
})

Prinzhorn avatar May 17 '18 08:05 Prinzhorn

Thanks @Prinzhorn Due to the long time it took to get a reply on this, I switched to jssha https://github.com/Caligatio/jsSHA which I had to use specific version 1.6.0 to get it working.

amreladawy avatar May 18 '18 10:05 amreladawy


const generateGoogleCode = secret => {
  const secretAscii = base32.decode(secret);
  const secretHex = toHex(secretAscii);
  const code = speakeasy.totp({
    secret: secretHex,
    algorithm: 'sha1',
    encoding: 'hex'
  });
  return code;
};

convert the base32 secret to ascii, then convert to hex, it will work

ygweric avatar Jun 28 '18 23:06 ygweric

Solved, In "node_modules/speakeasy/index.js" replace from line 39 to 41 with this code

if (!Buffer.isBuffer(secret)) {
    secret = encoding === 'base32' ? new Buffer(base32.decode(secret)) : new Buffer(secret, encoding);
  }

mustapha1509 avatar Mar 05 '20 15:03 mustapha1509

In case anyone had an issues with this. I used @ygweric code more or less but here I added full code (with dependencies and toHex implementation). For base32 decoding, I used `hi-base32: "^0.5.0" in my package.json

const secret = "...."; // retrieved from server as string in base32

const base32 = require('hi-base32');
const secretAscii = base32.decode(secret);
const secretHex = toHex(secretAscii);
const totp = speakeasy.totp({secret: secretHex, encoding: 'hex'});

function toHex(str) {
    let result = '';
    for (let i = 0; i < str.length; i++) {
        result += str.charCodeAt(i).toString(16);
    }
    return result;
}

zdenda-online avatar Feb 12 '21 08:02 zdenda-online

@mustapha1509 thanks it worked for me

joseneoito avatar Oct 31 '23 04:10 joseneoito