Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Rename constant_time_eq to constant_time_cmp to better indicate that these functions return 0 when values are equal, like memcmp, strcmp, etc., not truth, to avoid possible mistakes. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | dmitry-security |
| Files: | files | file ages | folders |
| SHA1: |
d244c484e73e5eb18eef0cc1f8e5fb04 |
| User & Date: | dmitry 2011-10-04 14:34:12.781 |
Context
|
2011-10-04
| ||
| 14:38 | Merge trunk into dmitry-security branch. ... (Closed-Leaf check-in: f4eb0f5afc user: dmitry tags: dmitry-security) | |
| 14:34 | Rename constant_time_eq to constant_time_cmp to better indicate that these functions return 0 when values are equal, like memcmp, strcmp, etc., not truth, to avoid possible mistakes. ... (check-in: d244c484e7 user: dmitry tags: dmitry-security) | |
| 14:28 | Revert the previous change after thinking more about it. Login cards in the sync protocol have the following format: login userid nonce signature Nonce is SHA-1 of the message that follows this line, signature is SHA-1 of the concatenation of the nonce and user's shared secret. The successful timing attack can reveal only signature for this particular packet due to nonce. However, as nonce is known to the attacker, it's theoretically possible for them to bruteforce the shared secret_offline_. The whole scenario sounds highly improbable, but using constant-time comparison function for such things by default is a good practice. ... (check-in: 13a9a1244c user: dmitry tags: dmitry-security) | |
Changes
Changes to src/blob.c.
| ︙ | ︙ | |||
317 318 319 320 321 322 323 | } /* ** Compare two blobs in constant time and return zero if they are equal. ** Constant time comparison only applies for blobs of the same length. ** If lengths are different, immediately returns 1. */ | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
}
/*
** Compare two blobs in constant time and return zero if they are equal.
** Constant time comparison only applies for blobs of the same length.
** If lengths are different, immediately returns 1.
*/
int blob_constant_time_cmp(Blob *pA, Blob *pB){
int szA, szB, i;
unsigned char *buf1, *buf2;
unsigned char rc = 0;
blob_is_init(pA);
blob_is_init(pB);
szA = blob_size(pA);
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
229 230 231 232 233 234 235 |
redirect_to_g();
}
if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
/* The user requests a password change */
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
if( db_int(1, "SELECT 0 FROM user"
" WHERE uid=%d"
| | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
redirect_to_g();
}
if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
/* The user requests a password change */
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
if( db_int(1, "SELECT 0 FROM user"
" WHERE uid=%d"
" AND (constant_time_cmp(pw,%Q)=0"
" OR constant_time_cmp(pw,%Q)=0)",
g.userUid, zSha1Pw, zPasswd) ){
sleep(1);
zErrMsg =
@ <p><span class="loginError">
@ You entered an incorrect old password while attempting to change
@ your password. Your password is unchanged.
@ </span></p>
|
| ︙ | ︙ | |||
308 309 310 311 312 313 314 |
*/
zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q"
" AND length(cap)>0 AND length(pw)>0"
" AND login NOT IN ('anonymous','nobody','developer','reader')"
| | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
*/
zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q"
" AND length(cap)>0 AND length(pw)>0"
" AND login NOT IN ('anonymous','nobody','developer','reader')"
" AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
zUsername, zSha1Pw, zPasswd
);
if( uid<=0 ){
sleep(1);
zErrMsg =
@ <p><span class="loginError">
@ You entered an unknown user or an incorrect password.
|
| ︙ | ︙ | |||
456 457 458 459 460 461 462 | style_footer(); } /* ** SQL function for constant time comparison of two values. ** Sets result to 0 if two values are equal. */ | | | 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
style_footer();
}
/*
** SQL function for constant time comparison of two values.
** Sets result to 0 if two values are equal.
*/
static void constant_time_cmp_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const unsigned char *buf1, *buf2;
int len, i;
unsigned char rc = 0;
|
| ︙ | ︙ | |||
508 509 510 511 512 513 514 |
zCode
);
if( zOtherRepo==0 ) return 0; /* No such peer repository */
rc = sqlite3_open(zOtherRepo, &pOther);
if( rc==SQLITE_OK ){
sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0);
| | | | | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 |
zCode
);
if( zOtherRepo==0 ) return 0; /* No such peer repository */
rc = sqlite3_open(zOtherRepo, &pOther);
if( rc==SQLITE_OK ){
sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0);
sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0,
constant_time_cmp_function, 0, 0);
sqlite3_busy_timeout(pOther, 5000);
zSQL = mprintf(
"SELECT cexpire FROM user"
" WHERE login=%Q"
" AND ipaddr=%Q"
" AND length(cap)>0"
" AND length(pw)>0"
" AND cexpire>julianday('now')"
" AND constant_time_cmp(cookie,%Q)=0",
zLogin, zRemoteAddr, zHash
);
pStmt = 0;
rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
db_multi_exec(
"UPDATE user SET cookie=%Q, ipaddr=%Q, cexpire=%.17g"
|
| ︙ | ︙ | |||
560 561 562 563 564 565 566 |
uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q"
" AND ipaddr=%Q"
" AND cexpire>julianday('now')"
" AND length(cap)>0"
" AND length(pw)>0"
| | | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
uid = db_int(0,
"SELECT uid FROM user"
" WHERE login=%Q"
" AND ipaddr=%Q"
" AND cexpire>julianday('now')"
" AND length(cap)>0"
" AND length(pw)>0"
" AND constant_time_cmp(cookie,%Q)=0",
zLogin, zRemoteAddr, zCookie
);
return uid;
}
/*
** This routine examines the login cookie to see if it exists and
|
| ︙ | ︙ | |||
584 585 586 587 588 589 590 | const char *zIpAddr; /* Raw IP address of the requestor */ char *zRemoteAddr; /* Abbreviated IP address of the requestor */ const char *zCap = 0; /* Capability string */ /* Only run this check once. */ if( g.userUid!=0 ) return; | | | | 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | const char *zIpAddr; /* Raw IP address of the requestor */ char *zRemoteAddr; /* Abbreviated IP address of the requestor */ const char *zCap = 0; /* Capability string */ /* Only run this check once. */ if( g.userUid!=0 ) return; sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, constant_time_cmp_function, 0, 0); /* If the HTTP connection is coming over 127.0.0.1 and if ** local login is disabled and if we are using HTTP and not HTTPS, ** then there is no need to check user credentials. ** ** This feature allows the "fossil ui" command to give the user ** full access rights without having to log in. |
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
db_ephemeral_blob(&q, 0, &pw);
szPw = blob_size(&pw);
blob_zero(&combined);
blob_copy(&combined, pNonce);
blob_append(&combined, blob_buffer(&pw), szPw);
sha1sum_blob(&combined, &hash);
assert( blob_size(&hash)==40 );
| | | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
db_ephemeral_blob(&q, 0, &pw);
szPw = blob_size(&pw);
blob_zero(&combined);
blob_copy(&combined, pNonce);
blob_append(&combined, blob_buffer(&pw), szPw);
sha1sum_blob(&combined, &hash);
assert( blob_size(&hash)==40 );
rc = blob_constant_time_cmp(&hash, pSig);
blob_reset(&hash);
blob_reset(&combined);
if( rc!=0 && szPw!=40 ){
/* If this server stores cleartext passwords and the password did not
** match, then perhaps the client is sending SHA1 passwords. Try
** again with the SHA1 password.
*/
const char *zPw = db_column_text(&q, 0);
char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
blob_zero(&combined);
blob_copy(&combined, pNonce);
blob_append(&combined, zSecret, -1);
free(zSecret);
sha1sum_blob(&combined, &hash);
rc = blob_constant_time_cmp(&hash, pSig);
blob_reset(&hash);
blob_reset(&combined);
}
if( rc==0 ){
const char *zCap;
zCap = db_column_text(&q, 1);
login_set_capabilities(zCap, 0);
|
| ︙ | ︙ |