-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve documentation with usage examples #283
Comments
What methods do you think would benefit from usage examples? Just a few examples (sorry for the repetition). Or, alternatively, for what usage scenarios it is unclear how to perform them, so that an example is needed? Also if I may criticize your example a little:
|
We've had several requests to improve documentation on this library and I agree it's a general problem. But I'm left feeling like @fjarri that I'm not sure what specific kinds of documentation would be helpful, and I've generally had trouble getting straight answers out of people as to what specific improvements they'd like to see. There are already several toplevel usage examples. Some inline ones would probably make sense, but identifying where would be a first step. |
Thank you so much for this, this is very helpful to me as I learn best practices. Im building out a cryptosystem as an academic project and if you felt inclined I would invite you to give feedback on the rest of the repo :D
I dont know if this is helpful to you both but maybe the perspective I can share is someone who is coming from num-bigint /rug and is unfamiliar with crypto-bigint, specifically for modular multiplication and reduction. Eventually I was able to come up with this: let product_mod_p = montgomery_reduction(&a.mul_wide(&b), &Modulus::MODULUS, Modulus::MOD_NEG_INV); The documentation for this function reads: "Algorithm 14.32 in Handbook of Applied Cryptography https://cacr.uwaterloo.ca/hac/about/chap14.pdf" And thats it, so here are the questions I had to answer on my own, that robust documentation may be able to address:
Ultimately Im looking at a repo like dalek and seeing more text than code almost. I absolutely respect the value of intent revealing code but Im questioning if its safe to assume that someone who wants to take advantage of the benefits of crypto-bigint should be expected to answer the above questions on their own. This is only one example, the rest of the standard operations (+, - etc) were simple enough to figure out. As I work with this library more I can pinpoint additional functions that may present similar questions regarding their usage.
Sorry for the essay, but my answer to this on my own work is usually "anything directly facing the caller should probably have an example of how we expect someone to use our library". Hope this helps and thanks again for taking the time to engage. |
To answer this question:
https://github.com/RustCrypto/crypto-bigint#goals
I'm not sure if that's not clear enough or what. We can explicitly document that every function which doesn't end with |
I only bring it up because some functions do actually explicitly label themselves as constant time in the documentation, such as add_mod for example: "Computes self + rhs mod p in constant time." To a newcomer, this may suggest that a function could be variable time if it is not labelled as such in the documentation. But, I do agree with you that the readme is very clear and should be fine. |
Honestly I wasn't even aware that
I would disagree with that, most methods in
This should be fixed. |
As a newcomer looking for a simple mul mod I dont know how I would ever have found this to be honest, and this is probably due to my own relative inexperience, but it doesnt seem terribly unrealistic to anticipate a 1:1 API with
If you'd like someone to help with this I can open a PR and track down all the places where this is happening. Thanks for the attention to this.
Agreed. Also, if DynResidue/Residue should be the correct way to achieve a modular reduction, showing it in action would be greatly beneficial. The documentation here appears to be substantially more concrete but an example demonstrating how DynResidue/Residue can be used to carry out a modular reduction would be fantastic. |
Open to better names... that one came from quite a bit of bikeshedding.
We can probably re-export the contents of both modules under Perhaps we could remove |
Here is a simple PR that should address the documentation inconsistency. |
Would something like this format be beneficial? /// Computes self / rhs, returns the quotient, remainder.
/// ### Usage:
/// ```
/// use crypto_bigint::{U448, NonZero};
///
/// let a = U448::from(8_u64);
/// let b = NonZero::new(U448::from(4_u64)).unwrap();
/// let res = a.div_rem(&b);
///
/// assert!(res.0 == U448::from(2_u64));
/// assert!(res.1 == U448::ZERO);
/// ```
pub fn div_rem(&self, rhs: &NonZero<Self>) -> (Self, Self) {
// Since `rhs` is nonzero, this should always hold.
let (q, r, _c) = self.ct_div_rem(rhs);
(q, r)
} This example is helpful in this case because it is relatively easy to get started with a Uint, but it is unclear how to get a Nonzero. I needed to search the entire repo for an example in a unit test to find this: let b = NonZero::new(U448::from(4_u64)).unwrap(); If we like this format, I can move through the rest of the repo where this might help, specifically in cases where the operation is happening on a type outside that of |
I would argue that this is one of those methods where a usage example is not necessary. The information on how to create a |
I wont disagree, but its hard for me to get onboard with the idea that following that sequence of steps you just mentioned is easier than simply hovering over the function in an ide and having the correct, safe usage presented to you immediately. Specifically, I struggled here because there is a Im sympathetic to the argument though that if we're sacrificing ease of use to ensure that the user is absolutely sure about what theyre doing by guiding them through the docs in this way youve mentioned and forcing them to find the correct answers to those questions on their own, then maybe that does make more sense over what Im suggesting. I could see it increasing understanding of the library as a whole in some way I guess. |
@drcapybara I think your example could be OK, however it shouldn't use |
heres a preliminary PR extending this pattern to some of the functions in div. could use some review if it makes sense |
I wanted to implement some toy cryptosystem and tried to use this crate because I needed prime-order integer rings.
Even if it's unclear how items should be documented further, it's always helpful to have textbook examples to demonstrate how to use the basic features (maybe a textbook RSA or DH). When a crate's types are not self-explanatory, the first thing I do is jump to the examples folder in the Git repo. |
Heres a blinded RSA protocol I wrote and was hoping to get into the docs here but I dont think I should be using use crypto_bigint::modular::ConstMontyParams;
use crypto_bigint::{const_monty_form, impl_modulus, Uint, U128, U64};
use crypto_bigint::{rand_core::OsRng, NonZero, RandomMod};
use rand::Rng;
pub fn main() {
// RSA public exponent
let e: U128 = Uint::from(65537_u64);
// obtained from crypto_primes::generate_safe_prime(Some(32))
let p: U64 = Uint::from_be_hex("ECAF5ED6493129D7");
let q: U64 = Uint::from_be_hex("98E1FD7AE92F68F3");
// p * q
const N: &str = "8D5910CC89AFF00D40B1ADB4D0230F15";
impl_modulus!(Modulus, U128, N);
// Euler's totient
let phi = (p - U64::ONE).widening_mul(&(q - U64::ONE));
// private decryption and signing key
let secret_d = e.inv_mod(&phi).expect("Modular inverse does not exist");
// super secret message
let message = U128::from(42_u64);
let m = const_monty_form!(message, Modulus);
// sign the message
let s = m.pow(&secret_d);
// verify the signature, without reducing
assert_eq!(m, s.pow(&e), "verification failed");
// public key modulus
let n = &NonZero::new(U128::from_be_hex(N)).unwrap();
// prover commits to c = z^e * m mod n for
// some random non-zero z
let z_rand = U128::random_mod(&mut OsRng, n);
let z = const_monty_form!(z_rand, Modulus);
let c = z.pow(&e).mul(&m);
// Soundness error = 1/2^k
let k = 20;
for _ in 0..k {
// Prover picks a random r_i and keeps it secret
let r_i_rand = U128::random_mod(&mut OsRng, n);
let r_i = const_monty_form!(r_i_rand, Modulus);
// d = r^e mod n is publicly known
let d = r_i.pow(&e);
// Verifier picks a random bit b_i
let b_i = OsRng.gen::<bool>();
// Prover reveals r_i
let z_i = if !b_i { r_i } else { r_i * s * z };
// if b = 0, Prover reveals r_i and verifier checks that
// r_i^e = d
if !b_i {
assert_eq!(d, z_i.pow(&e),);
// b_i = 1. Verifier checks that u^e = d * c mod n
} else {
assert_eq!(d * c, z_i.pow(&e))
}
}
} This shows basic usage of the updated traits. This crate has made a lot of progress towards usability in the last few versions but I share your sentiments as well about usage examples. Hopefully this can help you out a bit @ZettaScript . Maybe someone can rework my example here to be a more correct example, Im a bit short on time these days. |
I understand that this library is a work in progress, and a substantial amount of effort and work has and continues to go into bringing awesome functionality to life. So this isn't at all a criticism, but the documentation for public-facing apis, particularly in the modular math department, feels lacking. This has made it difficult to get started with the library.
Rust has one of the best documentation systems I've had the pleasure of using so far. If the code for some of the functions in the API is considered stable, and would benefit from documentation, Id like to propose that we begin to add usage examples. This will enhance the library by:
I fully acknowledge that this only makes sense if the functions we want to document are stable. If things are still developing and will dramatically change soon, we should we wait for stability. If it makes sense to do all of this, I volunteer as tribute for this task if no one else feels strongly compelled. Here is a sample of some of my work from my own cryptosystem Ive built as an academic exercise in cryptographic algorithm design:
If there's a good reason not to do this right now, we can close this issue and move on. But I didnt see this in the current issues list and feel it would greatly benefit current and new users of the library. Thanks for your consideration, sorry for the long message.
The text was updated successfully, but these errors were encountered: