Skip to content

Instantly share code, notes, and snippets.

@carbonphyber
Last active November 21, 2020 15:24
Show Gist options
  • Save carbonphyber/ffa3bdb94d59ea8f8466e34178279db5 to your computer and use it in GitHub Desktop.
Save carbonphyber/ffa3bdb94d59ea8f8466e34178279db5 to your computer and use it in GitHub Desktop.

What

After implementing speakeasy (npm, GitHub) for a Node web application (for both password reset and 2FA+SMS), we ran a penetration test and noticed that our speakeasy configuration+code was brute-force attackable.

We would like to share results of our findings so that other implementers of this great library are able to learn from our mistakes.

When using the speakeasy.totp.verify function for one-time password usage, we encountered conditions where we were defaulting the "secret" to a JavaScript empty-string (not null or undefined, which are the only two checks the speakeasy code internally makes). This is an extremely bad idea. In our findings, this creates false positives and greatly reduces the entropy of a TOTP implementation. With our original settings, our pen tester was able to find hundreds of false positive token-checks in just 30,000 iterations of a script.

The other finding we made was that our "window" setting was far too large. We used the default step size of 30 (seconds) and a "window" of 120 for password resets (meaning 240 "steps" in total). This large window creatly increased the likelihood of finding a false positive in a brute force attack.

How

After implementing our 3-step password reset flow, we had a pen tester probe it. They ran a brute force attack against our password reset tokens (created by speakeasy) and managed to successfully change the password for another user.

Results

See results.txt for an example run of speakeasy_totp_verify_test.js. This script runs the speakeasy.totp.verify, passing in an empty secret, thousands of times at each "window" size between 1 and 200.

As expected, a larger window size increases the possibility of a false positive.

An unexpected result was that the speakeasy.totp.verify function does not consider an empty string invalid and and empty string seems to statistically increase the odds of a false positive.

Conclusions

We recomend that implementers of speakeasy TOTP keep their window small, ensure they never run the speakeasy.totp.verify function on an empty string (we chose to return false on that condition before the verify function executed).

More importantly, we recommend the speakeasy documentation describe the "secret" and "window" options better to give implementers a better understanding of the tradeoff of passing in an empty string and a wider window.

It's probably wise to also increase entropy by hashing "algorihm", more "digits" on the token, longer "secret" length, etc, but these are all useless changes without first ensuring you never pass in an empty string to the verify.

