{"id":18894177,"url":"https://github.com/trailofbits/decree","last_synced_at":"2025-09-04T11:31:37.059Z","repository":{"id":230035502,"uuid":"749869568","full_name":"trailofbits/decree","owner":"trailofbits","description":"Decree Fiat Shamir Library","archived":false,"fork":false,"pushed_at":"2024-03-27T14:01:37.000Z","size":57,"stargazers_count":12,"open_issues_count":2,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-11-08T08:20:49.886Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trailofbits.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-01-29T15:04:11.000Z","updated_at":"2024-09-10T13:30:32.000Z","dependencies_parsed_at":"2024-03-27T14:28:45.778Z","dependency_job_id":"0784102e-c950-4c7a-b5bc-b80f7034ffa4","html_url":"https://github.com/trailofbits/decree","commit_stats":null,"previous_names":["trailofbits/decree"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fdecree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fdecree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fdecree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trailofbits%2Fdecree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trailofbits","download_url":"https://codeload.github.com/trailofbits/decree/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231955972,"owners_count":18451509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T08:19:39.151Z","updated_at":"2024-12-31T07:23:21.928Z","avatar_url":"https://github.com/trailofbits.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Decree Fiat-Shamir Library\n\nThe Decree library provides a set of tools that help developers prevent and identify [weak\nFiat-Shamir problems](https://eprint.iacr.org/2023/691) in their zero-knowledge (ZK) and\nmulti-party computation (MPC) software.\n\n## The `Inscribe` trait\n\nThe `Inscribe` trait allows developers to include useful contextual information for Fiat-Shamir\ntranscripts.  In the case of an elliptic curve point, for example, this might include the curve\nparameters (or, in the case of curves with well-known parameters, such as `secp256k1` or\n`ed25519`, simply the name of the curve). For an integer in a finite field, the field order might\nbe included.\n\nIn more complex systems like PLoNK or Bulletproofs, it's possible that _multiple_ parameters\ncould be included as contextual information.\n\nThe trait is derivable, allowing for recursive inclusion of contextual information: if a struct\nmember supports `Inscribe`, then it will be included with _its_ contextual information as well.\nStruct members that do _not_ support the `Inscribe` trait can be tagged for inclusion using\nthe `bcs` library, which serializes values in a way that is canonical and deterministic: given the\nsame data type with the same value, then regardless of the platform or operating system, the\nserialized result will be the same.\n\nStructs that use `#[derive(Inscribe)]` can specify `#[inscribe_addl(\u003cfunction\u003e)]`, where\n`function` can return any contextual information not included in the struct, whether explicitly\nor implicitly via `Inscribe` members. This is where implementers can include things like domain\nparameters, protocol versions (if such information is important), related values, etc.\n\nMember values and contextual information in `#[derive(Inscribe)]` structs are combined using\nTupleHash, which is derived from the SHA-3 hash function. This prevents issues with domain\nseparation and canonicalization.\n\nStructs with the `Inscribe` trait also provide a name or \"mark\". By default, the mark is just the\nname of the name of the struct; developers can override this by defining the `get_mark` method.\nSince many cryptographic libraries include distinct structures with the same name (think of\nstructs named `PublicKey` or `Proof`), it's a good idea to do so.\n\n## `Decree` transcripts\n\n### Overview\n\nA Decree transcript sits atop a [Merlin transcript](https://github.com/zkcrypto/merlin), adding\na protocol specification and enforcement mechanism. At each stage of a protocol, developers\nare required to specify the inputs and challenges for each step of a Fiat-Shamir transcript. If a\nspecified input is provided more than once, an `Error` is returned. If an input that is not\nincluded in the specification is not provided, an `Error` is returned.\n\nIf a challenge is requested before all specified inputs have been provided, an `Error` is returned.\nIf a challenge is requested out of the specified order, an `Error` is returned. If a challenge that\nis not not included in the specification is requested, an `Error` is returned.\n\nAfter challenges have been successfully generated, the Decree transcript can be \"continued\" by\nspecifying a new set of inputs and challenges. The underlying Merlin transcript is kept in place,\nensuring that transcript state is carried over to the new stage in the protocol.\n\nAdditionally, inputs at each stage are added to the Merlin transcript in a fixed order, regardless\nof the order in which they are added to the Decree transcript. If two programs provide the same\ninputs and request the same challenges, then the challenges will always match, even if they add\nthe inputs in different orders.\n\nIn other words: a Decree transcript requires developers to specify the contents of their\nFiat-Shamir transcripts in distinct stages, then _holds developers to that spec_, even as it\nmakes room for implementation flexibility.\n\nDecree transcripts ensure that:\n\n  - Transcript formats are specified at a single point per stage, not across entire functions\n  - Critical transcript inputs aren't skipped\n  - Transcript inputs are not multiply-specified\n  - Changes to the order of inputs don't result in incompatibility problems\n  - Challenges are generated in a specified order\n\n\n### Supported types\n\nOnce a `Decree` struct has been initialized or extended, inputs can be added using the `add` and\n`add_serial` methods.\n\nThe `add` method is meant for inputs that support the `Inscribe` trait, described above. Where\nsupported, this is the preferred method for adding data to a Decree transcript, as types that\nimplement `Inscribe` are less likely to have colliding transcript inputs across multiple\nparameterizations.\n\nThe `add_serial` method can be used for any input that supports the `Serialize` trait. This\nmethod uses the `bcs` (binary canonical serialization) library to serialize the input to a unique,\nplatform-independent binary representation, which is then used as the direct input to the Merlin\ntranscript. This ensures that serialized values don't change from platform to platform, as long\nas the data structures remain the same.\n\nBecause `Serialize` is implemented for `\u0026[u8]`, it is possible to serialize values \"by hand\" and\nfeed the resulting slice to the `add_serial` method. It is worth noting, however, that the\n`bcs` library is still used to serialize the `\u0026[u8]` input, so the result will not be the same\nas directly feeding the slice into the underlying Merlin transcript.\n\n### Example: Schnorr Proof\n\nConsider the following example from the doctests, a Schnorr proof that Alice knows the base-`43`\ndiscrete logarithm of `8675309` modulo a shared modulus of `0x1fffffffffffffff` (2^127 - 1).\n\n```rs\n    let inputs: [InputLabel; 4] = [\"modulus\", \"base\", \"target\", \"u\"];\n    let challenges: [ChallengeLabel; 1] = [\"c_challenge\"];\n    let mut transcript = Decree::new(\"schnorr proof\", \u0026inputs, \u0026challenges)?;\n\n    // Proof parameters\n    let target = BigInt::from(8675309u32);\n    let base = BigInt::from(43u32);\n    let log = BigInt::parse_bytes(b\"18777797083714995725967614997933308615\", 10).unwrap();\n    let modulus = \u0026BigInt::from(2u32).pow(127) - BigInt::from(1u32);\n\n    // Random exponent\n    let mut rng = rand::thread_rng();\n    let randomizer_exp = rng.gen_bigint(256) % (\u0026modulus - BigInt::from(1u32));\n    let randomizer = base.modpow(\u0026randomizer_exp, \u0026modulus);\n\n    // Add everything to the transcript-- note that order of addition doesn't matter!\n    transcript.add_serial(\"u\", \u0026randomizer);\n    transcript.add_serial(\"target\", \u0026target);\n    transcript.add_serial(\"base\", \u0026base);\n    transcript.add_serial(\"modulus\", \u0026modulus);\n\n    let mut challenge_out: [u8; 32] = [0u8; 32];\n    transcript.get_challenge(\"c_challenge\", \u0026mut challenge_out);\n```\n\n(Note: this code should not be _used_ for a variety of reasons; it is for illustrative purposes\nonly.)\n\nWe could extend this by adding an `Inscribe` implementation for the `target` and `base` values to\nindicate the associated modulus:\n\n```rs\n#[derive(Inscribe)]\npub struct BigIntTarget {\n   #[inscribe(serialize)]\n   target: BigInt,\n   #[inscribe(serialize)]\n   base: BigInt,\n   #[inscribe(serialize)]\n   modulus: BigInt,\n}\n\nimpl BigIntTarget {\n   fn get_extra(\u0026self) -\u003e Result\u003cVec\u003cu8\u003e, Error\u003e {\n       Ok(\"schnorr proof value\".as_bytes().to_vec())\n   }\n}\n\n [...]\n\n   let inputs: [InputLabel; 3] = [\"modulus\", \"target\", \"u\"];\n   let challenges: [ChallengeLabel; 1] = [\"c_challenge\"];\n   let mut transcript = Decree::new(\"schnorr proof\", \u0026inputs, \u0026challenges)?;\n\n   // Proof parameters\n   let modulus = \u0026BigInt::from(2u32).pow(127) - BigInt::from(1u32);\n   let base = BigInt::from(43u32);\n   let target = BigIntTarget{\n       target: BigInt::from(8675309u32),\n       base: base.clone(),\n       modulus: modulus.clone()};\n   let log = BigInt::from_str(\"18777797083714995725967614997933308615\").unwrap();\n\n   // Random exponent\n   let mut rng = rand::thread_rng();\n   let randomizer_exp = rng.gen_bigint(128).abs();\n   let randomizer_int = base.modpow(\u0026randomizer_exp, \u0026modulus);\n\n   // Add everything to the transcript-- note that order doesn't matter!\n   transcript.add_serial(\"modulus\", \u0026modulus);\n   transcript.add_serial(\"u\", \u0026randomizer_int);\n   transcript.add(\"target\", target);\n\n   // Generate challenge\n   let mut challenge_buffer: [u8; 16] = [0u8; 16];\n   transcript.get_challenge(\"c_challenge\", \u0026mut challenge_buffer)?;\n   let challenge_int = BigInt::from_bytes_le(Sign::Plus, \u0026challenge_buffer);\n\n   // Final proof value\n   let z = (challenge_int * log) + randomizer_int.clone();\n\n[...]\n\n```\n\n(Note: again, this code should not be used for a variety of reasons; it is for illustrative\npurposes only.)\n\nIn this case, we've \"forgotten\" to add the `base` input to the transcript, which would normally be\n[a major Fiat-Shamir vulnerability](). But we're still okay, because the `target` parameter supports\nthe `Inscribe` trait, and `base` is included in the transcript calculation.  If somebody tries to\ncheat by replacing `base` with a maliciously-crafted value, the verifier will see a different\nchallenge value than would be generated for a different `base` value.\n\nAlso worth noting: this approach includes `modulus` twice. Once explicitly via an `add_serial`\ncall, and once in the call to the `get_inscription` method of `target` that gets made by `add`.\nThis is okay; the implicit inclusion in `target` does not preclude inclusion elsewhere.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrailofbits%2Fdecree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrailofbits%2Fdecree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrailofbits%2Fdecree/lists"}