{
 "number": 36409,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/36409",
 "title": "sign: use fresh randomness as BIP340 auxiliary data",
 "author": "fametrano",
 "author_association": "FIRST_TIME_CONTRIBUTOR",
 "created_at": "2026-10-02T07:04:35Z",
 "updated_at": "2026-10-31T14:23:11Z",
 "description_hash": "d148afb32413dca2",
 "age_days": 30,
 "draft": false,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "1f274eae23f415ab29e22d25a846844629c672ad",
 "head_ref": "sign-bip340-aux-rand",
 "head_repo": "fametrano/bitcoin",
 "head_history": [],
 "additions": 17,
 "deletions": 4,
 "changed_files": 4,
 "commit_count": 1,
 "size_bucket": "S",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "ack": [
     {
      "login": "Yudis-bit",
      "url": "https://github.com/bitcoin/bitcoin/pull/36409#issuecomment-5974564439"
     }
    ]
   },
   "conflicts": []
  }
 },
 "acks_parsed": {
  "Yudis-bit": {
   "kind": "ack",
   "hash": "1f274eae23",
   "t": "2026-10-03T23:23:30Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 1,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "Yudis-bit"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-10-03T18:31:08Z",
  "last_reviewer_activity": "2026-10-03T23:23:30Z",
  "last_reviewer": "Yudis-bit",
  "author_silent_days": 29,
  "waiting_on_author_days": 29,
  "days_since_update": 1
 },
 "refs": {
  "mentioned": [
   31883
  ],
  "depends_on": [],
  "fixes": [
   31883
  ],
  "linked_issues": [
   {
    "number": 31883,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "SignatureCreator should supply auxiliary data argument for additional bip340 signature security"
   }
  ],
  "references": [
   {
    "number": 31883,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "SignatureCreator should supply auxiliary data argument for additional bip340 signature security"
   }
  ],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [],
 "body": "Bitcoin Core signs Taproot inputs with all-zero BIP340 auxiliary data. BIP340 recommends fresh randomness there, as protection against fault injection and side-channel attacks. I made `CreateSchnorrSig` pass `GetRandHash()` instead, unless `SignOptions::aux_rand` sets a fixed value.\n\nIn the unit tests, only `script_tests/bip341_keypath_test_vectors` depended on the zero value: the BIP341 vectors were made with zero auxiliary data. It now builds the signature creator with `aux_rand` set to zero and compares its signature byte for byte with the vectors, then signs twice with default options and checks that the two signatures differ. If `CreateSchnorrSig` ignores `aux_rand`, the vector check fails for all 7 key path inputs. The signing-related functional tests pass unchanged. The `script_sign` fuzz target can now reach `GetRandHash()`, so it seeds the RNG like the other signing targets.\n\nI chose `GetRandHash()` over `GetStrongRandBytes()`. Both draw from the same RNG, which is seeded with OS entropy at startup, so the output is unpredictable; `GetRandHash()` skips the OS call `GetStrongRandBytes()` makes on each draw. BIP340 says any non-repeating value increases protection against fault injection, and that no security property other than side-channel resistance depends on the quality of this randomness. In unit tests `GetRandHash()` follows the test seed, so the tests stay reproducible; `GetStrongRandBytes()` does not.\n\nDefault signing is no longer deterministic. `SignOptions::aux_rand` sets a fixed auxiliary value where one is needed, as in the vector test; Yudis-bit suggested it.\n\nFixes #31883.\n\n*Made with my usual tools: a computer, the Internet and an LLM. The mistakes, as usual, are all mine.*",
 "commits": [
  {
   "sha": "1f274eae23f415ab29e22d25a846844629c672ad",
   "date": "2026-10-03T18:02:46Z",
   "message": "sign: use fresh randomness as BIP340 auxiliary data\n\nMutableTransactionSignatureCreator::CreateSchnorrSig passed all-zero\nauxiliary data to SignSchnorr. BIP340 recommends fresh randomness there,\nfor protection against fault injection and side-channel attacks. Pass\nGetRandHash() instead, unless the new SignOptions::aux_rand sets a\nfixed value.\n\nThe BIP341 key path vectors were generated with zero auxiliary data.\nThe test now sets aux_rand to zero to compare the creator's signature\nwith the vectors, and checks that two signatures made with the default\noptions differ.\n\nThe script_sign fuzz target can now reach GetRandHash() through\nSignTransaction, so it seeds the global RNG like the other signing\ntargets."
  }
 ],
 "timeline": [
  {
   "t": "2026-10-03T16:33:20Z",
   "kind": "comment",
   "who": "Yudis-bit",
   "assoc": "NONE",
   "text": "Tested on be2bc19726.\n\nI think `GetRandHash()` is definitely the right choice here. For BIP340, nonce generation already binds the secret key with the message via tagged hashes, so auxiliary randomness is purely for fault injection and side-channel mitigation rather than baseline entropy. Skipping the `GetStrongRandBytes()` OS syscall keeps transaction signing fast, and crucially keeps fuzzing and unit tests deterministic under `SeedRandomStateForTest(SeedRand::ZEROS)`.\n\nRegarding your question about `SignOptions`: I'd definitely lean towards adding `std::optional<uint256> aux_rand` there.\n\nNotice what happens in `src/test/script_tests.cpp`: because `MutableTransactionSignatureCreator` doesn't accept an aux value, the test has to bypass the creator entirely and call `key.SignSchnorr(...)` directly. But the whole point of that test case in `script_tests.cpp` was to test `MutableTransactionSignatureCreator`'s end-to-end handling of the BIP-341 witness generation against the official vectors.\n\nIf `SignOptions` has:\n\n```cpp\nstruct SignOptions {\n    int sighash_type{SIGHASH_DEFAULT};\n    std::optional<uint256> aux_rand{std::nullopt};\n};\n```\n\nand in `MutableTransactionSignatureCreator::CreateSchnorrSig`:\n\n```cpp\nconst uint256 aux_rand = m_options.aux_rand.value_or(GetRandHash());\nif (!key.SignSchnorr(*hash, sig, merkle_root, aux_rand)) return false;\n```\n\nThen default transaction signing keeps fresh randomness, but `script_tests` can still test the creator against the exact BIP-341 vector:\n\n```cpp\nMutableTransactionSignatureCreator creator_zero_aux(tx, txinpos, utxos[txinpos].nValue, &txdata, {.sighash_type = hashtype, .aux_rand = uint256{}});\nstd::vector<unsigned char> zero_aux_sig;\nBOOST_CHECK(creator_zero_aux.CreateSchnorrSig(provider, zero_aux_sig, pubkey, nullptr, &merkle_root, SigVersion::TAPROOT));\nBOOST_CHECK_EQUAL(HexStr(zero_aux_sig), expected);\n```\n\nVerified this locally with `bip341_wallet_vectors.json`. All 7 keypath test vector inputs reproduce the expected witness signature directly through the creator, alongside the two differing non-deterministic signatures under default options.",
   "id": "c:5971122715",
   "url": "https://github.com/bitcoin/bitcoin/pull/36409#issuecomment-5971122715",
   "hash": "e186d0f3d9354a17",
   "edited": null
  },
  {
   "t": "2026-10-03T18:31:08Z",
   "kind": "comment",
   "who": "fametrano",
   "assoc": "NONE",
   "text": "Done in 1f274eae23, as you suggested: `SignOptions::aux_rand`, defaulting to `GetRandHash()`. The vector test goes through the creator again with `aux_rand = uint256{}`, and two default-options signatures are checked to differ. With `aux_rand` ignored, the vector check fails for all 7 inputs. PR description updated. Thanks.",
   "id": "c:5972197429",
   "url": "https://github.com/bitcoin/bitcoin/pull/36409#issuecomment-5972197429",
   "hash": "233a0e42283c953a",
   "edited": null
  },
  {
   "t": "2026-10-03T23:23:30Z",
   "kind": "comment",
   "who": "Yudis-bit",
   "assoc": "NONE",
   "text": "ACK 1f274eae23\n\nRe-reviewed the commit. Cleanly addresses the test vector coupling by keeping `SignOptions::aux_rand` optional while defaulting to `GetRandHash()`. Verified that `bip341_keypath_test_vectors` reproduces the BIP-341 witness with zero aux and confirms divergence under default options, with `script_sign` fuzzing seeded deterministically.",
   "id": "c:5974564439",
   "url": "https://github.com/bitcoin/bitcoin/pull/36409#issuecomment-5974564439",
   "hash": "a06054fc4783791f",
   "edited": null
  }
 ],
 "labels_log": [
  {
   "t": "2026-10-02T08:32:58Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-03T19:42:14Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [],
 "text_chars": 5295,
 "text_tokens_estimate": 1323,
 "changed_paths": [
  "src/script/sign.cpp",
  "src/script/sign.h",
  "src/test/fuzz/script_sign.cpp",
  "src/test/script_tests.cpp"
 ],
 "files": [
  {
   "path": "src/script/sign.cpp",
   "add": 4,
   "del": 2
  },
  {
   "path": "src/script/sign.h",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/fuzz/script_sign.cpp",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/script_tests.cpp",
   "add": 9,
   "del": 2
  }
 ],
 "test_lines": 13,
 "git": {
  "head": "1f274eae23f415ab29e22d25a846844629c672ad",
  "head_matches_backup": true,
  "base": "7988380da987c0b860885853ace8149b84ac2ee8",
  "patch_id": "3d519e73faad893f",
  "commits": [
   {
    "sha": "1f274eae23",
    "subject": "sign: use fresh randomness as BIP340 auxiliary data",
    "files": 4,
    "add": 17,
    "del": 4
   }
  ],
  "patch_truncated": false
 },
 "patch_id": "3d519e73faad893f",
 "input_hash": "5f2a81d80d9edb6d",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}