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

ed25519-dalek: make ExpandedSecretKey fields private #544

Closed

Conversation

tarcieri
Copy link
Contributor

@tarcieri tarcieri commented Jul 2, 2023

Motivation can be found in this comment:

dalek-cryptography/ed25519-dalek#293 (review)

Replaces public fields with private fields + accessor methods.

Also adds an ExpandedSecretKey::from_scalar_and_prefix constructor which makes it possible to construct ExpandedSecretKey using a Scalar again, which is useful for protocols that derive the scalar such as Ed25519-BIP32.

Additionally defines a HashPrefix type alias to make it semantically clear which [u8; 32] is involved in type signatures.

@tarcieri tarcieri requested a review from rozbb July 2, 2023 19:56
@@ -106,6 +120,18 @@ impl ExpandedSecretKey {
.into()
})
}

/// Construct an `ExpandedSecretKey` from a scalar and hash prefix.
pub fn from_scalar_and_prefix(scalar: Scalar, hash_prefix: HashPrefix) -> Self {
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 thought from_scalar_and_hash_prefix was a bit unwieldy but it would be more consistent.

This method could probably use a longer comment explaining its use cases (e.g. Ed25519-BIP32).

Copy link
Contributor

Choose a reason for hiding this comment

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

I think naming is fine.

More docs here. I do feel weird about linking to niche cryptocurrency specs from our repo though, especially in the hazmat section. I included the text anyway. I vote we cut the whole second paragraph, but I'll defer to you.

Construct an ExpandedSecretKey from a scalar and hash prefix. In the spec, these are derived a SHA-512 hash. Specifically, the scalar is derived by clamping and reducing the first 32 bytes of the hash. And the prefix is set equal to the second 32 bytes.

This constructor might be useful when the given scalar is derived by some arithmetic means, such as in the BIP-32 hierarchical key derivation scheme.

Comment on lines +131 to 139
impl From<(Scalar, HashPrefix)> for ExpandedSecretKey {
fn from(parts: (Scalar, HashPrefix)) -> ExpandedSecretKey {
ExpandedSecretKey::from_scalar_and_prefix(parts.0, parts.1)
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This feels a bit debatable

Copy link
Contributor

Choose a reason for hiding this comment

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

My opinion is that there shouldn't be a constructor with unnamed types unless the struct is overtly made from those parts. Since everything is pub(crate) now (as, I agree, it should be), this makes less sense imo.

Motivation can be found in this comment:

dalek-cryptography/ed25519-dalek#293 (review)

Replaces public fields with private fields + accessor methods.

Also adds an `ExpandedSecretKey::from_scalar_and_prefix` constructor
which makes it possible to construct `ExpandedSecretKey` using a
`Scalar` again, which is useful for protocols that derive the scalar
such as Ed25519-BIP32.

Additionally defines a `HashPrefix` type alias to make it semantically
clear which `[u8; 32]` is involved in type signatures.
@tarcieri tarcieri force-pushed the ed25519-dalek/make-expanded-secret-key-fields-private branch from 5138ef0 to 58f2855 Compare July 2, 2023 19:59
@@ -40,14 +43,15 @@ pub struct ExpandedSecretKey {
// modulo l.
pub(crate) scalar_bytes: [u8; 32],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sidebar: perhaps this should be canonical_scalar_bytes?

Copy link
Contributor

@rozbb rozbb Jul 6, 2023

Choose a reason for hiding this comment

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

Actually, now that you bring this up again, I'm not sure if this was even the right choice. The original reason I included scalar_bytes was because section 5.1.5 didn't say you do a modular reduction of the clamped integer when computing the pubkey. So I kept this by the letter of the law.

But this detail doesn't matter! Yes it is true that sometimes s.reduce() * H != s * H, but this is only for H that are not in the prime subgroup. The only place scalar_bytes is used (outside of defining scalar) is in the pubkey generation step EdwardsPoint::mul_base_clamped(scalar_bytes). Since the generator is in the prime order subgroup, I should be able to just do scalar * ED25519_BASEPOINT.

Does this make sense? Basically I think we should remove scalar_bytes. Lmk and I'll push to this branch

Copy link
Contributor Author

@tarcieri tarcieri Jul 6, 2023

Choose a reason for hiding this comment

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

#545 already does it as an alternative to this PR

@tarcieri
Copy link
Contributor Author

tarcieri commented Jul 2, 2023

Alternatively (and alternatively to #541) we could get rid of the to_bytes method, which seems to be the reason for retaining it in the first place.

I'm not sure what the motivation is for having it at all. Serializing this format on the wire is an antipattern, IMO.

tarcieri added a commit that referenced this pull request Jul 2, 2023
The reason `ExpandedSecretKey` needs a private `scalar_bytes` field is
to retain the canonical scalar bytes as output by SHA-512 during key
expansion so they can be serialized by the `to_bytes` method.

However, `ExpandedSecretKey`s should not be serialized to the wire.

Removing this method allows the private field to be removed, which
allows `ExpandedSecretKey` to be constructed entirely from public
fields. This provides an alternative to #544 for use cases like
Ed25519-BIP32 where the private scalar is derived rather than clamped
from bytes.

One other change is needed: `to_scalar_bytes` was changed to `to_scalar`
as the canonical scalar bytes are no longer retained, however this has
no impact on its main use case, X25519 Diffie-Hellman exchanges, where
the `Scalar` should NOT be written to the wire anyway.
@tarcieri
Copy link
Contributor Author

tarcieri commented Jul 2, 2023

I opened #545 as an alternative to this PR to try just getting rid of the private field, which makes all of the fields pub now and allows for public construction of ExpandedSecretKey again

@pinkforest pinkforest mentioned this pull request Jul 5, 2023
rozbb added a commit that referenced this pull request Jul 11, 2023
* ed25519-dalek: remove `ExpandedSecretKey::to_bytes`

The reason `ExpandedSecretKey` needs a private `scalar_bytes` field is
to retain the canonical scalar bytes as output by SHA-512 during key
expansion so they can be serialized by the `to_bytes` method.

However, `ExpandedSecretKey`s should not be serialized to the wire.

Removing this method allows the private field to be removed, which
allows `ExpandedSecretKey` to be constructed entirely from public
fields. This provides an alternative to #544 for use cases like
Ed25519-BIP32 where the private scalar is derived rather than clamped
from bytes.

One other change is needed: `to_scalar_bytes` was changed to `to_scalar`
as the canonical scalar bytes are no longer retained, however this has
no impact on its main use case, X25519 Diffie-Hellman exchanges, where
the `Scalar` should NOT be written to the wire anyway.

* Added scalar byte comparison back to ed25519-dalek x25519 test

---------

Co-authored-by: Michael Rosenberg <[email protected]>
@rozbb
Copy link
Contributor

rozbb commented Jul 11, 2023

Superseded by #545

@rozbb rozbb closed this Jul 11, 2023
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.

2 participants