{"action":"created","comment":{"url":"https://api.github.com/repos/ericelliott/credential/pulls/comments/22479826","id":22479826,"diff_hunk":"@@ -156,6 +156,59 @@ test('verify with empty password', function(t) {\n \n });\n \n+test('constantEquals', function (t) {\n+  function timed_compare(a, b) {\n+    var start = process.hrtime();\n+    pw.constantEquals(a, b);\n+    return process.hrtime(start)[1];\n+  }\n+  var i,\n+      iterations = 25000,\n+      equal_results = 0,\n+      inequal_results = 0,\n+      difflen_results = 0;\n+  // Ensure it works\n+  t.ok(pw.constantEquals(\"abc\", \"abc\"), 'equality')\n+  t.ok(pw.constantEquals(\"\", \"\"), 'equal-empty')\n+  t.ok(!pw.constantEquals(\"a\", \"\"), 'inequal 1-char')\n+  t.ok(pw.constantEquals(\"a\", \"a\"), 'equal 1-char')\n+  t.ok(!pw.constantEquals(\"ab\", \"ac\"), 'inequal 2-char')\n+  t.ok(pw.constantEquals(\"ab\", \"ab\"), 'equal 2-char')\n+  t.ok(!pw.constantEquals(\"abc\", \"abC\"), 'inequality - difference')\n+  t.ok(!pw.constantEquals(\"abc\", \"abcD\"), 'inequality - addition')\n+  t.ok(!pw.constantEquals(\"abc\", \"ab\"), 'inequality - missing')\n+\n+  // Ensure timing is sane\n+  // Differing lengths\n+  for (i = 0; i < iterations; i++) {\n+    difflen_results += timed_compare(\"abcd\", \"abcdefghijklmnopqrstuvwxyz\");\n+  }\n+\n+  for (i = 0; i < iterations; i++) {\n+    equal_results   += timed_compare(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefghijklmnopqrstuvwxyz\");\n+  }\n+\n+  for (i = 0; i < iterations; i++) {\n+    inequal_results += timed_compare(\"abcdefghijklmnopqrstuvwxyz\", \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\");\n+  }\n+\n+  // This is a point of some statistical importance. A tolerance of ","path":"test/credential-test.js","position":42,"original_position":40,"commit_id":"67efc5b6d972356540370e763be197479a7703a4","original_commit_id":"f8e5608a7b896755af4900e8cc39e4feedcc118f","user":{"login":"ericelliott","id":364727,"avatar_url":"https://avatars.githubusercontent.com/u/364727?v=3","gravatar_id":"","url":"https://api.github.com/users/ericelliott","html_url":"https://github.com/ericelliott","followers_url":"https://api.github.com/users/ericelliott/followers","following_url":"https://api.github.com/users/ericelliott/following{/other_user}","gists_url":"https://api.github.com/users/ericelliott/gists{/gist_id}","starred_url":"https://api.github.com/users/ericelliott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ericelliott/subscriptions","organizations_url":"https://api.github.com/users/ericelliott/orgs","repos_url":"https://api.github.com/users/ericelliott/repos","events_url":"https://api.github.com/users/ericelliott/events{/privacy}","received_events_url":"https://api.github.com/users/ericelliott/received_events","type":"User","site_admin":false},"body":"This is where I'm out of my depth, and I'd really like a statistical timing expert to review our tests. Do you know somebody we could ask?","created_at":"2015-01-05T18:42:41Z","updated_at":"2015-01-05T18:42:41Z","html_url":"https://github.com/ericelliott/credential/pull/13#discussion_r22479826","pull_request_url":"https://api.github.com/repos/ericelliott/credential/pulls/13","_links":{"self":{"href":"https://api.github.com/repos/ericelliott/credential/pulls/comments/22479826"},"html":{"href":"https://github.com/ericelliott/credential/pull/13#discussion_r22479826"},"pull_request":{"href":"https://api.github.com/repos/ericelliott/credential/pulls/13"}}},"pull_request":{"url":"https://api.github.com/repos/ericelliott/credential/pulls/13","id":26827554,"html_url":"https://github.com/ericelliott/credential/pull/13","diff_url":"https://github.com/ericelliott/credential/pull/13.diff","patch_url":"https://github.com/ericelliott/credential/pull/13.patch","issue_url":"https://api.github.com/repos/ericelliott/credential/issues/13","number":13,"state":"open","locked":false,"title":"Proposed constant time string comparison re #12","user":{"login":"brianmhunt","id":135528,"avatar_url":"https://avatars.githubusercontent.com/u/135528?v=3","gravatar_id":"","url":"https://api.github.com/users/brianmhunt","html_url":"https://github.com/brianmhunt","followers_url":"https://api.github.com/users/brianmhunt/followers","following_url":"https://api.github.com/users/brianmhunt/following{/other_user}","gists_url":"https://api.github.com/users/brianmhunt/gists{/gist_id}","starred_url":"https://api.github.com/users/brianmhunt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brianmhunt/subscriptions","organizations_url":"https://api.github.com/users/brianmhunt/orgs","repos_url":"https://api.github.com/users/brianmhunt/repos","events_url":"https://api.github.com/users/brianmhunt/events{/privacy}","received_events_url":"https://api.github.com/users/brianmhunt/received_events","type":"User","site_admin":false},"body":"This is a variant of the canonical method for comparing strings in constant time, regarding issue #12 .\r\n\r\nNote that one has to disable `use strict` to be able to use `with({})`, which one must employ to sidetrack V8 compiler optimizations that ruin the timing characteristics.\r\n\r\nThe unit testing of this PR is rudimentary and does not reveal statistical significance of timing variations.\r\n\r\nIdeally the average deviation (excluding the occasional long run-time of the function) ought to vary by 25µs or so between equal, and inequal (of the same and different lengths) strings.\r\n\r\nIf this design, or a variant of it, is unable to achieve the desired timing characteristic then one would have to fundamentally reconsider how constant time string comparisons would occur in Javascript.","created_at":"2015-01-05T14:48:29Z","updated_at":"2015-01-05T18:42:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3d3ea481eb6847ce8d112c5bffe23dc241ead0c5","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ericelliott/credential/pulls/13/commits","review_comments_url":"https://api.github.com/repos/ericelliott/credential/pulls/13/comments","review_comment_url":"https://api.github.com/repos/ericelliott/credential/pulls/comments/{number}","comments_url":"https://api.github.com/repos/ericelliott/credential/issues/13/comments","statuses_url":"https://api.github.com/repos/ericelliott/credential/statuses/67efc5b6d972356540370e763be197479a7703a4","head":{"label":"brianmhunt:master","ref":"master","sha":"67efc5b6d972356540370e763be197479a7703a4","user":{"login":"brianmhunt","id":135528,"avatar_url":"https://avatars.githubusercontent.com/u/135528?v=3","gravatar_id":"","url":"https://api.github.com/users/brianmhunt","html_url":"https://github.com/brianmhunt","followers_url":"https://api.github.com/users/brianmhunt/followers","following_url":"https://api.github.com/users/brianmhunt/following{/other_user}","gists_url":"https://api.github.com/users/brianmhunt/gists{/gist_id}","starred_url":"https://api.github.com/users/brianmhunt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brianmhunt/subscriptions","organizations_url":"https://api.github.com/users/brianmhunt/orgs","repos_url":"https://api.github.com/users/brianmhunt/repos","events_url":"https://api.github.com/users/brianmhunt/events{/privacy}","received_events_url":"https://api.github.com/users/brianmhunt/received_events","type":"User","site_admin":false},"repo":{"id":28785094,"name":"credential","full_name":"brianmhunt/credential","owner":{"login":"brianmhunt","id":135528,"avatar_url":"https://avatars.githubusercontent.com/u/135528?v=3","gravatar_id":"","url":"https://api.github.com/users/brianmhunt","html_url":"https://github.com/brianmhunt","followers_url":"https://api.github.com/users/brianmhunt/followers","following_url":"https://api.github.com/users/brianmhunt/following{/other_user}","gists_url":"https://api.github.com/users/brianmhunt/gists{/gist_id}","starred_url":"https://api.github.com/users/brianmhunt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brianmhunt/subscriptions","organizations_url":"https://api.github.com/users/brianmhunt/orgs","repos_url":"https://api.github.com/users/brianmhunt/repos","events_url":"https://api.github.com/users/brianmhunt/events{/privacy}","received_events_url":"https://api.github.com/users/brianmhunt/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/brianmhunt/credential","description":"Easy password hashing and verification in Node. Protects against brute force, rainbow tables, and timing attacks.","fork":true,"url":"https://api.github.com/repos/brianmhunt/credential","forks_url":"https://api.github.com/repos/brianmhunt/credential/forks","keys_url":"https://api.github.com/repos/brianmhunt/credential/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brianmhunt/credential/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brianmhunt/credential/teams","hooks_url":"https://api.github.com/repos/brianmhunt/credential/hooks","issue_events_url":"https://api.github.com/repos/brianmhunt/credential/issues/events{/number}","events_url":"https://api.github.com/repos/brianmhunt/credential/events","assignees_url":"https://api.github.com/repos/brianmhunt/credential/assignees{/user}","branches_url":"https://api.github.com/repos/brianmhunt/credential/branches{/branch}","tags_url":"https://api.github.com/repos/brianmhunt/credential/tags","blobs_url":"https://api.github.com/repos/brianmhunt/credential/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brianmhunt/credential/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brianmhunt/credential/git/refs{/sha}","trees_url":"https://api.github.com/repos/brianmhunt/credential/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brianmhunt/credential/statuses/{sha}","languages_url":"https://api.github.com/repos/brianmhunt/credential/languages","stargazers_url":"https://api.github.com/repos/brianmhunt/credential/stargazers","contributors_url":"https://api.github.com/repos/brianmhunt/credential/contributors","subscribers_url":"https://api.github.com/repos/brianmhunt/credential/subscribers","subscription_url":"https://api.github.com/repos/brianmhunt/credential/subscription","commits_url":"https://api.github.com/repos/brianmhunt/credential/commits{/sha}","git_commits_url":"https://api.github.com/repos/brianmhunt/credential/git/commits{/sha}","comments_url":"https://api.github.com/repos/brianmhunt/credential/comments{/number}","issue_comment_url":"https://api.github.com/repos/brianmhunt/credential/issues/comments/{number}","contents_url":"https://api.github.com/repos/brianmhunt/credential/contents/{+path}","compare_url":"https://api.github.com/repos/brianmhunt/credential/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brianmhunt/credential/merges","archive_url":"https://api.github.com/repos/brianmhunt/credential/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brianmhunt/credential/downloads","issues_url":"https://api.github.com/repos/brianmhunt/credential/issues{/number}","pulls_url":"https://api.github.com/repos/brianmhunt/credential/pulls{/number}","milestones_url":"https://api.github.com/repos/brianmhunt/credential/milestones{/number}","notifications_url":"https://api.github.com/repos/brianmhunt/credential/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brianmhunt/credential/labels{/name}","releases_url":"https://api.github.com/repos/brianmhunt/credential/releases{/id}","created_at":"2015-01-04T20:27:33Z","updated_at":"2015-01-05T18:38:37Z","pushed_at":"2015-01-05T18:38:37Z","git_url":"git://github.com/brianmhunt/credential.git","ssh_url":"git@github.com:brianmhunt/credential.git","clone_url":"https://github.com/brianmhunt/credential.git","svn_url":"https://github.com/brianmhunt/credential","homepage":"","size":230,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ericelliott:master","ref":"master","sha":"c7f3267f931d27915bafe49e53bd86a8bb8ec28a","user":{"login":"ericelliott","id":364727,"avatar_url":"https://avatars.githubusercontent.com/u/364727?v=3","gravatar_id":"","url":"https://api.github.com/users/ericelliott","html_url":"https://github.com/ericelliott","followers_url":"https://api.github.com/users/ericelliott/followers","following_url":"https://api.github.com/users/ericelliott/following{/other_user}","gists_url":"https://api.github.com/users/ericelliott/gists{/gist_id}","starred_url":"https://api.github.com/users/ericelliott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ericelliott/subscriptions","organizations_url":"https://api.github.com/users/ericelliott/orgs","repos_url":"https://api.github.com/users/ericelliott/repos","events_url":"https://api.github.com/users/ericelliott/events{/privacy}","received_events_url":"https://api.github.com/users/ericelliott/received_events","type":"User","site_admin":false},"repo":{"id":10586296,"name":"credential","full_name":"ericelliott/credential","owner":{"login":"ericelliott","id":364727,"avatar_url":"https://avatars.githubusercontent.com/u/364727?v=3","gravatar_id":"","url":"https://api.github.com/users/ericelliott","html_url":"https://github.com/ericelliott","followers_url":"https://api.github.com/users/ericelliott/followers","following_url":"https://api.github.com/users/ericelliott/following{/other_user}","gists_url":"https://api.github.com/users/ericelliott/gists{/gist_id}","starred_url":"https://api.github.com/users/ericelliott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ericelliott/subscriptions","organizations_url":"https://api.github.com/users/ericelliott/orgs","repos_url":"https://api.github.com/users/ericelliott/repos","events_url":"https://api.github.com/users/ericelliott/events{/privacy}","received_events_url":"https://api.github.com/users/ericelliott/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ericelliott/credential","description":"Easy password hashing and verification in Node. Protects against brute force, rainbow tables, and timing attacks.","fork":false,"url":"https://api.github.com/repos/ericelliott/credential","forks_url":"https://api.github.com/repos/ericelliott/credential/forks","keys_url":"https://api.github.com/repos/ericelliott/credential/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ericelliott/credential/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ericelliott/credential/teams","hooks_url":"https://api.github.com/repos/ericelliott/credential/hooks","issue_events_url":"https://api.github.com/repos/ericelliott/credential/issues/events{/number}","events_url":"https://api.github.com/repos/ericelliott/credential/events","assignees_url":"https://api.github.com/repos/ericelliott/credential/assignees{/user}","branches_url":"https://api.github.com/repos/ericelliott/credential/branches{/branch}","tags_url":"https://api.github.com/repos/ericelliott/credential/tags","blobs_url":"https://api.github.com/repos/ericelliott/credential/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ericelliott/credential/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ericelliott/credential/git/refs{/sha}","trees_url":"https://api.github.com/repos/ericelliott/credential/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ericelliott/credential/statuses/{sha}","languages_url":"https://api.github.com/repos/ericelliott/credential/languages","stargazers_url":"https://api.github.com/repos/ericelliott/credential/stargazers","contributors_url":"https://api.github.com/repos/ericelliott/credential/contributors","subscribers_url":"https://api.github.com/repos/ericelliott/credential/subscribers","subscription_url":"https://api.github.com/repos/ericelliott/credential/subscription","commits_url":"https://api.github.com/repos/ericelliott/credential/commits{/sha}","git_commits_url":"https://api.github.com/repos/ericelliott/credential/git/commits{/sha}","comments_url":"https://api.github.com/repos/ericelliott/credential/comments{/number}","issue_comment_url":"https://api.github.com/repos/ericelliott/credential/issues/comments/{number}","contents_url":"https://api.github.com/repos/ericelliott/credential/contents/{+path}","compare_url":"https://api.github.com/repos/ericelliott/credential/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ericelliott/credential/merges","archive_url":"https://api.github.com/repos/ericelliott/credential/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ericelliott/credential/downloads","issues_url":"https://api.github.com/repos/ericelliott/credential/issues{/number}","pulls_url":"https://api.github.com/repos/ericelliott/credential/pulls{/number}","milestones_url":"https://api.github.com/repos/ericelliott/credential/milestones{/number}","notifications_url":"https://api.github.com/repos/ericelliott/credential/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ericelliott/credential/labels{/name}","releases_url":"https://api.github.com/repos/ericelliott/credential/releases{/id}","created_at":"2013-06-09T17:11:42Z","updated_at":"2015-01-05T17:27:08Z","pushed_at":"2015-01-04T09:31:15Z","git_url":"git://github.com/ericelliott/credential.git","ssh_url":"git@github.com:ericelliott/credential.git","clone_url":"https://github.com/ericelliott/credential.git","svn_url":"https://github.com/ericelliott/credential","homepage":"","size":230,"stargazers_count":97,"watchers_count":97,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"open_issues_count":3,"forks":8,"open_issues":3,"watchers":97,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ericelliott/credential/pulls/13"},"html":{"href":"https://github.com/ericelliott/credential/pull/13"},"issue":{"href":"https://api.github.com/repos/ericelliott/credential/issues/13"},"comments":{"href":"https://api.github.com/repos/ericelliott/credential/issues/13/comments"},"review_comments":{"href":"https://api.github.com/repos/ericelliott/credential/pulls/13/comments"},"review_comment":{"href":"https://api.github.com/repos/ericelliott/credential/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/ericelliott/credential/pulls/13/commits"},"statuses":{"href":"https://api.github.com/repos/ericelliott/credential/statuses/67efc5b6d972356540370e763be197479a7703a4"}}}}