241 Commits

Author SHA1 Message Date
Yush G
48656fab1f Add deepwiki badge 2025-06-06 15:18:42 -07:00
skaunov
03e132597f improve plume_arkworks::sec1_affine to be used downstream 2025-06-06 17:42:10 +03:00
Sergey Kaunov
2bca61f809 Pr (#130)
* fix `zeroize` minimal version

* fix `dev-dependencies` minimal versions

* re-exports `rand` from `arkworks`
2025-06-05 23:25:36 +03:00
skaunov
385f35dd7c Fill in the Pallas implementation. 2025-05-30 04:44:36 +03:00
skaunov
c281e3223b plume_arkworks improvement 2025-05-28 19:21:59 +03:00
Sergey Kaunov
3798b4d3d2 Solve #120 (#126)
* rescue <javascript> `v2` and change the paths in <test> to the rescued thing

* reflect the changed paths in the instruction
2025-05-27 23:33:59 +03:00
skaunov
ad5df31ac9 from #121 and suits repo navigation 2025-05-26 22:40:22 +03:00
Sergey Kaunov
091114ea2e Merge pull request #124 from plume-sig:pr
implements separate public and private inputs in API #113 for the package
2025-05-24 14:28:57 +03:00
skaunov
aa341b06e2 implements separate public and private inputs in API #113 for the package 2025-05-24 14:27:47 +03:00
Sergey Kaunov
d6b80bc2e3 Merge pull request #123 from plume-sig:pr
re-export few items needed to `use` with Poseidon
2025-05-24 14:25:31 +03:00
skaunov
5d83129648 re-export few items needed to use with Poseidon 2025-05-24 14:22:18 +03:00
Sergey Kaunov
5a5c11fc2b Merge pull request #117 from plume-sig/arkworks04
Bump to the recent `arkworks` (`v0.5`)
2024-12-15 03:38:20 +03:00
skaunov
1f52416c1d remove patches from the workspace 2024-11-29 11:25:33 +03:00
skaunov
7b8c77a60f [plume_arkworks] fmt 2024-11-17 10:33:09 +03:00
skaunov
f7206ab581 upgrade <rust-arkworks> to the current arkworks version
# "Hazmat"
was removed since `arkworks` security model differs significantly from RustCrypto, and approaching it from the standpoint of the PLUME crate published earlier isn't really a correct way. \
Still I'd like to have a better look into `zeroize` for this crate before calling it `v.0.1`.
# `test_vectors`
were taken from the deprecated crate for SEC1 encoding testing.
2024-11-17 10:21:57 +03:00
Sergey Kaunov
c835718a4c Merge pull request #116 from plume-sig/release3
promote `rc`
2024-08-10 15:24:22 +03:00
skaunov
1b8af975e1 promote rc 2024-08-10 15:19:17 +03:00
Yush G
a17da7f347 Added noir plume link 2024-08-02 20:49:39 -04:00
Yush G
dd45306634 Added link to Noir PLUME 2024-08-02 20:49:16 -04:00
Sergey Kaunov
92315e24f4 Wasm readability (#115)
reorg code for better reading through
2024-07-17 12:43:15 +03:00
Sergey Kaunov
83e3ed91f0 Replaces NPM package with Wasm wrapper
Resolves #114

removes respective GA
this one should be tested like a package I guess,
ideas for such tests are welcome as issues

Couple of things left out of the committed code.

# Subtle
I was really late to understand that Subtle crypto supports the different curve `secp256r`, *and* it doesn't provide a facility to store secret values. So implementation for `web_sys::SecretKey` turned out to be just extra miles leading nowhere.
```toml
web-sys = { version = "0.3", features = ["CryptoKey", "SubtleCrypto", "Crypto", "EcKeyImportParams"] }
wasm-bindgen-futures = "0.4"
```
```rust
#[wasm_bindgen]
extern "C" {
    // Return type of js_sys::global()
    type Global;
    // // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
    // type WebCrypto;
    // Getters for the WebCrypto API
    #[wasm_bindgen(method, getter)]
    fn crypto(this: &Global) -> web_sys::Crypto;
}

// `fn sign`
if sk.type_() != "secret" {return Err(JsError::new("`sk` must be secret key"))}
if !js_sys::Object::values(&sk.algorithm().map_err(
    |er|
        JsError::new(er.as_string().expect("TODO check this failing").as_str())
)?).includes(&JsValue::from_str("P-256"), 0) {return Err(JsError::new("`sk` must be from `secp256`"))}

// this was my approach, but seems I got what they did at <https://github.com/rust-random/getrandom/blob/master/src/js.rs>
// js_sys::global().entries().find(); // TODO throw if no Crypto in global

let global_the: Global = js_sys::global().unchecked_into();
let crypto_the: web_sys::Crypto = global_the.crypto();
let subtle_the = crypto_the.subtle();
let sk = JsFuture::from(subtle_the.export_key("pkcs8", &sk)?).await?;

// ...
::from_pkcs8_der(js_sys::ArrayBuffer::from(sk).try_into()?)?;
    zeroize::Zeroizing::new(js_sys::Uint8Array::from(JsFuture::from(subtle_the.export_key("pkcs8", &sk).map_err(
        |er|
            Err(JsError::new(er.as_string().expect("TODO check this failing").as_str()))
        )?).await?).to_vec());

// ...

// `fn try_into`

// ...

// zeroization protection ommitted here due to deprecation // <https://github.com/plume-sig/zk-nullifier-sig/issues/112>
// mostly boilerplate from signing; also some excessive ops left for the same reason
// TODO align error-handling in this part
if self.c.type_() != "secret" {return Err(JsError::new("`c` must be secret key"))}
if !js_sys::Object::values(&self.c.algorithm()?).includes(js_sys::JsString::from("P-256").into(), 0) {return Err(JsError::new("`c` must be from `secp256`"))}
this was my approach, but seems I got what they did at <https://github.com/rust-random/getrandom/blob/master/src/js.rs>
js_sys::global().entries().find(); // TODO throw if no Crypto in global
let global_the: Global = js_sys::global().unchecked_into();
let crypto_the: web_sys::Crypto = global_the.crypto();
let subtle_the = crypto_the.subtle();
let c_pkcs = //zeroize::Zeroizing::new(
    js_sys::Uint8Array::from(JsFuture::from(subtle_the.export_key("pkcs8", &self.c)?).await?).to_vec();
// );
let c_scalar = &plume_rustcrypto::SecretKey::from_pkcs8_der(&c_pkcs)?.to_nonzero_scalar();
sk_z.zeroize();

// ...
```

# randomness
Somehow I thought Wasm doesn't have access to RNG, so I used a seedable one and required the seed. Here's how `sign` `fn` was different.
```rust
// Wasm environment doesn't have a suitable way to get randomness for the signing process, so this instantiates ChaCha20 RNG with the provided seed.
// @throws a "crypto error" in case of a problem with the secret key, and a verbal error on a problem with `seed`
// @param {Uint8Array} seed - must be exactly 32 bytes.
pub fn sign(seed: &mut [u8], v1: bool, sk: &mut [u8], msg: &[u8]) -> Result<PlumeSignature, JsError> {
    // ...

    let seed_z: zeroize::Zeroizing<[u8; 32]> = zeroize::Zeroizing::new(seed.try_into()?);
    seed.zeroize();

    // TODO switch to `wasi-random` when that will be ready for crypto
    let sig = match v1 {
        true => plume_rustcrypto::PlumeSignature::sign_v1(
            &sk_z, msg, &mut rand_chacha::ChaCha20Rng::from_seed(seed_z)
        ),
        false => plume_rustcrypto::PlumeSignature::sign_v2(
            &sk_z, msg, &mut rand_chacha::ChaCha20Rng::from_seed(seed_z)
        ),
    };

    let sig = signer.sign_with_rng(
        &mut rand_chacha::ChaCha20Rng::from_seed(*seed_z), msg
    );

    // ...
}
```

# `BigInt` conversion
It was appealing to leave `s` as `BigInt` (see the comments), but that seems to be confusing and hinder downstream code reusage. There's an util function left for anybody who would want to have it as `BigInt`, but leaving the contraty function makes less sense and also makes the thing larger. So let me left it here for reference.
```rust
let scalar_from_bigint =
    |n: js_sys::BigInt| -> Result<plume_rustcrypto::NonZeroScalar, anyhow::Error> {
        let result = plume_rustcrypto::NonZeroScalar::from_repr(k256::FieldBytes::from_slice(
            hex::decode({
                let hexstring_freelen = n.to_string(16).map_err(
                    |er|
                        anyhow::Error::msg(er.as_string().expect("`RangeError` can be printed out"))
                )?.as_string().expect("on `JsString` this always produce a `String`");
                let l = hexstring_freelen.len();
                if l > 32*2 {return Err(anyhow::Error::msg("too many digits"))}
                else {["0".repeat(64-l), hexstring_freelen].concat()}
            })?.as_slice()
        ).to_owned());
        if result.is_none().into() {Err(anyhow::Error::msg("isn't valid `secp256` non-zero scalar"))}
        else {Ok(result.expect(EXPECT_NONEALREADYCHECKED))}
    };
```
2024-07-17 03:15:22 +03:00
Sergey Kaunov
bb24ab0ecf Resolve #94 (#109)
* semantically finished

* `fmt`

* Update Cargo.toml

version bump
2024-04-27 16:45:10 +03:00
Sergey Kaunov
7893200dc9 Copy keywords from the crate (#108)
> ...
so, I will take the tags from a create, btw
>> and the npm js package has no keywords, if we can add some for indexing, that would be great
2024-04-17 21:34:21 +03:00
Sergey Kaunov
1fa9c09f3b Resolve #86 (#104)
`_arkworks` checked, `_rustcrypto` updated
2024-04-09 19:24:56 +03:00
Sergey Kaunov
d0ceebc44d Readme (#107)
* usage example correction

* version bump for publishing
2024-03-10 16:43:38 +03:00
Sergey Kaunov
83dc5ff82b Readme (#106)
* just some readme stuff for @Divide-by-0

* some requested edits

* some requested edits

* some requested edits

* version bump for publishing
2024-03-10 16:28:40 +03:00
Yush G
14fc2f89e8 Update README.md 2024-03-09 15:57:01 -08:00
Yush G
5123acf585 Add installation guides 2024-03-09 15:17:27 -08:00
Yush G
ff7d074649 Fix dead geometry link, add zk snap 2024-03-09 15:03:40 -08:00
Yush G
bc1d2f873d Update Mina references 2024-03-05 16:25:31 -08:00
Sergey Kaunov
e956b27ee1 Draft of <README.MD> for NPM (#103)
I believe these should be organized other way (divide the monorepo?). But @Divide-by-0 seems to really need something to start with rn
2024-02-29 23:46:39 +03:00
Sergey Kaunov
a44d60c78e Npm repository link (#102)
* add the link to the repo for NPM

* version bump / <npmjs> sync
2024-02-29 18:52:09 +03:00
Yush G
5bb3dda0d6 Add docs and tests (#100)
See issues #67 and #84 .

Few snippets of other paths I considered.
```rust
        ...
        [ProjectivePoint::GENERATOR.to_encoded_point(true).as_bytes(), &pk_bytes, enc!(hashed_to_curve)].map(|b| hashers[0].update(b));
        for b in [enc!(nullifier), enc![r_point], enc!(hashed_to_curve_r)] {
            hashers[0].update(b);
            hashers[1].update(b);
        }
        
        let c = hashers.map(|h| h.finalize());
        let c_scalar = c.clone().map(|c_i| NonZeroScalar::reduce_nonzero(U256::from_be_byte_array(c_i)));
        // Compute s = r + sk ⋅ c
        let s_scalar = c_scalar.map(|c_scalar_i| NonZeroScalar::new(*r_scalar + *(self.to_nonzero_scalar() * c_scalar_i))
            .expect("something is terribly wrong if the nonce is equal to negated product of the secret and the hash"));
        
        Ok(PlumeSignatureCombined{
            message: msg.to_owned(),
            pk: pk.into(),
            nullifier: nullifier.to_point(),
            v2_c: c[1],
            v2_s: *s_scalar[1],
            v1_c: c[0],
            v1_s: *s_scalar[0],
            v1_r_point: r_point.into(),
            v1_hashed_to_curve_r: hashed_to_curve_r.to_point(),
        })
    }
}
/// Struct holding mandatory signature data for ... PLUME signature
#[derive(Debug)]
pub struct PlumeSignatureCombined {
    /// The message that was signed.
    pub message: Vec<u8>,
    /// The public key used to verify the signature.
    pub pk: ProjectivePoint,
    /// The nullifier.
    pub nullifier: ProjectivePoint,
    /// Part of the signature data.
    pub v2_c: Output<Sha256>,
    /// Part of the signature data, a scalar value.
    pub v2_s: Scalar,
    /// Part of the signature data.
    pub v1_c: Output<Sha256>,
    /// Part of the signature data, a scalar value.
    pub v1_s: Scalar,
    /// Part of the V1 signature data, a curve point.  
    pub v1_r_point: ProjectivePoint,
    /// Part of the V1 signature data, a curve point.
    pub v1_hashed_to_curve_r: ProjectivePoint,
}
impl PlumeSignatureCombined {
    pub fn separate(self) -> (PlumeSignature, PlumeSignature) {
        let (pk, nullifier) = (self.pk, self.nullifier); 
        (
            PlumeSignature{ 
                message: self.message.clone(), pk, nullifier, c: self.v1_c, s: self.v1_s, v1specific: Some(
                    PlumeSignatureV1Fields{ r_point: self.v1_r_point, hashed_to_curve_r: self.v1_hashed_to_curve_r }
                ) 
            }, 
            PlumeSignature{ message: self.message, pk, nullifier, c: self.v2_c, s: self.v2_s, v1specific: None }, 
        )
    }
}
```
_____________________________________
```rust
pub enum PlumeSignature{
    V1(PlumeSignatureV1),
    V2(PlumeSignatureV2),
}
pub struct PlumeSignatureV1 {
    v2: PlumeSignatureV2,
    v1: PlumeSignatureV1Fields
}
/// Struct holding mandatory signature data for a PLUME signature.
pub struct PlumeSignatureV2 {
    /// The message that was signed.
    pub message: Vec<u8>,
    /// The public key used to verify the signature.
    pub pk: ProjectivePoint,
    /// The nullifier.
    pub nullifier: ProjectivePoint,
    /// Part of the signature data.
    pub c: Output<Sha256>,
    /// Part of the signature data, a scalar value.
    pub s: Scalar,
    // /// Optional signature data for variant 1 signatures.
    // pub v1: Option<PlumeSignatureV1Fields>,
}
/// struct holding additional signature data used in variant 1 of the protocol.
#[derive(Debug)]
pub struct PlumeSignatureV1Fields {
    /// Part of the signature data, a curve point.  
    pub r_point: ProjectivePoint,
    /// Part of the signature data, a curve point.
    pub hashed_to_curve_r: ProjectivePoint,
}

impl signature::RandomizedSigner<PlumeSignatureV1> for SecretKey {}
impl signature::RandomizedSigner<PlumeSignatureV2> for SecretKey {}
```
---------

Co-authored-by: skaunov <skaunov@disroot.org>
2024-02-28 12:17:58 +03:00
Sergey Kaunov
e5febe3cf3 Resolve half of #67 (Part 1/2) (#93)
* current progress

* link to relevant issue

* crate name edit

* Flat the docs entities

* meta information

* Update the crate name in `tests`

* current progress
2024-02-15 15:18:30 -05:00
Yush G
71cea99030 added snaps support info 2024-02-14 15:09:35 -05:00
Yush G
c2fe28c9e4 Update README.md with rabby url 2024-02-14 15:04:20 -05:00
Yush G
f8d656037e typos 2024-02-10 20:43:52 -05:00
Sergey Kaunov
fa25172f04 Solve #69 (Tests improvements) (#87)
here I came to <https://github.com/plume-sig/zk-nullifier-sig/issues/84>
* Refactor tests

And reread the thing estimating clarity.

* `fmt` ready
* Tests improvements

Divides tests into unit and integrational.
Clean and structure info it provided.

Disjoints `mod helpers` from the `release`; along the road where pastes and adapts "one-liners" where it relied on the actual code under the testing (preserves used function calls indication on the best efforts as comments, they're good to be removed on a next iteration).

should add that it's also needed to
* convert `tests` to integration,
* clean hex strings assertions for further comprehensibilty. \
(This well might demote `AsRef` issue to a _nice to have_ thing.)
2024-02-06 03:25:57 -05:00
Sergey Kaunov
602ec7bdab Solve #60 (#91)
* Sane error handling and some simplification (ditch unwrap)
* excessive code and entities were removed
2024-02-06 03:24:10 -05:00
Divide-By-0
5bd0bf01d2 fix #15 2024-02-05 23:54:15 -05:00
Yush G
42789dc945 Merge pull request #78 from plume-sig/13
Solve #13
2023-12-09 13:01:25 -05:00
Sergey Kaunov
3d9180983c Fix issue with crate crate and mistakes of merge (#81) 2023-12-09 19:24:39 +03:00
Rajesh
b018d850bd build: bump k256 to 0.13.2 (#76)
* Fix: array signals

* Revert "Fix: array signals"

This reverts commit 253cc8c238.

* bump k256 to 0.13.2

* bump k256

* ran cargo fmt and clippy

---------

Co-authored-by: Sergey Kaunov <skaunov@disroot.org>
2023-12-09 19:13:17 +03:00
Yush G
db9b8354e4 Merge pull request #79 from plume-sig/63
Solve #63
2023-12-09 03:18:16 -05:00
skaunov
44e48202a3 fmt 2023-12-02 17:48:21 +03:00
skaunov
9167fabbe4 Some chore 2023-12-02 17:46:41 +03:00
skaunov
4396fb4b44 fmt and lint 2023-12-02 17:32:17 +03:00
skaunov
50516445e0 Solve #63, reduce unwrap,
streamline error handling, and ditch panicing.
2023-12-02 17:29:22 +03:00
skaunov
93e8844881 prettier lint 2023-12-02 02:38:22 +03:00
skaunov
94fd5b5e97 Solve #13
I had an idea to just hardcode the binary array, but it seems that
to actually constrait the circuit to it it's beneficial to use the
`component`.
```
bit_length_binary <== [0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
```
2023-12-02 02:25:40 +03:00