Skip to content
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

unifying the crypto keypair APIs #21002

Closed
wants to merge 10 commits into from

Conversation

jakubDoka
Copy link
Contributor

Related to #20953

I changed all keypair APIs I could find to init for the pure derivation from seed, and added initWithRandomSeed that's mostly used in tests/benchmarks.

Note: I noticed docs on the new functions are missing, though, I feel like they are unnecessary.

@jakubDoka jakubDoka changed the title unifiing the crypto keypair APIs unifying the crypto keypair APIs Aug 8, 2024
Copy link
Member

@andrewrk andrewrk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Let's take this opportunity to also fix something else that I noticed.

@@ -506,7 +507,7 @@ test "signature" {
test "batch verification" {
var i: usize = 0;
while (i < 100) : (i += 1) {
const key_pair = try Ed25519.KeyPair.create(null);
const key_pair = try Ed25519.KeyPair.initWithRandomSeed();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unit tests should not use std.crypto.random, instead they should use std.testing.random_seed.

this is a problem with master branch, but it should be fixed in this PR before merging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed https://github.com/ziglang/zig/blob/master/lib/std/crypto/salsa20.zig#L539 will need to be changed to take a seed, the initWithRandomSeed should probably be removed to reduce the likelihood of similar mistakes.

@jakubDoka
Copy link
Contributor Author

I have repeated the initTestKeypair to keep it private and also moved the seed_length into the KeyPair scope.

@jakubDoka jakubDoka requested a review from andrewrk August 8, 2024 23:22
@jedisct1
Copy link
Contributor

jedisct1 commented Aug 8, 2024

I strongly disagree with these changes.

Virtually all applications should always use non-deterministic keys. We should absolutely not encourage people to provide their own seed, possibly from insecure random number generators or constant seeds. This is a recipe for disaster.

Aa create() function that doesn't take any seed for the common case, and createDeterministic() for deterministic keys would be way better.

@@ -19,8 +19,6 @@ pub const X25519 = struct {
pub const public_length = 32;
/// Length (in bytes) of the output of the DH function.
pub const shared_length = 32;
/// Seed (for key pair creation) length in bytes.
pub const seed_length = 32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That shouldn't be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's was moved to the keypair to unify things

@@ -229,8 +229,6 @@ fn Kyber(comptime p: Params) type {
pub const shared_length = common_shared_key_size;
/// Length (in bytes) of a seed for deterministic encapsulation.
pub const encaps_seed_length = common_encaps_seed_length;
/// Length (in bytes) of a seed for key generation.
pub const seed_length: usize = inner_seed_length + shared_length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove these useful constants?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@andrewrk
Copy link
Member

andrewrk commented Aug 9, 2024

Virtually all applications should always use non-deterministic keys.

Testing is an extremely common use case.

We should absolutely not encourage people to provide their own seed, possibly from insecure random number generators or constant seeds. This is a recipe for disaster.

You really think people are going to reach for Ed25519 encryption, and then pass the system time or something like that? ...you know what now that I think of it, yes, people are absolutely that stupid. Point taken.

Aa create() function that doesn't take any seed for the common case, and createDeterministic() for deterministic keys would be way better.

create typically means "heap allocated" or allocation of another resource which is not the case here. As a rule of thumb, if there is no "destroy" then there should be no "create".

However, let us accomplish your goal while keeping existing conventions, with init and initDeterministic. Naming aside, the key changes here are good, making those two different functions rather than one with an optional.

@jakubDoka
Copy link
Contributor Author

Besides humans, AI now also writes code so this is even more important 😂. I'll get to this tomorrow

@jedisct1
Copy link
Contributor

jedisct1 commented Aug 9, 2024

create typically means "heap allocated" or allocation of another resource which is not the case here. As a rule of thumb, if there is no "destroy" then there should be no "create".

Not really. Maybe in other languages, but in Zig, init() is typically what gets an allocator, with a corresponding deinit() function.

init() also conveys the idea that the result with be initialized with default values, not with something different every time.

There's nothing wrong with create(). This actually creates a new key every time.

@jakubDoka jakubDoka requested a review from jedisct1 August 9, 2024 12:00
pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
const msg = [_]u8{0} ** 64;
const key_pair = try Signature.KeyPair.create(null);
const key_pair = try initBenchKeyPair(Signature);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmark can simply use Signature.KeyPair.create().

test "batch verification" {
var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
Copy link

@alterstep alterstep Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People often refer to tests to understand how to use things.

So perhaps use DefaultCsprng instead, and rename prng to test_prng to clarify that this code is intended solely for testing.

@andrewrk
Copy link
Member

andrewrk commented Aug 9, 2024

Not really. Maybe in other languages, but in Zig, init() is typically what gets an allocator, with a corresponding deinit() function.

Frank, please do a couple greps and see that this is completely wrong.

@jakubDoka
Copy link
Contributor Author

And once again I am renaming things🍷

@andrewrk
Copy link
Member

andrewrk commented Aug 9, 2024

@jakubDoka I recommend for you to sit back and do nothing until we work this out.

@jakubDoka
Copy link
Contributor Author

jakubDoka commented Aug 9, 2024

So, does the create/destroy pattern signify that the object holds an allocation for the full lifetime, whereas init/deinit hints that the initialization never allocates and/or allocation is not required for the object to exist?

@andrewrk
Copy link
Member

andrewrk commented Aug 9, 2024

The create convention generally indicates that the object itself is heap allocated and the return value will be a pointer.

init does not hint anything other than that the return type will be of the parent namespace type and not be a pointer.

@jedisct1
Copy link
Contributor

jedisct1 commented Aug 9, 2024

Would generate() work? This is quite a common name for creating keys.

@andrewrk
Copy link
Member

andrewrk commented Aug 9, 2024

"generate" is fine. Mainly, I care about separate functions rather than optionals (the pattern in #20953).

I understand the desire to help people write better code (really, I do), but the bottom line is that entropy is an externally-provided dependency, and it needs to be recognized as one in the API. That's just a fact of reality in programming.

I would like to avoid the convention-breaking create name if possible. However if you want to have your own convention in std.crypto, I think that's ... acceptable.

@jedisct1
Copy link
Contributor

jedisct1 commented Aug 9, 2024

Let's opt for generate and generateDeterministic, then.

@jakubDoka
Copy link
Contributor Author

Is this all?

jedisct1 added a commit to jedisct1/zig that referenced this pull request Nov 10, 2024
Our key pair creation API was ugly and inconsistent between ecdsa
keys and other keys.

The same `generate()` function can now be used to generate key pairs,
and that function cannot fail.

For deterministic keys, a `generateDeterministic()` function is
available for all key types.

Fix comments and compilation of the benchmark by the way.

Fixes ziglang#21002
@jedisct1 jedisct1 closed this in 8a00bd4 Nov 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants