orlp / ed25519

Portable C implementation of Ed25519, a high-speed high-security public-key signature system.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Possible issues regarding signed to unsigned conversions.

jimktrains opened this issue · comments

Compiling with the -Wsign-conversion option gives the following warnings.

src/ge.c:334:24: warning: implicit conversion changes signedness: 'signed char' to 'unsigned char' [-Wsign-conversion]
    unsigned char ub = b;
                  ~~   ^
src/ge.c:335:24: warning: implicit conversion changes signedness: 'signed char' to 'unsigned char' [-Wsign-conversion]
    unsigned char uc = c;
                  ~~   ^
src/ge.c:344:18: warning: implicit conversion changes signedness: 'signed char' to 'uint64_t' (aka 'unsigned long') [-Wsign-conversion]
    uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
             ~   ^
src/ge.c:363:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][0], equal(babs, 1));
                           ~~~~~ ^~~~
src/ge.c:364:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][1], equal(babs, 2));
                           ~~~~~ ^~~~
src/ge.c:365:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][2], equal(babs, 3));
                           ~~~~~ ^~~~
src/ge.c:366:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][3], equal(babs, 4));
                           ~~~~~ ^~~~
src/ge.c:367:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][4], equal(babs, 5));
                           ~~~~~ ^~~~
src/ge.c:368:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][5], equal(babs, 6));
                           ~~~~~ ^~~~
src/ge.c:369:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][6], equal(babs, 7));
                           ~~~~~ ^~~~
src/ge.c:370:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][7], equal(babs, 8));

I'm also confused as to why there are parameters that are explicitly signed and then immediately assigned to unsigned variables.

                                                                                                               
static unsigned char equal(signed char b, signed char c) {                                                    
    unsigned char ub = b;                                                                                     
    unsigned char uc = c;                                                                                     
    unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */                                                       
    uint64_t y = x; /* 0: yes; 1..255: no */                                                                  
    y -= 1; /* large: yes; 0..254: no */                                                                      
    y >>= 63; /* 1: yes; 0: no */                                                                             
    return (unsigned char) y;                                                                                 
}                                                                                                             
                                                                                                              
static unsigned char negative(signed char b) {                                                                
    uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */                         
    x >>= 63; /* 1: yes; 0: no */                                                                             
    return (unsigned char) x;                                                                                 
}

equal appears to only be used in this file and is called with unsigned values. negative seems to just be testing if b is in the range [-128.-1]. why not return b < 0 or return b >> (sizeof(signed char) - 1)?

Also, the comment 0..255: no is wrong since b will never be in [128,255].

Also, in equal, why use the 64-bit intermediate?

These are all issues (?) directly found in the reference code of Ed25519. I'm not too comfortable changing things around to fix a couple compiler warnings or things that 'look wrong' as it takes an incredible amount of effort and time to verify my changes, considering this is crypto code. If it's not broken I'm (probably) not going to fix it.

BTW the negative is a constant time implementation to avoid branch.