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

Change short-circuiting logical ORs to bitwise ORs in Ristretto decoding. #243

Conversation

isislovecruft
Copy link
Member

@isislovecruft isislovecruft commented Apr 22, 2019

This prevents a timing side channel which would allow us to determine which check failed. I can't think of a case where this would create problems in practice, but nonetheless I'd argue we should prevent the leak.

Related to dalek-cryptography/subtle#42

@hdevalence
Copy link
Contributor

I'm not sure if this is a good change, because the decoding function doesn't operate on secret data, and can't be constant-time anyways because it returns an Option: even if we were to consider unvalidated point data as secret, we would need to prevent data flows from that point data into a branch, but it's impossible to use an Option without branching.

@@ -296,7 +296,7 @@ impl CompressedRistretto {
// t == ((1+as²) sqrt(4s²/(ad(1+as²)² - (1-as²)²)))/(1-as²)
let t = &x * &y;

if ok.unwrap_u8() == 0u8 || t.is_negative().unwrap_u8() == 1u8 || y.is_zero().unwrap_u8() == 1u8 {
if (ok.unwrap_u8() == 0u8) | (t.is_negative().unwrap_u8() == 1u8) | (y.is_zero().unwrap_u8() == 1u8) {
Copy link
Contributor

@str4d str4d Oct 29, 2019

Choose a reason for hiding this comment

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

Given that Choice implements BitXor and Not, I think this could be written more simply as:

Suggested change
if (ok.unwrap_u8() == 0u8) | (t.is_negative().unwrap_u8() == 1u8) | (y.is_zero().unwrap_u8() == 1u8) {
if (!ok | t.is_negative() | y.is_zero()).unwrap_u8() == 1u8 {

@@ -266,7 +266,7 @@ impl CompressedRistretto {
&s_bytes_check[..].ct_eq(self.as_bytes());
let s_is_negative = s.is_negative();

if s_encoding_is_canonical.unwrap_u8() == 0u8 || s_is_negative.unwrap_u8() == 1u8 {
if (s_encoding_is_canonical.unwrap_u8() == 0u8) | (s_is_negative.unwrap_u8() == 1u8) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly:

Suggested change
if (s_encoding_is_canonical.unwrap_u8() == 0u8) | (s_is_negative.unwrap_u8() == 1u8) {
if (!s_encoding_is_canonical | s_is_negative).unwrap_u8() == 1u8 {

@str4d
Copy link
Contributor

str4d commented Oct 29, 2019

If timing is a concern here, the API should probably also be altered to return a CtOption<RistrettoPoint>:

CtOption::new(s, s_encoding_is_canonical & !s_is_negative).and_then(|s| {
    [...]

    CtOption::new(
        RistrettoPoint(EdwardsPoint{X: x, Y: y, Z: one, T: t}),
        ok & !t.is_negative() & !y.is_zero(),
    )
)

@hdevalence
Copy link
Contributor

Timing isn't a concern here, because point decompression does not operate on secret data.

@hdevalence hdevalence closed this Oct 29, 2019
pinkforest pushed a commit to pinkforest/curve25519-dalek that referenced this pull request Jun 27, 2023
…key-docs-coverage

Fix `SigningKey` from/to_bytes docs +coverage
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.

3 participants