From 4f49b16079b27d7ce633619e4cddfdf2bffbc952 Mon Sep 17 00:00:00 2001 From: cashmere Date: Fri, 4 Sep 2026 09:05:24 +0200 Subject: initial commit --- src/sha1.c | 549 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 src/sha1.c (limited to 'src/sha1.c') diff --git a/src/sha1.c b/src/sha1.c new file mode 100644 index 0000000..5874693 --- /dev/null +++ b/src/sha1.c @@ -0,0 +1,549 @@ +/* +** Copyright (c) 2006 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the Simplified BSD License (also +** known as the "2-Clause License" or "FreeBSD License".) +** +** This program is distributed in the hope that it will be useful, +** but without any warranty; without even the implied warranty of +** merchantability or fitness for a particular purpose. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +******************************************************************************* +** +** This implementation of SHA1. +*/ +#include "config.h" +#include +#include "sha1.h" + + +/* +** SHA1 Implementation #1 is the hardened SHA1 implementation by +** Marc Stevens. Code obtained from GitHub +** +** https://github.com/cr-marcstevens/sha1collisiondetection +** +** Downloaded on 2017-03-01 then repackaged to work with Fossil +** and makeheaders. +*/ +#if FOSSIL_HARDENED_SHA1 + +#if INTERFACE +typedef void(*collision_block_callback)(uint64_t, const uint32_t*, + const uint32_t*, const uint32_t*, const uint32_t*); +struct SHA1_CTX { + uint64_t total; + uint32_t ihv[5]; + unsigned char buffer[64]; + int bigendian; + int found_collision; + int safe_hash; + int detect_coll; + int ubc_check; + int reduced_round_coll; + collision_block_callback callback; + + uint32_t ihv1[5]; + uint32_t ihv2[5]; + uint32_t m1[80]; + uint32_t m2[80]; + uint32_t states[80][5]; +}; +#endif +void SHA1DCInit(SHA1_CTX*); +void SHA1DCUpdate(SHA1_CTX*, const unsigned char*, unsigned); +int SHA1DCFinal(unsigned char[20], SHA1_CTX*); + +#define SHA1Context SHA1_CTX +#define SHA1Init SHA1DCInit +#define SHA1Update SHA1DCUpdate +#define SHA1Final SHA1DCFinal + +/* +** SHA1 Implementation #2: use the SHA1 algorithm built into SSL +*/ +#elif defined(FOSSIL_ENABLE_SSL) + +# include +# define SHA1Context SHA_CTX +# define SHA1Init SHA1_Init +# define SHA1Update SHA1_Update +# define SHA1Final SHA1_Final + +/* +** SHA1 Implementation #3: If none of the previous two SHA1 +** algorithms work, there is this built-in. This built-in was the +** original implementation used by Fossil. +*/ +#else +/* +** The SHA1 implementation below is adapted from: +** +** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ +** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ +** +** SHA-1 in C +** By Steve Reid +** 100% Public Domain +*/ +typedef struct SHA1Context SHA1Context; +struct SHA1Context { + unsigned int state[5]; + unsigned int count[2]; + unsigned char buffer[64]; +}; + +/* + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + * + * blk0le() for little-endian and blk0be() for big-endian. + */ +#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r)) +#define rol(x,k) SHA_ROT(x,k,32-(k)) +#define ror(x,k) SHA_ROT(x,32-(k),k) +#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \ + |(rol(block[i],8)&0x00FF00FF)) +#define blk0be(i) block[i] +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ + ^block[(i+2)&15]^block[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + * + * Rl0() for little-endian and Rb0() for big-endian. Endianness is + * determined at run-time. + */ +#define Rl0(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define Rb0(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define R1(v,w,x,y,z,i) \ + z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2); +#define R2(v,w,x,y,z,i) \ + z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2); +#define R3(v,w,x,y,z,i) \ + z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2); +#define R4(v,w,x,y,z,i) \ + z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2); + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +#define a qq[0] +#define b qq[1] +#define c qq[2] +#define d qq[3] +#define e qq[4] + +void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]) +{ + unsigned int qq[5]; /* a, b, c, d, e; */ + static int one = 1; + unsigned int block[16]; + memcpy(block, buffer, 64); + memcpy(qq,state,5*sizeof(unsigned int)); + + /* Copy context->state[] to working vars */ + /* + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + */ + + /* 4 rounds of 20 operations each. Loop unrolled. */ + if( 1 == *(unsigned char*)&one ){ + Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3); + Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7); + Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11); + Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15); + }else{ + Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3); + Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7); + Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11); + Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15); + } + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; +} + + +/* + * SHA1Init - Initialize new context + */ +static void SHA1Init(SHA1Context *context){ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* + * Run your data through this. + */ +static void SHA1Update( + SHA1Context *context, + const unsigned char *data, + unsigned int len +){ + unsigned int i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1] += (len>>29)+1; + j = (j >> 3) & 63; + if ((j + len) > 63) { + (void)memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + SHA1Transform(context->state, &data[i]); + j = 0; + } else { + i = 0; + } + (void)memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* + * Add padding and return the message digest. + */ +static void SHA1Final(unsigned char *digest, SHA1Context *context){ + unsigned int i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (const unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) + SHA1Update(context, (const unsigned char *)"\0", 1); + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + + if (digest) { + for (i = 0; i < 20; i++) + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} +#endif /* Built-in SHA1 implementation */ + +/* +** Convert a digest into base-16. digest should be declared as +** "unsigned char digest[20]" in the calling function. The SHA1 +** digest is stored in the first 20 bytes. zBuf should +** be "char zBuf[41]". +*/ +static void DigestToBase16(unsigned char *digest, char *zBuf){ + static const char zEncode[] = "0123456789abcdef"; + int ix; + + for(ix=0; ix<20; ix++){ + *zBuf++ = zEncode[(*digest>>4)&0xf]; + *zBuf++ = zEncode[*digest++ & 0xf]; + } + *zBuf = '\0'; +} + +/* +** The state of an incremental SHA1 checksum computation. Only one +** such computation can be underway at a time, of course. +*/ +static SHA1Context incrCtx; +static int incrInit = 0; + +/* +** Add more text to the incremental SHA1 checksum. +*/ +void sha1sum_step_text(const char *zText, int nBytes){ + if( !incrInit ){ + SHA1Init(&incrCtx); + incrInit = 1; + } + if( nBytes<=0 ){ + if( nBytes==0 ) return; + nBytes = strlen(zText); + } + SHA1Update(&incrCtx, (unsigned char*)zText, nBytes); +} + +/* +** Add the content of a blob to the incremental SHA1 checksum. +*/ +void sha1sum_step_blob(Blob *p){ + sha1sum_step_text(blob_buffer(p), blob_size(p)); +} + +/* +** Finish the incremental SHA1 checksum. Store the result in blob pOut +** if pOut!=0. Also return a pointer to the result. +** +** This resets the incremental checksum preparing for the next round +** of computation. The return pointer points to a static buffer that +** is overwritten by subsequent calls to this function. +*/ +char *sha1sum_finish(Blob *pOut){ + unsigned char zResult[20]; + static char zOut[41]; + sha1sum_step_text(0,0); + SHA1Final(zResult, &incrCtx); + incrInit = 0; + DigestToBase16(zResult, zOut); + if( pOut ){ + blob_zero(pOut); + blob_append(pOut, zOut, 40); + } + return zOut; +} + + +/* +** Compute the SHA1 checksum of a file on disk. Store the resulting +** checksum in the blob pCksum. pCksum is assumed to be initialized. +** +** Return the number of errors. +*/ +int sha1sum_file(const char *zFilename, int eFType, Blob *pCksum){ + FILE *in; + SHA1Context ctx; + unsigned char zResult[20]; + char zBuf[10240]; + + if( eFType==RepoFILE && file_islink(zFilename) ){ + /* Instead of file content, return sha1 of link destination path */ + Blob destinationPath; + int rc; + + blob_read_link(&destinationPath, zFilename); + rc = sha1sum_blob(&destinationPath, pCksum); + blob_reset(&destinationPath); + return rc; + } + + in = fossil_fopen(zFilename,"rb"); + if( in==0 ){ + return 1; + } + SHA1Init(&ctx); + for(;;){ + int n; + n = fread(zBuf, 1, sizeof(zBuf), in); + if( n<=0 ) break; + SHA1Update(&ctx, (unsigned char*)zBuf, (unsigned)n); + } + fclose(in); + blob_zero(pCksum); + blob_resize(pCksum, 40); + SHA1Final(zResult, &ctx); + DigestToBase16(zResult, blob_buffer(pCksum)); + return 0; +} + +/* +** Compute the SHA1 checksum of a blob in memory. Store the resulting +** checksum in the blob pCksum. pCksum is assumed to be either +** uninitialized or the same blob as pIn. +** +** Return the number of errors. +*/ +int sha1sum_blob(const Blob *pIn, Blob *pCksum){ + SHA1Context ctx; + unsigned char zResult[20]; + + SHA1Init(&ctx); + SHA1Update(&ctx, (unsigned char*)blob_buffer(pIn), blob_size(pIn)); + if( pIn==pCksum ){ + blob_reset(pCksum); + }else{ + blob_zero(pCksum); + } + blob_resize(pCksum, 40); + SHA1Final(zResult, &ctx); + DigestToBase16(zResult, blob_buffer(pCksum)); + return 0; +} + +/* +** Compute a binary SHA1 checksum of a zero-terminated string. The +** result is stored in zOut, which is a buffer that must be at least +** 20 bytes in size. +*/ +void sha1sum_binary(const char *zIn, unsigned char *zOut){ + SHA1Context ctx; + + SHA1Init(&ctx); + SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); + SHA1Final(zOut, &ctx); +} + +/* +** Compute the SHA1 checksum of a zero-terminated string. The +** result is held in memory obtained from mprintf(). +*/ +char *sha1sum(const char *zIn){ + SHA1Context ctx; + unsigned char zResult[20]; + char zDigest[41]; + + SHA1Init(&ctx); + SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); + SHA1Final(zResult, &ctx); + DigestToBase16(zResult, zDigest); + return fossil_strdup(zDigest); +} + +/* +** Convert a cleartext password for a specific user into a SHA1 hash. +** +** The algorithm here is: +** +** SHA1( project-code + "/" + login + "/" + password ) +** +** In words: The users login name and password are appended to the +** project ID code and the SHA1 hash of the result is computed. +** +** The result of this function is the shared secret used by a client +** to authenticate to a server for the sync protocol. It is also the +** value stored in the USER.PW field of the database. By mixing in the +** login name and the project id with the hash, different shared secrets +** are obtained even if two users select the same password, or if a +** single user selects the same password for multiple projects. +*/ +char *sha1_shared_secret( + const char *zPw, /* The password to encrypt */ + const char *zLogin, /* Username */ + const char *zProjCode /* Project-code. Use built-in project code if NULL */ +){ + static char *zProjectId = 0; + SHA1Context ctx; + unsigned char zResult[20]; + char zDigest[41]; + + SHA1Init(&ctx); + if( zProjCode==0 ){ + if( zProjectId==0 ){ + zProjectId = db_get("project-code", 0); + + /* On the first xfer request of a clone, the project-code is not yet + ** known. Use the cleartext password, since that is all we have. + */ + if( zProjectId==0 ){ + return fossil_strdup(zPw); + } + } + zProjCode = zProjectId; + } + SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode)); + SHA1Update(&ctx, (unsigned char*)"/", 1); + SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin)); + SHA1Update(&ctx, (unsigned char*)"/", 1); + SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw)); + SHA1Final(zResult, &ctx); + DigestToBase16(zResult, zDigest); + return fossil_strdup(zDigest); +} + +/* +** Implement the shared_secret() SQL function. shared_secret() takes two or +** three arguments; the third argument is optional. +** +** (1) The cleartext password +** (2) The login name +** (3) The project code +** +** Returns sha1($password/$login/$projcode). +*/ +void sha1_shared_secret_sql_function( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zPw; + const char *zLogin; + const char *zProjid; + + assert( argc==2 || argc==3 ); + zPw = (const char*)sqlite3_value_text(argv[0]); + if( zPw==0 || zPw[0]==0 ) return; + zLogin = (const char*)sqlite3_value_text(argv[1]); + if( zLogin==0 ) return; + if( argc==3 ){ + zProjid = (const char*)sqlite3_value_text(argv[2]); + if( zProjid && zProjid[0]==0 ) zProjid = 0; + }else{ + zProjid = 0; + } + sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, + fossil_free); +} + +/* +** COMMAND: sha1sum* +** +** Usage: %fossil sha1sum FILE... +** +** Compute an SHA1 checksum of all files named on the command-line. +** If a file is named "-" then take its content from standard input. +** +** Options: +** -h|--dereference If FILE is a symbolic link, compute the hash +** on the object that the link points to. Normally, +** the hash is over the name of the object that +** the link points to. +** +** See also: [[md5sum]], [[sha3sum]] +*/ +void sha1sum_test(void){ + int i; + Blob in; + Blob cksum; + int eFType = SymFILE; + if( find_option("dereference","h",0)!=0 ){ + eFType = ExtFILE; + } + + for(i=2; i