-
Notifications
You must be signed in to change notification settings - Fork 8
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
Copy Libsodium's randombytes API and use for prng seeding #27
Copy Libsodium's randombytes API and use for prng seeding #27
Conversation
Codecov Report
@@ Coverage Diff @@
## master #27 +/- ##
==========================================
- Coverage 76.96% 76.65% -0.32%
==========================================
Files 21 22 +1
Lines 990 1041 +51
Branches 59 69 +10
==========================================
+ Hits 762 798 +36
- Misses 208 215 +7
- Partials 20 28 +8
Continue to review full report at Codecov.
|
I'm conflicted on the libsodium import. It pulls in a bunch of stuff we don't use (various functions and macros in Some other options to consider (I'm not writing off the current approach - just want to discuss these):
|
include/ecdaa/prng.h
Outdated
* | ||
* No dynamic memory allocation is performed. | ||
*/ | ||
void ecdaa_prng_construct(struct ecdaa_prng *prng_in); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call this ecdaa_prng_init
. I believe the _init
prefix (and _free
suffix if any cleanup is needed) are de facto standards for C (de)constructor naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/amcl-extensions/big_256_56.h
Outdated
@@ -24,9 +24,10 @@ | |||
extern "C" { | |||
#endif | |||
|
|||
struct ecdaa_prng; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really part of this review, but I'm just noticing that many of the headers redeclare the structs as opaque, rather than importing the defining header? What's the reason for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That started with a circular dependency I had in an early version of this project, and I kept doing it just as a nod to compilation time reduction. But, this is a tiny project, so that's really not an issue. I can change it easily if you think it confuses things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, my reason for importing headers was going to be that "it helps ensure circular dependencies don't creep in".
I think eventually standard imports will be better, but that's an easy change to make down the line.
Yea, I definitely agree. I think this stems from a misunderstanding I had of a comment you made last week sometime. My preference is actually something akin to option 2, but I misunderstood and thought you recommended pulling in Libsodium's code. Here's my proposal: Keep the But, the user can call something like |
Gotcha. I made that comment thinking we'd only have to copy a function or two, not realizing the extent of the Your proposal sounds good. Maybe call the option |
Yea, I thought it would be simpler, too. When I realized it was more intricate, I started following your option 3 above, then got a little frightened I was pulling a bull-in-a-chinashop without understanding how it all works. Hence the wholesale copying. I just pushed a few commits implementing this new version. It's not building on travis at the moment, because I have to install libsodium there. But, let me know if this looks more like what you're thinking. |
Yes, looks good to me. The global for the seed function bothers me a bit, but I think is better (easier to use and more likely to be used correctly) than requiring |
I was just thinking about which of those I prefer. I hate global stuff like this, and taking it as an argument would also allow for per-signature setting, if that were ever required. We could have two functions for That way, most people would just use the default one. But, if you want, you have to go out of your way to use a non-default one. |
Good point. I still suggest keeping your current version. It's got the simplest API (1. Set the seed function at program start. 2. init each prng when created) The |
6cd72bb
to
3fae763
Compare
👍 |
33793d3
to
eb4a344
Compare
src/prng.c
Outdated
fputs("Unable to initialize libsodium random seed source. Aborting", stderr); | ||
abort(); | ||
} | ||
#if defined(__linux__) && defined(RNDGETENTCNT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this section belongs in a low-level crypto library - for the same reasons that it's not included in libsodium
by default.
How the program should respond to low entropy is application-specific. For many services, the right answer might be to keep running, but refuse connections until sufficient entropy is available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be better to let the ecdaa_init()
have the same semantics as the sodium_init()
return code. Then copy the that code snippet from the libsodium
readme to the ecdaa
readme.
src/prng.c
Outdated
void ecdaa_prng_init(struct ecdaa_prng *prng_in) | ||
{ | ||
#ifdef USE_LIBSODIUM_RNG_SEED_FUNCTION | ||
sodium_init_if_needed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be put in ecdaa_init()
instead of here? Then we don't need to track the libsodium init state. If the user isn't using libsodium, ecdaa_init()
will just be a no-op for now.
eb4a344
to
974586b
Compare
848bbc2
to
3f036c8
Compare
After discussion offline, this PR has been updated to use the following design:
|
include/ecdaa/prng.h
Outdated
* 0 on success | ||
* return value of `custom_func`, if non-zero | ||
*/ | ||
int ecdaa_prng_init_custom(struct ecdaa_prng *prng_in, ecdaa_prng_custom_seed_function_type custom_func); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just take the seed directly, rather than a seed function? My gut feeling is that will be the easiest API for users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to make sure we actually get AMCL_SEED_SIZE
bytes of seed. I can imagine someone thinking "well, bytes is an appropriate seed size, so that's what I'll pass in" without realizing we're requesting a specific size, then we try to read more bytes than they gave us.
We can document the requirement by taking the seed as char seed[AMCL_SEED_SIZE]
, but we have no way of determining if that's what is actually passed in.
Maybe I'm being anal, and this is a caveat emptor situation. And, yea, I can see that having to pass in a function with a specific signature can be a pain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, after thinking about it more, I think you're right. I think I'm trying to hard to tell the user what to do, and making the API hard to use.
I agree now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we take both the pointer and size as arguments, and return an error if the size is less than AMCL_SEED_SIZE
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/prng.c
Outdated
ret = -1; | ||
break; | ||
} | ||
#if defined(__linux__) && defined(RNDGETENTCNT) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this block to a separate function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should also go before the call to sodium_init
. Otherwise the entropy might have been low when sodium_init
was run, but high when the check is being made.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/prng.c
Outdated
#include <linux/random.h> | ||
#endif // defined(__linux__) | ||
|
||
int ecdaa_prng_init(struct ecdaa_prng *prng_in) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation should call ecdaa_prng_init_custom
internally, to avoid code duplication. Generate the seed and call ecdaa_prng_init_custom
with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this hinges on the signature of ecdaa_prng_init_custom
, as per above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if we keep the function arg for ecdaa_prng_init_custom
, ecdaa_prng_init
should call through to ecdaa_prng_init_custom
. After all, we're just providing a default libsodium-based variant. If using ecdaa_prng_init_custom
for that is too difficult for us, then what hope does an end user have? It would imply that the ecdaa_prng_init_custom
API needs more work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Implementation has been updated now.
6705bcc
to
391124b
Compare
include/ecdaa/prng.h
Outdated
#define AMCL_SEED_SIZE 128 // As recommended in AMCL's "rand.c" | ||
|
||
enum ecdaa_prng_initialized { | ||
ECDAA_PRNG_INITIALIZED_YES, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicitly define the numeric values and make sure the ECDAA_PRNG_INITIALIZED_NO
is 0. As it is right now, I think that on systems the zero new memory allocations, a new ecdaa_prng.initialized
will default to 0, which is ECDAA_PRNG_INITIALIZED_YES
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
src/amcl-extensions/big_256_56.c
Outdated
{ | ||
if (ECDAA_PRNG_INITIALIZED_YES != prng->initialized) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can all of these checks be moved into one function - an accessor for ecdaa_prng->impl
? Then we don't have to remember to check the initialized state in so many places; just the once in the accessor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And actually, in this particular case, because it is a amcl-extension
it should take the csprng
directly, not a ecdaa_prng
. Ideally acml-extensions
shouldn't depend on ecdaa
code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea.
👍
src/amcl-extensions/big_256_56.c
Outdated
BIG_256_56 curve_order; | ||
BIG_256_56_rcopy(curve_order, CURVE_Order_BN254); | ||
|
||
BIG_256_56_randomnum(*big_out, curve_order, rng); | ||
BIG_256_56_randomnum(*big_out, curve_order, &prng->impl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Continuing the prior comment, replace this with
BIG_256_56_randomnum(*big_out, curve_order, ecdaa_prng_get_csprng(prng));
where ecdaa_prng_get_csprng(...)
returns csprng*
or calls abort()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made all the functions take ecdaa_prng
rather than letting the amcl-extensions
take the bare one, to make it easier to see where the bare one is getting used.
If I move to the accessor discussed above, that would mean a grep of the source code would immediately show us where the bare csprng is being used directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just grep on everything except acml-extensions
(or throw a | grep -v acml-extensions
on the end of your shell command to filter out those matches).
It depends on what is meant by acml-extensions
. In my mind, those are things that would ideally be provided by the relevant modules in acml
(and might sometime be contributed back). So to the main ecdaa
code, functions from acml
or acml-extensions
should be indistinguishable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I hadn't been thinking of amcl-extensions
that way. I'm sure I understand the advantage of thinking of it that way. Is that what others would expect from such a sub-directory?
There's only one function in amcl-extensions
that uses the csprng
, and thus really only one place in our code where we actually use an rng: big_256_56_random_mod_order
. The implementation of that function may change (cf. #1 ), so maybe that should get lifted out of amcl-extensions
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm a little confused as the to the point of big256_56_random_mod_order()
in the first place. Why not just call BIG_256_56_randomnum(*big_out, CURVE_Order_BN254, rng)
directly? I don't see the purpose of the BIG_256_56_rcopy
call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As always, after thinking about it, I think I see your point. It's nice to not have the cognitive friction of having two conventions when using AMCL-related code in our project. Besides, if we end up wanting to submit any of our extensions to the AMCL project for inclusion, this makes it easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, weird, I didn't see your question about rcopy
when I left my previous comment.
That's copying from AMCL's examples. Concretely, it has to do with const
vs non-const
. The r
seems to indicate ROM
in their naming scheme. It does look like simply casting away const might work, but the fact that they explicitly do the copy in their examples makes me wonder if they're trying to be careful there (cf. my worries in #4 ).
So, at the moment, it's simply a wrapper to do the copy from "ROM" then call _randomnum
, but I don't feel confident removing it.
do { | ||
ret = check_entropy(); | ||
if (0 != ret) | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd personally use goto
s rather than do/while/break
to avoid the extra level of indentation, but that's a personal preference.
ret = check_entropy();
if (0 != ret)
goto out;
...
prng_in->initialized = ECDAA_PRNG_INITIALIZED_YES;
out:
return ret;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The do-while
construct appears in other crypto software I've seen, that's why I adopted it. I kind of like the extra indentation, because it clearly shows what's wrapped (like a try-catch block).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
include/ecdaa/prng.h
Outdated
* | ||
* Does not do any heap deallocation. | ||
*/ | ||
void ecdaa_prng_clean(struct ecdaa_prng *prng); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to ecdaa_prng_free()
to match C convention?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After addressing this, I say merge it. The public API seems to be good.
The nature of the inner usage can evolve (what the acml-extensions
conventions should be will become more clear as support for other curves are added. Whether get_csrng
should be prng_get_csrng
or ecdaa_prng_get_csrng
will become more clear when naming conventions for internal code are decided. For now there are bigger fish to fry.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I avoided free
here to keep from insinuating memory is being freed. But, maybe making people think that would be good (more people have been burned by memory leaks than by side-channel attacks, at least that they know of).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, bigger fish indeed.
I spoke with Michael Scott from AMCL a fair amount today, getting advice on using their csprng. Definitely feel more confident in their implementation and our usage of it now. So, I'm go for this PR, other than your input on free
vs clean
.
cbfe1ff
to
144afb9
Compare
Awesome, thanks so much for all the help and sorry for all the back and forth! |
Copy (with appropriate attribution) Libsodium's
randombytes_buf
functionality, for getting crypto-strength random bytes for the purposes of seeding AMCL's pseudo random number generator.This copy takes everything in-tact from Libsodium, except the
nativeclient
orsalsa
randombytes options. The default randombytes backend, thesysrandom
, is retained. As is the ability for a user to define a custom backend (e.g. for a hardware device).closes #9
Also, this PR introduces a wrapper around AMCL's prng, so the user won't be expected to seed the rng themselves (instead, we make it very clear that they need to use a "constructor" for our prng wrapper, which uses the Libsodium randombytes functionality to seed the prng).
closes #11
A CMake option for Libsodium's
USE_BLOCKING_RANDOM
is also included, but not documented in the README.