Fossil SCM
Add the hname_verify_hash() routine and use it to fix "fossil test-integrity" such that it verifies the display hash regardless of the algorithm used.
Commit
08badee3a415cf0788adc1d50eeb03a19fac9b5b
Parent
1f61b2dc88ebca7…
2 files changed
+5
-8
+33
+5
-8
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -497,11 +497,11 @@ | ||
| 497 | 497 | ** to be responsible for pBlob. This routine does *not* take over |
| 498 | 498 | ** responsibility for freeing pBlob. |
| 499 | 499 | */ |
| 500 | 500 | int content_put_ex( |
| 501 | 501 | Blob *pBlob, /* Content to add to the repository */ |
| 502 | - const char *zUuid, /* SHA1 hash of reconstructed pBlob */ | |
| 502 | + const char *zUuid, /* artifact hash of reconstructed pBlob */ | |
| 503 | 503 | int srcId, /* pBlob is a delta from this entry */ |
| 504 | 504 | int nBlob, /* pBlob is compressed. Original size is this */ |
| 505 | 505 | int isPrivate /* The content should be marked private */ |
| 506 | 506 | ){ |
| 507 | 507 | int size; |
| @@ -868,11 +868,10 @@ | ||
| 868 | 868 | ** so forth, reporting any errors found. |
| 869 | 869 | */ |
| 870 | 870 | void test_integrity(void){ |
| 871 | 871 | Stmt q; |
| 872 | 872 | Blob content; |
| 873 | - Blob cksum; | |
| 874 | 873 | int n1 = 0; |
| 875 | 874 | int n2 = 0; |
| 876 | 875 | int nErr = 0; |
| 877 | 876 | int total; |
| 878 | 877 | int nCA = 0; |
| @@ -905,10 +904,11 @@ | ||
| 905 | 904 | db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid"); |
| 906 | 905 | total = db_int(0, "SELECT max(rid) FROM blob"); |
| 907 | 906 | while( db_step(&q)==SQLITE_ROW ){ |
| 908 | 907 | int rid = db_column_int(&q, 0); |
| 909 | 908 | const char *zUuid = db_column_text(&q, 1); |
| 909 | + int nUuid = db_column_bytes(&q, 1); | |
| 910 | 910 | int size = db_column_int(&q, 2); |
| 911 | 911 | n1++; |
| 912 | 912 | fossil_print(" %d/%d\r", n1, total); |
| 913 | 913 | fflush(stdout); |
| 914 | 914 | if( size<0 ){ |
| @@ -919,14 +919,12 @@ | ||
| 919 | 919 | if( blob_size(&content)!=size ){ |
| 920 | 920 | fossil_print("size mismatch on artifact %d: wanted %d but got %d\n", |
| 921 | 921 | rid, size, blob_size(&content)); |
| 922 | 922 | nErr++; |
| 923 | 923 | } |
| 924 | - sha1sum_blob(&content, &cksum); | |
| 925 | - if( fossil_strcmp(blob_str(&cksum), zUuid)!=0 ){ | |
| 926 | - fossil_print("wrong hash on artifact %d: wanted %s but got %s\n", | |
| 927 | - rid, zUuid, blob_str(&cksum)); | |
| 924 | + if( !hname_verify_hash(&content, zUuid, nUuid) ){ | |
| 925 | + fossil_print("wrong hash on artifact %d\n",rid); | |
| 928 | 926 | nErr++; |
| 929 | 927 | } |
| 930 | 928 | if( bParse && looks_like_control_artifact(&content) ){ |
| 931 | 929 | Blob err; |
| 932 | 930 | int i, n; |
| @@ -941,11 +939,11 @@ | ||
| 941 | 939 | memcpy(zFirstLine, z, i); |
| 942 | 940 | zFirstLine[i] = 0; |
| 943 | 941 | p = manifest_parse(&content, 0, &err); |
| 944 | 942 | if( p==0 ){ |
| 945 | 943 | fossil_print("manifest_parse failed for %s:\n%s\n", |
| 946 | - blob_str(&cksum), blob_str(&err)); | |
| 944 | + zUuid, blob_str(&err)); | |
| 947 | 945 | if( strncmp(blob_str(&err), "line 1:", 7)==0 ){ |
| 948 | 946 | fossil_print("\"%s\"\n", zFirstLine); |
| 949 | 947 | } |
| 950 | 948 | }else{ |
| 951 | 949 | anCA[p->type]++; |
| @@ -954,11 +952,10 @@ | ||
| 954 | 952 | } |
| 955 | 953 | blob_reset(&err); |
| 956 | 954 | }else{ |
| 957 | 955 | blob_reset(&content); |
| 958 | 956 | } |
| 959 | - blob_reset(&cksum); | |
| 960 | 957 | n2++; |
| 961 | 958 | } |
| 962 | 959 | db_finalize(&q); |
| 963 | 960 | fossil_print("%d non-phantom blobs (out of %d total) checked: %d errors\n", |
| 964 | 961 | n2, n1, nErr); |
| 965 | 962 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -497,11 +497,11 @@ | |
| 497 | ** to be responsible for pBlob. This routine does *not* take over |
| 498 | ** responsibility for freeing pBlob. |
| 499 | */ |
| 500 | int content_put_ex( |
| 501 | Blob *pBlob, /* Content to add to the repository */ |
| 502 | const char *zUuid, /* SHA1 hash of reconstructed pBlob */ |
| 503 | int srcId, /* pBlob is a delta from this entry */ |
| 504 | int nBlob, /* pBlob is compressed. Original size is this */ |
| 505 | int isPrivate /* The content should be marked private */ |
| 506 | ){ |
| 507 | int size; |
| @@ -868,11 +868,10 @@ | |
| 868 | ** so forth, reporting any errors found. |
| 869 | */ |
| 870 | void test_integrity(void){ |
| 871 | Stmt q; |
| 872 | Blob content; |
| 873 | Blob cksum; |
| 874 | int n1 = 0; |
| 875 | int n2 = 0; |
| 876 | int nErr = 0; |
| 877 | int total; |
| 878 | int nCA = 0; |
| @@ -905,10 +904,11 @@ | |
| 905 | db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid"); |
| 906 | total = db_int(0, "SELECT max(rid) FROM blob"); |
| 907 | while( db_step(&q)==SQLITE_ROW ){ |
| 908 | int rid = db_column_int(&q, 0); |
| 909 | const char *zUuid = db_column_text(&q, 1); |
| 910 | int size = db_column_int(&q, 2); |
| 911 | n1++; |
| 912 | fossil_print(" %d/%d\r", n1, total); |
| 913 | fflush(stdout); |
| 914 | if( size<0 ){ |
| @@ -919,14 +919,12 @@ | |
| 919 | if( blob_size(&content)!=size ){ |
| 920 | fossil_print("size mismatch on artifact %d: wanted %d but got %d\n", |
| 921 | rid, size, blob_size(&content)); |
| 922 | nErr++; |
| 923 | } |
| 924 | sha1sum_blob(&content, &cksum); |
| 925 | if( fossil_strcmp(blob_str(&cksum), zUuid)!=0 ){ |
| 926 | fossil_print("wrong hash on artifact %d: wanted %s but got %s\n", |
| 927 | rid, zUuid, blob_str(&cksum)); |
| 928 | nErr++; |
| 929 | } |
| 930 | if( bParse && looks_like_control_artifact(&content) ){ |
| 931 | Blob err; |
| 932 | int i, n; |
| @@ -941,11 +939,11 @@ | |
| 941 | memcpy(zFirstLine, z, i); |
| 942 | zFirstLine[i] = 0; |
| 943 | p = manifest_parse(&content, 0, &err); |
| 944 | if( p==0 ){ |
| 945 | fossil_print("manifest_parse failed for %s:\n%s\n", |
| 946 | blob_str(&cksum), blob_str(&err)); |
| 947 | if( strncmp(blob_str(&err), "line 1:", 7)==0 ){ |
| 948 | fossil_print("\"%s\"\n", zFirstLine); |
| 949 | } |
| 950 | }else{ |
| 951 | anCA[p->type]++; |
| @@ -954,11 +952,10 @@ | |
| 954 | } |
| 955 | blob_reset(&err); |
| 956 | }else{ |
| 957 | blob_reset(&content); |
| 958 | } |
| 959 | blob_reset(&cksum); |
| 960 | n2++; |
| 961 | } |
| 962 | db_finalize(&q); |
| 963 | fossil_print("%d non-phantom blobs (out of %d total) checked: %d errors\n", |
| 964 | n2, n1, nErr); |
| 965 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -497,11 +497,11 @@ | |
| 497 | ** to be responsible for pBlob. This routine does *not* take over |
| 498 | ** responsibility for freeing pBlob. |
| 499 | */ |
| 500 | int content_put_ex( |
| 501 | Blob *pBlob, /* Content to add to the repository */ |
| 502 | const char *zUuid, /* artifact hash of reconstructed pBlob */ |
| 503 | int srcId, /* pBlob is a delta from this entry */ |
| 504 | int nBlob, /* pBlob is compressed. Original size is this */ |
| 505 | int isPrivate /* The content should be marked private */ |
| 506 | ){ |
| 507 | int size; |
| @@ -868,11 +868,10 @@ | |
| 868 | ** so forth, reporting any errors found. |
| 869 | */ |
| 870 | void test_integrity(void){ |
| 871 | Stmt q; |
| 872 | Blob content; |
| 873 | int n1 = 0; |
| 874 | int n2 = 0; |
| 875 | int nErr = 0; |
| 876 | int total; |
| 877 | int nCA = 0; |
| @@ -905,10 +904,11 @@ | |
| 904 | db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid"); |
| 905 | total = db_int(0, "SELECT max(rid) FROM blob"); |
| 906 | while( db_step(&q)==SQLITE_ROW ){ |
| 907 | int rid = db_column_int(&q, 0); |
| 908 | const char *zUuid = db_column_text(&q, 1); |
| 909 | int nUuid = db_column_bytes(&q, 1); |
| 910 | int size = db_column_int(&q, 2); |
| 911 | n1++; |
| 912 | fossil_print(" %d/%d\r", n1, total); |
| 913 | fflush(stdout); |
| 914 | if( size<0 ){ |
| @@ -919,14 +919,12 @@ | |
| 919 | if( blob_size(&content)!=size ){ |
| 920 | fossil_print("size mismatch on artifact %d: wanted %d but got %d\n", |
| 921 | rid, size, blob_size(&content)); |
| 922 | nErr++; |
| 923 | } |
| 924 | if( !hname_verify_hash(&content, zUuid, nUuid) ){ |
| 925 | fossil_print("wrong hash on artifact %d\n",rid); |
| 926 | nErr++; |
| 927 | } |
| 928 | if( bParse && looks_like_control_artifact(&content) ){ |
| 929 | Blob err; |
| 930 | int i, n; |
| @@ -941,11 +939,11 @@ | |
| 939 | memcpy(zFirstLine, z, i); |
| 940 | zFirstLine[i] = 0; |
| 941 | p = manifest_parse(&content, 0, &err); |
| 942 | if( p==0 ){ |
| 943 | fossil_print("manifest_parse failed for %s:\n%s\n", |
| 944 | zUuid, blob_str(&err)); |
| 945 | if( strncmp(blob_str(&err), "line 1:", 7)==0 ){ |
| 946 | fossil_print("\"%s\"\n", zFirstLine); |
| 947 | } |
| 948 | }else{ |
| 949 | anCA[p->type]++; |
| @@ -954,11 +952,10 @@ | |
| 952 | } |
| 953 | blob_reset(&err); |
| 954 | }else{ |
| 955 | blob_reset(&content); |
| 956 | } |
| 957 | n2++; |
| 958 | } |
| 959 | db_finalize(&q); |
| 960 | fossil_print("%d non-phantom blobs (out of %d total) checked: %d errors\n", |
| 961 | n2, n1, nErr); |
| 962 |
+33
| --- src/hname.c | ||
| +++ src/hname.c | ||
| @@ -82,5 +82,38 @@ | ||
| 82 | 82 | default: return HNAME_ERROR; |
| 83 | 83 | } |
| 84 | 84 | if( !validate16(zHash, nHash) ) return HNAME_ERROR; |
| 85 | 85 | return id; |
| 86 | 86 | } |
| 87 | + | |
| 88 | +/* | |
| 89 | +** Verify that zHash is a valid hash for the content in pContent. | |
| 90 | +** Return true if the hash is correct. Return false if the content | |
| 91 | +** does not match the hash. | |
| 92 | +** | |
| 93 | +** Actually, the returned value is one of the hash algorithm constants | |
| 94 | +** corresponding to the hash that matched if the hash is correct. | |
| 95 | +** (Examples: HNAME_SHA1 or HNAME_K224). And the return is HNAME_ERROR | |
| 96 | +** if the hash does not match. | |
| 97 | +*/ | |
| 98 | +int hname_verify_hash(Blob *pContent, const char *zHash, int nHash){ | |
| 99 | + int id = HNAME_ERROR; | |
| 100 | + switch( nHash ){ | |
| 101 | + case HNAME_LEN_SHA1: { | |
| 102 | + Blob hash; | |
| 103 | + sha1sum_blob(pContent, &hash); | |
| 104 | + if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1; | |
| 105 | + blob_reset(&hash); | |
| 106 | + break; | |
| 107 | + } | |
| 108 | + case HNAME_LEN_K224: | |
| 109 | + case HNAME_LEN_K256: { | |
| 110 | + sha3sum_init(nHash*4); | |
| 111 | + sha3sum_step_blob(pContent); | |
| 112 | + if( memcmp(sha3sum_finish(0),zHash,nHash)==0 ){ | |
| 113 | + id = nHash==HNAME_LEN_K224 ? HNAME_K224 : HNAME_K256; | |
| 114 | + } | |
| 115 | + break; | |
| 116 | + } | |
| 117 | + } | |
| 118 | + return id; | |
| 119 | +} | |
| 87 | 120 |
| --- src/hname.c | |
| +++ src/hname.c | |
| @@ -82,5 +82,38 @@ | |
| 82 | default: return HNAME_ERROR; |
| 83 | } |
| 84 | if( !validate16(zHash, nHash) ) return HNAME_ERROR; |
| 85 | return id; |
| 86 | } |
| 87 |
| --- src/hname.c | |
| +++ src/hname.c | |
| @@ -82,5 +82,38 @@ | |
| 82 | default: return HNAME_ERROR; |
| 83 | } |
| 84 | if( !validate16(zHash, nHash) ) return HNAME_ERROR; |
| 85 | return id; |
| 86 | } |
| 87 | |
| 88 | /* |
| 89 | ** Verify that zHash is a valid hash for the content in pContent. |
| 90 | ** Return true if the hash is correct. Return false if the content |
| 91 | ** does not match the hash. |
| 92 | ** |
| 93 | ** Actually, the returned value is one of the hash algorithm constants |
| 94 | ** corresponding to the hash that matched if the hash is correct. |
| 95 | ** (Examples: HNAME_SHA1 or HNAME_K224). And the return is HNAME_ERROR |
| 96 | ** if the hash does not match. |
| 97 | */ |
| 98 | int hname_verify_hash(Blob *pContent, const char *zHash, int nHash){ |
| 99 | int id = HNAME_ERROR; |
| 100 | switch( nHash ){ |
| 101 | case HNAME_LEN_SHA1: { |
| 102 | Blob hash; |
| 103 | sha1sum_blob(pContent, &hash); |
| 104 | if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1; |
| 105 | blob_reset(&hash); |
| 106 | break; |
| 107 | } |
| 108 | case HNAME_LEN_K224: |
| 109 | case HNAME_LEN_K256: { |
| 110 | sha3sum_init(nHash*4); |
| 111 | sha3sum_step_blob(pContent); |
| 112 | if( memcmp(sha3sum_finish(0),zHash,nHash)==0 ){ |
| 113 | id = nHash==HNAME_LEN_K224 ? HNAME_K224 : HNAME_K256; |
| 114 | } |
| 115 | break; |
| 116 | } |
| 117 | } |
| 118 | return id; |
| 119 | } |
| 120 |