15

I am creating a hash of an auto-incrementing number. I have created two example loops of how I'm trying to achieve this.

When #1 is is run, the first hash is logged to the console and on the second iteration through the loop, the following error is returned. Error: Digest already called

I believe this is due to this reference in the documentation: The Hash object can not be used again after hash.digest() method has been called. Multiple calls will cause an error to be thrown.

How can I create a loop that uses Node's crypto library to create multiple hashes at one time?

 // Reproduce #1
 const crypto = require('crypto');

 const hash = crypto.createHash('sha256');

 for (let i = 0; i < 5; i++) {
   hash.update('secret' + i);

   console.log(hash.digest('hex'));
 }
agm1984
  • 15,500
  • 6
  • 89
  • 113
  • 2
    Which node version are you using ? For #1 I had to move `const hash = crypto.createHash('sha256');` inside your `for` loop (instance not reusable). For #2, you have a typo `sha1256` to `sha256` – Bertrand Martel Jun 30 '17 at 22:36
  • I have Node v8.1.2. Thanks for the typo alert. The first one doesnt work because it violates this from the docs: `The Hash object can not be used again after hash.digest() method has been called. Multiple calls will cause an error to be thrown.` – agm1984 Jun 30 '17 at 22:47
  • My objective is to create a method that creates a serial number based on hashing some input fields based on the 'APIs secret'. If I put this logic or anything remotely similar to it, it fails. The first one makes a valid serial, but subsequent ones don't actually throw an error. They just make invalid serials due to `.digest()` and/or `.createHash()` – agm1984 Jun 30 '17 at 22:50

2 Answers2

30

If the error is "Digest already called", then the idea would be to call the Hash only once. You can do that by creating a fresh Hash instance on each iteration:

const crypto = require('crypto');
for (let i = 0; i < 5; i++) {
    const hash = crypto.createHash('sha256');
    hash.update('secret' + i);
    console.log(hash.digest('hex'));
}

Output:

97699b7cc0a0ed83b78b2002f0e57046ee561be6942bec256fe201abba552a9e
5b11618c2e44027877d0cd0921ed166b9f176f50587fc91e7534dd2946db77d6
35224d0d3465d74e855f8d69a136e79c744ea35a675d3393360a327cbf6359a2
e0d9ac7d3719d04d3d68bc463498b0889723c4e70c3549d43681dd8996b7177f
fe2d033fef7942ed06d418992d35ca98feb53943d452f5994f96934d754e15cb
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • 8
    Seems a library bug, it is **ugly** to repeat `const H = crypto.createHash('sha256')`... The initialization `H` must be reusable... how to request to fix it? – Peter Krauss Jan 31 '19 at 20:10
  • 1
    I'm not fully convinced. My current idea is that `hash.digest` would be deterministic, so subsequent calls using the same hash would output the same result. So it is therefore required to create a new hash every time. Additionally, my guess is there is a Singleton local state field inside the hash object that is set upon createHash and then reset upon digest, so if you call createHash twice in a row, it will detect the stale condition. – agm1984 Jun 19 '20 at 20:58
  • @agm1984 That might be a reason why an error occurs. Also, I haven't tried to determine in my answer why the behavior is as it is. I only provided a way to fix the problem. – Artjom B. Jun 19 '20 at 22:05
3

A clean way to do it without having to repeat const H = crypto.createHash('sha256') on every instance is to use hash.copy() -

 const crypto = require('crypto');
 const hash = crypto.createHash('sha256');

 for (let i = 0; i < 5; i++) {
   hash.update('secret' + i);
   console.log(hash.copy().digest('hex'));
 }

You get the desired output -

e7ebc4daa65343449285b5736ebe98a575c50ce337e86055683452d7d612ac78
3dc562fa371a320efb0cca0ae344c8a5bddfcd3d5191cd124798404b729423c2
7547b5c1992ed566a2125817b2c76ed4a7d3c551232904f886bd954e649e3144
b49247304dc3ef76d9ebfd0482bfc68ab9b7b0fe2007b7c60e03ad6b8123be33
82bc2bcfc528fd55807a981c79e0b6aa430a690b51de79d9d0c5f5627864965b
  • I'm getting `TypeError: hash.copy is not a function` with the proposed solution. – Frenchcooc Oct 21 '20 at 19:21
  • 3
    According to the documentation of `hash.copy([options])`, the it *Creates a new Hash object that contains a deep copy of the internal state of the current Hash object.* This is not what OP wants since copying the internal state means that during the second iteration, the data from the first iteration is also taken into account to produce the second hash. – Artjom B. Jan 12 '21 at 18:40