Passing an empty string "secret" speakeasy.totp.verify creates lots of false positives.
With larger "window"s, this makes brute force token attacks reasonable.
Testing speakeasy.totp.verify using secret pre-verification (9999 iterations each)
false positive count(iterations = 9999; window = 1): 0 (0.03 sec)
false positive count(iterations = 9999; window = 2): 0 (0.02 sec)
false positive count(iterations = 9999; window = 3): 0 (0.01 sec)
false positive count(iterations = 9999; window = 4): 0 (0.02 sec)
false positive count(iterations = 9999; window = 5): 0 (0.01 sec)
false positive count(iterations = 9999; window = 6): 0 (0.01 sec)
false positive count(iterations = 9999; window = 7): 0 (0.01 sec)
false positive count(iterations = 9999; window = 8): 0 (0.01 sec)
false positive count(iterations = 9999; window = 9): 0 (0.01 sec)
false positive count(iterations = 9999; window = 10): 0 (0.01 sec)
false positive count(iterations = 9999; window = 11): 0 (0.01 sec)
false positive count(iterations = 9999; window = 12): 0 (0.01 sec)
false positive count(iterations = 9999; window = 13): 0 (0.01 sec)
false positive count(iterations = 9999; window = 14): 0 (0.01 sec)
false positive count(iterations = 9999; window = 15): 0 (0.01 sec)
false positive count(iterations = 9999; window = 16): 0 (0.01 sec)
false positive count(iterations = 9999; window = 17): 0 (0.01 sec)
false positive count(iterations = 9999; window = 18): 0 (0.01 sec)
false positive count(iterations = 9999; window = 19): 0 (0.01 sec)
false positive count(iterations = 9999; window = 20): 0 (0.01 sec)
false positive count(iterations = 9999; window = 21): 0 (0.01 sec)
false positive count(iterations = 9999; window = 22): 0 (0.01 sec)
false positive count(iterations = 9999; window = 23): 0 (0.01 sec)
false positive count(iterations = 9999; window = 24): 0 (0.01 sec)
false positive count(iterations = 9999; window = 25): 0 (0.01 sec)
false positive count(iterations = 9999; window = 26): 0 (0.01 sec)
false positive count(iterations = 9999; window = 27): 0 (0.01 sec)
false positive count(iterations = 9999; window = 28): 0 (0.01 sec)
false positive count(iterations = 9999; window = 29): 0 (0.01 sec)
false positive count(iterations = 9999; window = 30): 0 (0.01 sec)
false positive count(iterations = 9999; window = 31): 0 (0.01 sec)
false positive count(iterations = 9999; window = 32): 0 (0.01 sec)
false positive count(iterations = 9999; window = 33): 0 (0.01 sec)
false positive count(iterations = 9999; window = 34): 0 (0.01 sec)
false positive count(iterations = 9999; window = 35): 0 (0.01 sec)
false positive count(iterations = 9999; window = 36): 0 (0.01 sec)
false positive count(iterations = 9999; window = 37): 0 (0.01 sec)
false positive count(iterations = 9999; window = 38): 0 (0.01 sec)
false positive count(iterations = 9999; window = 39): 0 (0.01 sec)
false positive count(iterations = 9999; window = 40): 0 (0.01 sec)
false positive count(iterations = 9999; window = 41): 0 (0.01 sec)
false positive count(iterations = 9999; window = 42): 0 (0.01 sec)
false positive count(iterations = 9999; window = 43): 0 (0.01 sec)
false positive count(iterations = 9999; window = 44): 0 (0.01 sec)
false positive count(iterations = 9999; window = 45): 0 (0.01 sec)
false positive count(iterations = 9999; window = 46): 0 (0.01 sec)
false positive count(iterations = 9999; window = 47): 0 (0.01 sec)
false positive count(iterations = 9999; window = 48): 0 (0.01 sec)
false positive count(iterations = 9999; window = 49): 0 (0.01 sec)
false positive count(iterations = 9999; window = 50): 0 (0.01 sec)
false positive count(iterations = 9999; window = 51): 0 (0.01 sec)
false positive count(iterations = 9999; window = 52): 0 (0.01 sec)
false positive count(iterations = 9999; window = 53): 0 (0.01 sec)
false positive count(iterations = 9999; window = 54): 0 (0.01 sec)
false positive count(iterations = 9999; window = 55): 0 (0.01 sec)
false positive count(iterations = 9999; window = 56): 0 (0.01 sec)
false positive count(iterations = 9999; window = 57): 0 (0.01 sec)
false positive count(iterations = 9999; window = 58): 0 (0.01 sec)
false positive count(iterations = 9999; window = 59): 0 (0.01 sec)
false positive count(iterations = 9999; window = 60): 0 (0.01 sec)
false positive count(iterations = 9999; window = 61): 0 (0.01 sec)
false positive count(iterations = 9999; window = 62): 0 (0.01 sec)
false positive count(iterations = 9999; window = 63): 0 (0.01 sec)
false positive count(iterations = 9999; window = 64): 0 (0.01 sec)
false positive count(iterations = 9999; window = 65): 0 (0.01 sec)
false positive count(iterations = 9999; window = 66): 0 (0.01 sec)
false positive count(iterations = 9999; window = 67): 0 (0.01 sec)
false positive count(iterations = 9999; window = 68): 0 (0.01 sec)
false positive count(iterations = 9999; window = 69): 0 (0.01 sec)
false positive count(iterations = 9999; window = 70): 0 (0.01 sec)
false positive count(iterations = 9999; window = 71): 0 (0.01 sec)
false positive count(iterations = 9999; window = 72): 0 (0.01 sec)
false positive count(iterations = 9999; window = 73): 0 (0.01 sec)
false positive count(iterations = 9999; window = 74): 0 (0.01 sec)
false positive count(iterations = 9999; window = 75): 0 (0.01 sec)
false positive count(iterations = 9999; window = 76): 0 (0.01 sec)
false positive count(iterations = 9999; window = 77): 0 (0.01 sec)
false positive count(iterations = 9999; window = 78): 0 (0.01 sec)
false positive count(iterations = 9999; window = 79): 0 (0.01 sec)
false positive count(iterations = 9999; window = 80): 0 (0.01 sec)
false positive count(iterations = 9999; window = 81): 0 (0.01 sec)
false positive count(iterations = 9999; window = 82): 0 (0.01 sec)
false positive count(iterations = 9999; window = 83): 0 (0.01 sec)
false positive count(iterations = 9999; window = 84): 0 (0.01 sec)
false positive count(iterations = 9999; window = 85): 0 (0.01 sec)
false positive count(iterations = 9999; window = 86): 0 (0.01 sec)
false positive count(iterations = 9999; window = 87): 0 (0.01 sec)
false positive count(iterations = 9999; window = 88): 0 (0.01 sec)
false positive count(iterations = 9999; window = 89): 0 (0.01 sec)
false positive count(iterations = 9999; window = 90): 0 (0.01 sec)
false positive count(iterations = 9999; window = 91): 0 (0.01 sec)
false positive count(iterations = 9999; window = 92): 0 (0.01 sec)
false positive count(iterations = 9999; window = 93): 0 (0.01 sec)
false positive count(iterations = 9999; window = 94): 0 (0.01 sec)
false positive count(iterations = 9999; window = 95): 0 (0.01 sec)
false positive count(iterations = 9999; window = 96): 0 (0.01 sec)
false positive count(iterations = 9999; window = 97): 0 (0.01 sec)
false positive count(iterations = 9999; window = 98): 0 (0.01 sec)
false positive count(iterations = 9999; window = 99): 0 (0.01 sec)
false positive count(iterations = 9999; window = 100): 0 (0.01 sec)
false positive count(iterations = 9999; window = 101): 0 (0.01 sec)
false positive count(iterations = 9999; window = 102): 0 (0.01 sec)
false positive count(iterations = 9999; window = 103): 0 (0.02 sec)
false positive count(iterations = 9999; window = 104): 0 (0.02 sec)
false positive count(iterations = 9999; window = 105): 0 (0.02 sec)
false positive count(iterations = 9999; window = 106): 0 (0.02 sec)
false positive count(iterations = 9999; window = 107): 0 (0.01 sec)
false positive count(iterations = 9999; window = 108): 0 (0.01 sec)
false positive count(iterations = 9999; window = 109): 0 (0.01 sec)
false positive count(iterations = 9999; window = 110): 0 (0.01 sec)
false positive count(iterations = 9999; window = 111): 0 (0.01 sec)
false positive count(iterations = 9999; window = 112): 0 (0.01 sec)
false positive count(iterations = 9999; window = 113): 0 (0.01 sec)
false positive count(iterations = 9999; window = 114): 0 (0.01 sec)
false positive count(iterations = 9999; window = 115): 0 (0.01 sec)
false positive count(iterations = 9999; window = 116): 0 (0.01 sec)
false positive count(iterations = 9999; window = 117): 0 (0.01 sec)
false positive count(iterations = 9999; window = 118): 0 (0.01 sec)
false positive count(iterations = 9999; window = 119): 0 (0.01 sec)
false positive count(iterations = 9999; window = 120): 0 (0.01 sec)
false positive count(iterations = 9999; window = 121): 0 (0.01 sec)
false positive count(iterations = 9999; window = 122): 0 (0.01 sec)
false positive count(iterations = 9999; window = 123): 0 (0.01 sec)
false positive count(iterations = 9999; window = 124): 0 (0.01 sec)
false positive count(iterations = 9999; window = 125): 0 (0.01 sec)
false positive count(iterations = 9999; window = 126): 0 (0.01 sec)
false positive count(iterations = 9999; window = 127): 0 (0.01 sec)
false positive count(iterations = 9999; window = 128): 0 (0.01 sec)
false positive count(iterations = 9999; window = 129): 0 (0.01 sec)
false positive count(iterations = 9999; window = 130): 0 (0.01 sec)
false positive count(iterations = 9999; window = 131): 0 (0.01 sec)
false positive count(iterations = 9999; window = 132): 0 (0.01 sec)
false positive count(iterations = 9999; window = 133): 0 (0.01 sec)
false positive count(iterations = 9999; window = 134): 0 (0.01 sec)
false positive count(iterations = 9999; window = 135): 0 (0.01 sec)
false positive count(iterations = 9999; window = 136): 0 (0.01 sec)
false positive count(iterations = 9999; window = 137): 0 (0.01 sec)
false positive count(iterations = 9999; window = 138): 0 (0.01 sec)
false positive count(iterations = 9999; window = 139): 0 (0.01 sec)
false positive count(iterations = 9999; window = 140): 0 (0.01 sec)
false positive count(iterations = 9999; window = 141): 0 (0.01 sec)
false positive count(iterations = 9999; window = 142): 0 (0.01 sec)
false positive count(iterations = 9999; window = 143): 0 (0.01 sec)
false positive count(iterations = 9999; window = 144): 0 (0.01 sec)
false positive count(iterations = 9999; window = 145): 0 (0.01 sec)
false positive count(iterations = 9999; window = 146): 0 (0.01 sec)
false positive count(iterations = 9999; window = 147): 0 (0.01 sec)
false positive count(iterations = 9999; window = 148): 0 (0.01 sec)
false positive count(iterations = 9999; window = 149): 0 (0.01 sec)
false positive count(iterations = 9999; window = 150): 0 (0.01 sec)
false positive count(iterations = 9999; window = 151): 0 (0.01 sec)
false positive count(iterations = 9999; window = 152): 0 (0.01 sec)
false positive count(iterations = 9999; window = 153): 0 (0.01 sec)
false positive count(iterations = 9999; window = 154): 0 (0.01 sec)
false positive count(iterations = 9999; window = 155): 0 (0.01 sec)
false positive count(iterations = 9999; window = 156): 0 (0.01 sec)
false positive count(iterations = 9999; window = 157): 0 (0.01 sec)
false positive count(iterations = 9999; window = 158): 0 (0.01 sec)
false positive count(iterations = 9999; window = 159): 0 (0.01 sec)
false positive count(iterations = 9999; window = 160): 0 (0.01 sec)
false positive count(iterations = 9999; window = 161): 0 (0.01 sec)
false positive count(iterations = 9999; window = 162): 0 (0.01 sec)
false positive count(iterations = 9999; window = 163): 0 (0.01 sec)
false positive count(iterations = 9999; window = 164): 0 (0.01 sec)
false positive count(iterations = 9999; window = 165): 0 (0.01 sec)
false positive count(iterations = 9999; window = 166): 0 (0.01 sec)
false positive count(iterations = 9999; window = 167): 0 (0.01 sec)
false positive count(iterations = 9999; window = 168): 0 (0.01 sec)
false positive count(iterations = 9999; window = 169): 0 (0.01 sec)
false positive count(iterations = 9999; window = 170): 0 (0.01 sec)
false positive count(iterations = 9999; window = 171): 0 (0.01 sec)
false positive count(iterations = 9999; window = 172): 0 (0.01 sec)
false positive count(iterations = 9999; window = 173): 0 (0.01 sec)
false positive count(iterations = 9999; window = 174): 0 (0.01 sec)
false positive count(iterations = 9999; window = 175): 0 (0.01 sec)
false positive count(iterations = 9999; window = 176): 0 (0.01 sec)
false positive count(iterations = 9999; window = 177): 0 (0.01 sec)
false positive count(iterations = 9999; window = 178): 0 (0.01 sec)
false positive count(iterations = 9999; window = 179): 0 (0.01 sec)
false positive count(iterations = 9999; window = 180): 0 (0.01 sec)
false positive count(iterations = 9999; window = 181): 0 (0.01 sec)
false positive count(iterations = 9999; window = 182): 0 (0.01 sec)
false positive count(iterations = 9999; window = 183): 0 (0.01 sec)
false positive count(iterations = 9999; window = 184): 0 (0.01 sec)
false positive count(iterations = 9999; window = 185): 0 (0.01 sec)
false positive count(iterations = 9999; window = 186): 0 (0.01 sec)
false positive count(iterations = 9999; window = 187): 0 (0.01 sec)
false positive count(iterations = 9999; window = 188): 0 (0.01 sec)
false positive count(iterations = 9999; window = 189): 0 (0.01 sec)
false positive count(iterations = 9999; window = 190): 0 (0.01 sec)
false positive count(iterations = 9999; window = 191): 0 (0.01 sec)
false positive count(iterations = 9999; window = 192): 0 (0.01 sec)
false positive count(iterations = 9999; window = 193): 0 (0.01 sec)
false positive count(iterations = 9999; window = 194): 0 (0.01 sec)
false positive count(iterations = 9999; window = 195): 0 (0.01 sec)
false positive count(iterations = 9999; window = 196): 0 (0.01 sec)
false positive count(iterations = 9999; window = 197): 0 (0.01 sec)
false positive count(iterations = 9999; window = 198): 0 (0.01 sec)
false positive count(iterations = 9999; window = 199): 0 (0.01 sec)
false positive count(iterations = 9999; window = 200): 0 (0.01 sec)
Testing speakeasy.totp.verify (9999 iterations each)
false positive count(iterations = 9999; window = 1): 0 (0.79 sec)
false positive count(iterations = 9999; window = 2): 0 (1.76 sec)
false positive count(iterations = 9999; window = 3): 0 (2.86 sec)
false positive count(iterations = 9999; window = 4): 0 (4.11 sec)
false positive count(iterations = 9999; window = 5): 0 (5.53 sec)
false positive count(iterations = 9999; window = 6): 0 (7.09 sec)
false positive count(iterations = 9999; window = 7): 0 (8.78 sec)
false positive count(iterations = 9999; window = 8): 1 (10.65 sec)
false positive count(iterations = 9999; window = 9): 0 (12.68 sec)
false positive count(iterations = 9999; window = 10): 1 (14.88 sec)
false positive count(iterations = 9999; window = 11): 0 (17.38 sec)
false positive count(iterations = 9999; window = 12): 1 (19.89 sec)
false positive count(iterations = 9999; window = 13): 0 (22.75 sec)
false positive count(iterations = 9999; window = 14): 0 (25.85 sec)
false positive count(iterations = 9999; window = 15): 1 (28.95 sec)
false positive count(iterations = 9999; window = 16): 0 (32.38 sec)
false positive count(iterations = 9999; window = 17): 0 (35.87 sec)
false positive count(iterations = 9999; window = 18): 1 (39.54 sec)
false positive count(iterations = 9999; window = 19): 0 (43.36 sec)
false positive count(iterations = 9999; window = 20): 0 (47.31 sec)
false positive count(iterations = 9999; window = 21): 1 (51.51 sec)
false positive count(iterations = 9999; window = 22): 1 (55.77 sec)
false positive count(iterations = 9999; window = 23): 0 (60.09 sec)
false positive count(iterations = 9999; window = 24): 0 (64.48 sec)
false positive count(iterations = 9999; window = 25): 2 (69.08 sec)
false positive count(iterations = 9999; window = 26): 0 (73.83 sec)
false positive count(iterations = 9999; window = 27): 1 (79.00 sec)
false positive count(iterations = 9999; window = 28): 2 (84.07 sec)
false positive count(iterations = 9999; window = 29): 1 (89.27 sec)
false positive count(iterations = 9999; window = 30): 0 (94.73 sec)
false positive count(iterations = 9999; window = 31): 0 (100.25 sec)
false positive count(iterations = 9999; window = 32): 0 (105.86 sec)
false positive count(iterations = 9999; window = 33): 0 (111.67 sec)
false positive count(iterations = 9999; window = 34): 0 (117.70 sec)
false positive count(iterations = 9999; window = 35): 0 (123.96 sec)
false positive count(iterations = 9999; window = 36): 0 (130.28 sec)
false positive count(iterations = 9999; window = 37): 0 (136.76 sec)
false positive count(iterations = 9999; window = 38): 1 (143.42 sec)
false positive count(iterations = 9999; window = 39): 1 (150.22 sec)
false positive count(iterations = 9999; window = 40): 1 (157.25 sec)
false positive count(iterations = 9999; window = 41): 1 (164.40 sec)
false positive count(iterations = 9999; window = 42): 1 (171.91 sec)
false positive count(iterations = 9999; window = 43): 2 (179.54 sec)
false positive count(iterations = 9999; window = 44): 1 (187.39 sec)
false positive count(iterations = 9999; window = 45): 0 (195.48 sec)
false positive count(iterations = 9999; window = 46): 0 (203.60 sec)
false positive count(iterations = 9999; window = 47): 1 (211.61 sec)
false positive count(iterations = 9999; window = 48): 0 (220.03 sec)
false positive count(iterations = 9999; window = 49): 0 (228.45 sec)
false positive count(iterations = 9999; window = 50): 1 (237.03 sec)
false positive count(iterations = 9999; window = 51): 0 (245.84 sec)
false positive count(iterations = 9999; window = 52): 1 (254.77 sec)
false positive count(iterations = 9999; window = 53): 0 (263.82 sec)
false positive count(iterations = 9999; window = 54): 3 (273.00 sec)
false positive count(iterations = 9999; window = 55): 2 (282.57 sec)
false positive count(iterations = 9999; window = 56): 2 (292.05 sec)
false positive count(iterations = 9999; window = 57): 2 (301.75 sec)
false positive count(iterations = 9999; window = 58): 0 (311.67 sec)
false positive count(iterations = 9999; window = 59): 0 (321.59 sec)
false positive count(iterations = 9999; window = 60): 2 (331.93 sec)
false positive count(iterations = 9999; window = 61): 1 (342.11 sec)
false positive count(iterations = 9999; window = 62): 3 (352.55 sec)
false positive count(iterations = 9999; window = 63): 2 (363.33 sec)
false positive count(iterations = 9999; window = 64): 3 (374.51 sec)
false positive count(iterations = 9999; window = 65): 1 (385.38 sec)
false positive count(iterations = 9999; window = 66): 0 (396.40 sec)
false positive count(iterations = 9999; window = 67): 2 (407.69 sec)
false positive count(iterations = 9999; window = 68): 0 (419.02 sec)
false positive count(iterations = 9999; window = 69): 3 (430.35 sec)
false positive count(iterations = 9999; window = 70): 0 (442.01 sec)
false positive count(iterations = 9999; window = 71): 1 (453.73 sec)
false positive count(iterations = 9999; window = 72): 3 (465.71 sec)
false positive count(iterations = 9999; window = 73): 0 (477.61 sec)
false positive count(iterations = 9999; window = 74): 1 (489.85 sec)
false positive count(iterations = 9999; window = 75): 2 (502.38 sec)
false positive count(iterations = 9999; window = 76): 4 (514.90 sec)
false positive count(iterations = 9999; window = 77): 2 (527.74 sec)
false positive count(iterations = 9999; window = 78): 1 (540.62 sec)
false positive count(iterations = 9999; window = 79): 4 (553.51 sec)
false positive count(iterations = 9999; window = 80): 0 (566.83 sec)
false positive count(iterations = 9999; window = 81): 1 (580.14 sec)
false positive count(iterations = 9999; window = 82): 0 (593.53 sec)
false positive count(iterations = 9999; window = 83): 2 (607.35 sec)
false positive count(iterations = 9999; window = 84): 2 (621.24 sec)
false positive count(iterations = 9999; window = 85): 2 (635.22 sec)
false positive count(iterations = 9999; window = 86): 2 (649.31 sec)
false positive count(iterations = 9999; window = 87): 2 (663.61 sec)
false positive count(iterations = 9999; window = 88): 1 (678.03 sec)
false positive count(iterations = 9999; window = 89): 1 (692.78 sec)
false positive count(iterations = 9999; window = 90): 3 (707.44 sec)
false positive count(iterations = 9999; window = 91): 3 (722.46 sec)
false positive count(iterations = 9999; window = 92): 1 (737.72 sec)
false positive count(iterations = 9999; window = 93): 4 (752.75 sec)
false positive count(iterations = 9999; window = 94): 2 (768.09 sec)
false positive count(iterations = 9999; window = 95): 2 (783.50 sec)
false positive count(iterations = 9999; window = 96): 1 (799.28 sec)
false positive count(iterations = 9999; window = 97): 0 (815.30 sec)
false positive count(iterations = 9999; window = 98): 4 (831.24 sec)
false positive count(iterations = 9999; window = 99): 2 (847.26 sec)
false positive count(iterations = 9999; window = 100): 2 (863.34 sec)
false positive count(iterations = 9999; window = 101): 5 (879.87 sec)
false positive count(iterations = 9999; window = 102): 3 (896.36 sec)
false positive count(iterations = 9999; window = 103): 1 (913.21 sec)
false positive count(iterations = 9999; window = 104): 2 (930.08 sec)
false positive count(iterations = 9999; window = 105): 4 (946.99 sec)
false positive count(iterations = 9999; window = 106): 1 (964.02 sec)
false positive count(iterations = 9999; window = 107): 1 (981.53 sec)
false positive count(iterations = 9999; window = 108): 5 (999.00 sec)
false positive count(iterations = 9999; window = 109): 2 (1016.54 sec)
false positive count(iterations = 9999; window = 110): 1 (1034.24 sec)
false positive count(iterations = 9999; window = 111): 0 (1052.36 sec)
false positive count(iterations = 9999; window = 112): 3 (1070.54 sec)
false positive count(iterations = 9999; window = 113): 2 (1088.85 sec)
false positive count(iterations = 9999; window = 114): 3 (1107.28 sec)
false positive count(iterations = 9999; window = 115): 2 (1125.80 sec)
false positive count(iterations = 9999; window = 116): 2 (1144.69 sec)
false positive count(iterations = 9999; window = 117): 4 (1163.62 sec)
false positive count(iterations = 9999; window = 118): 2 (1182.76 sec)
false positive count(iterations = 9999; window = 119): 4 (1202.32 sec)
false positive count(iterations = 9999; window = 120): 4 (1221.78 sec)
false positive count(iterations = 9999; window = 121): 4 (1241.49 sec)
false positive count(iterations = 9999; window = 122): 5 (1261.15 sec)
false positive count(iterations = 9999; window = 123): 3 (1281.12 sec)
false positive count(iterations = 9999; window = 124): 3 (1301.00 sec)
false positive count(iterations = 9999; window = 125): 5 (1320.93 sec)
false positive count(iterations = 9999; window = 126): 3 (1341.27 sec)
false positive count(iterations = 9999; window = 127): 1 (1361.73 sec)
false positive count(iterations = 9999; window = 128): 0 (1382.20 sec)
false positive count(iterations = 9999; window = 129): 0 (1403.12 sec)
false positive count(iterations = 9999; window = 130): 0 (1424.53 sec)
false positive count(iterations = 9999; window = 131): 1 (1445.82 sec)
false positive count(iterations = 9999; window = 132): 3 (1467.29 sec)
false positive count(iterations = 9999; window = 133): 1 (1488.78 sec)
false positive count(iterations = 9999; window = 134): 0 (1510.21 sec)
false positive count(iterations = 9999; window = 135): 1 (1531.86 sec)
false positive count(iterations = 9999; window = 136): 2 (1553.83 sec)
false positive count(iterations = 9999; window = 137): 4 (1575.84 sec)
false positive count(iterations = 9999; window = 138): 2 (1597.70 sec)
false positive count(iterations = 9999; window = 139): 5 (1620.05 sec)
false positive count(iterations = 9999; window = 140): 3 (1642.55 sec)
false positive count(iterations = 9999; window = 141): 1 (1665.01 sec)
false positive count(iterations = 9999; window = 142): 1 (1687.77 sec)
false positive count(iterations = 9999; window = 143): 3 (1710.63 sec)
false positive count(iterations = 9999; window = 144): 1 (1733.74 sec)
false positive count(iterations = 9999; window = 145): 3 (1756.98 sec)
false positive count(iterations = 9999; window = 146): 2 (1780.35 sec)
false positive count(iterations = 9999; window = 147): 2 (1803.92 sec)
false positive count(iterations = 9999; window = 148): 7 (1827.62 sec)
false positive count(iterations = 9999; window = 149): 5 (1851.50 sec)
false positive count(iterations = 9999; window = 150): 0 (1875.54 sec)
false positive count(iterations = 9999; window = 151): 5 (1899.74 sec)
false positive count(iterations = 9999; window = 152): 1 (1923.90 sec)
false positive count(iterations = 9999; window = 153): 1 (1948.57 sec)
false positive count(iterations = 9999; window = 154): 3 (1973.14 sec)
false positive count(iterations = 9999; window = 155): 2 (1998.20 sec)
false positive count(iterations = 9999; window = 156): 6 (2023.24 sec)
false positive count(iterations = 9999; window = 157): 3 (2048.47 sec)
false positive count(iterations = 9999; window = 158): 2 (2073.92 sec)
false positive count(iterations = 9999; window = 159): 2 (2099.38 sec)
false positive count(iterations = 9999; window = 160): 7 (2125.09 sec)
false positive count(iterations = 9999; window = 161): 5 (2151.00 sec)
false positive count(iterations = 9999; window = 162): 3 (2176.85 sec)
false positive count(iterations = 9999; window = 163): 1 (2202.89 sec)
false positive count(iterations = 9999; window = 164): 3 (2229.16 sec)
false positive count(iterations = 9999; window = 165): 6 (2255.52 sec)
false positive count(iterations = 9999; window = 166): 2 (2282.22 sec)
false positive count(iterations = 9999; window = 167): 3 (2308.79 sec)
false positive count(iterations = 9999; window = 168): 6 (2336.25 sec)
false positive count(iterations = 9999; window = 169): 1 (2363.35 sec)
false positive count(iterations = 9999; window = 170): 4 (2390.47 sec)
false positive count(iterations = 9999; window = 171): 4 (2418.20 sec)
false positive count(iterations = 9999; window = 172): 2 (2445.73 sec)
false positive count(iterations = 9999; window = 173): 3 (2473.37 sec)
false positive count(iterations = 9999; window = 174): 2 (2501.35 sec)
false positive count(iterations = 9999; window = 175): 3 (2529.34 sec)
false positive count(iterations = 9999; window = 176): 4 (2557.62 sec)
false positive count(iterations = 9999; window = 177): 6 (2585.99 sec)
false positive count(iterations = 9999; window = 178): 7 (2614.33 sec)
false positive count(iterations = 9999; window = 179): 2 (2643.10 sec)
false positive count(iterations = 9999; window = 180): 1 (2671.66 sec)
false positive count(iterations = 9999; window = 181): 3 (2701.16 sec)
false positive count(iterations = 9999; window = 182): 1 (2730.66 sec)
false positive count(iterations = 9999; window = 183): 4 (2759.75 sec)
false positive count(iterations = 9999; window = 184): 3 (2788.97 sec)
false positive count(iterations = 9999; window = 185): 3 (2818.31 sec)
false positive count(iterations = 9999; window = 186): 3 (2847.83 sec)
false positive count(iterations = 9999; window = 187): 4 (2877.59 sec)
false positive count(iterations = 9999; window = 188): 4 (2907.52 sec)
false positive count(iterations = 9999; window = 189): 4 (2937.58 sec)
false positive count(iterations = 9999; window = 190): 6 (2967.94 sec)
false positive count(iterations = 9999; window = 191): 6 (2998.31 sec)
false positive count(iterations = 9999; window = 192): 5 (3028.60 sec)
false positive count(iterations = 9999; window = 193): 6 (3059.39 sec)
false positive count(iterations = 9999; window = 194): 0 (3090.24 sec)
false positive count(iterations = 9999; window = 195): 3 (3121.46 sec)
false positive count(iterations = 9999; window = 196): 4 (3152.72 sec)
false positive count(iterations = 9999; window = 197): 6 (3183.82 sec)
false positive count(iterations = 9999; window = 198): 4 (3215.36 sec)
false positive count(iterations = 9999; window = 199): 3 (3247.27 sec)
false positive count(iterations = 9999; window = 200): 5 (3279.28 sec)
const async = require('async'),
speakeasy = require('speakeasy'),
_ = require('lodash');
const CHUNK_SIZE = 9999,
NUM_TESTS = 200,
NUM_DIGITS = 6;
let tokens = [],
rawSpeakEasyValidate = (s, t, w) => speakeasy.totp.verify({
'secret': s,
'token': t,
'window': w,
'digits': NUM_DIGITS,
}),
// this function checks to make sure that s is non-empty before running speakeasy.totp.verify
betterSpeakEasyValidate = (s, t, w) => s && speakeasy.totp.verify({
'secret': s,
'token': t,
'window': w,
'digits': NUM_DIGITS,
}),
timeBefore,
newWindowSize,
count,
i;
console.log("Testing speakeasy.totp.verify using secret pre-verification (" + CHUNK_SIZE + " iterations each)\n");
// Run a loop of NUM_TESTS tests, each of NUM_TESTS tests
for (i = 0, newWindowSize = 1, timeBefore = new Date; i < NUM_TESTS; ++i, ++newWindowSize, timeBefore = new Date) {
// generate CHUNK_SIZE random tokens of NUM_DIGITS digits
tokens = _.range(0, CHUNK_SIZE).map(e => Math.round(Math.random() * Math.pow(10, NUM_DIGITS)));
// for each token, run betterSpeakEasyValidate
// do it in parallel to make use of multiple processors
async.parallel(tokens.map(token => function (done) {
return done(null, betterSpeakEasyValidate('', token, newWindowSize));
}),
(err, results) => {
// counter up the results for this chunk and display them
count = (results || []).reduce((sum, curr) => sum += +curr, 0);
console.log('false positive count(iterations = ' + CHUNK_SIZE + '; window = ' + newWindowSize + '): ' + count + ' (' + (((new Date).valueOf() - timeBefore.valueOf()) / 1000).toFixed(2) + ' sec)');
});
}
console.log("\n\n\nTesting speakeasy.totp.verify (" + CHUNK_SIZE + " iterations each)\n");
// Run a loop of NUM_TESTS tests, each of NUM_TESTS tests
for (i = 0, newWindowSize = 1; i < NUM_TESTS; ++i, ++newWindowSize, timeBefore = new Date) {
// generate CHUNK_SIZE random tokens of NUM_DIGITS digits
tokens = _.range(0, CHUNK_SIZE).map(e => Math.round(Math.random() * Math.pow(10, NUM_DIGITS)));
// for each token, run rawSpeakEasyValidate
// do it in parallel to make use of multiple processors
async.parallel(tokens.map(token => function (done) {
return done(null, rawSpeakEasyValidate('', token, newWindowSize));
}),
(err, results) => {
// counter up the results for this chunk and display them
count = (results || []).reduce((sum, curr) => sum += +curr, 0);
console.log('false positive count(iterations = ' + CHUNK_SIZE + '; window = ' + newWindowSize + '): ' + count + ' (' + (((new Date).valueOf() - timeBefore.valueOf()) / 1000).toFixed(2) + ' sec)');
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment