/*
** Copyright (c) 2017 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 file contains generic code for dealing with hashes used for
** naming artifacts. Specific hash algorithms are implemented separately
** (for example in sha1.c and sha3.c). This file contains the generic
** interface logic.
**
** "hname" is intended to be an abbreviation of "hash name".
*/
#include "config.h"
#include "hname.h"
#if INTERFACE
/*
** Code numbers for the allowed hash algorithms.
*/
#define HNAME_ERROR 0 /* Not a valid hash */
#define HNAME_SHA1 1 /* SHA1 */
#define HNAME_K256 2 /* SHA3-256 */
/*
** Minimum and maximum lengths for a hash value when hex encoded.
*/
#define HNAME_MIN 40 /* Length for SHA1 */
#define HNAME_MAX 64 /* Length for SHA3-256 */
/*
** Hash lengths for the various algorithms
*/
#define HNAME_LEN_SHA1 40
#define HNAME_LEN_K256 64
/*
** The number of distinct hash algorithms:
*/
#define HNAME_COUNT 2 /* Just SHA1 and SHA3-256. Let's keep it that way! */
/*
** Hash naming policies
*/
#define HPOLICY_SHA1 0 /* Use SHA1 hashes */
#define HPOLICY_AUTO 1 /* SHA1 but auto-promote to SHA3 */
#define HPOLICY_SHA3 2 /* Use SHA3 hashes */
#define HPOLICY_SHA3_ONLY 3 /* Use SHA3 hashes exclusively */
#define HPOLICY_SHUN_SHA1 4 /* Shun all SHA1 objects */
#endif /* INTERFACE */
/*
** Return a human-readable name for the hash algorithm given a hash with
** a length of nHash hexadecimal digits.
*/
const char *hname_alg(int nHash){
if( nHash==HNAME_LEN_SHA1 ) return "SHA1";
if( nHash==HNAME_LEN_K256 ) return "SHA3-256";
return "?";
}
/*
** Return the integer hash algorithm code number (ex: HNAME_K256) for
** the hash string provided. Or return HNAME_ERROR (0) if the input string
** is not a valid artifact hash string.
*/
int hname_validate(const char *zHash, int nHash){
int id;
switch( nHash ){
case HNAME_LEN_SHA1: id = HNAME_SHA1; break;
case HNAME_LEN_K256: id = HNAME_K256; break;
default: return HNAME_ERROR;
}
if( !validate16(zHash, nHash) ) return HNAME_ERROR;
return id;
}
/*
** Verify that zHash is a valid hash for the content in pContent.
** Return true if the hash is correct. Return false if the content
** does not match the hash.
**
** Actually, the returned value is one of the hash algorithm constants
** corresponding to the hash that matched if the hash is correct.
** (Examples: HNAME_SHA1 or HNAME_K256). And the return is HNAME_ERROR
** if the hash does not match.
*/
int hname_verify_hash(Blob *pContent, const char *zHash, int nHash){
int id = HNAME_ERROR;
switch( nHash ){
case HNAME_LEN_SHA1: {
Blob hash;
sha1sum_blob(pContent, &hash);
if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1;
blob_reset(&hash);
break;
}
case HNAME_LEN_K256: {
sha3sum_init(256);
sha3sum_step_blob(pContent);
if( memcmp(sha3sum_finish(0),zHash,64)==0 ) id = HNAME_K256;
break;
}
}
return id;
}
/*
** Verify that zHash is a valid hash for the content of a file on
** disk named zFile.
**
** Return true if the hash is correct. Return false if the content
** does not match the hash.
**
** Actually, the returned value is one of the hash algorithm constants
** corresponding to the hash that matched if the hash is correct.
** (Examples: HNAME_SHA1 or HNAME_K256). And the return is HNAME_ERROR
** if the hash does not match.
*/
int hname_verify_file_hash(const char *zFile, const char *zHash, int nHash){
int id = HNAME_ERROR;
switch( nHash ){
case HNAME_LEN_SHA1: {
Blob hash;
if( sha1sum_file(zFile, RepoFILE, &hash) ) break;
if( memcmp(blob_buffer(&hash),zHash,HNAME_LEN_SHA1)==0 ) id = HNAME_SHA1;
blob_reset(&hash);
break;
}
case HNAME_LEN_K256: {
Blob hash;
if( sha3sum_file(zFile, RepoFILE, 256, &hash) ) break;
if( memcmp(blob_buffer(&hash),zHash,64)==0 ) id = HNAME_LEN_K256;
blob_reset(&hash);
break;
}
}
return id;
}
/*
** Compute a hash on blob pContent. Write the hash into blob pHashOut.
** This routine assumes that pHashOut is uninitialized.
**
** The preferred hash is used for iHType==0 and the alternative hash is
** used if iHType==1. (The interface is designed to accommodate more than
** just two hashes, but HNAME_COUNT is currently fixed at 2.)
**
** Depending on the hash policy, the alternative hash may be disallowed.
** If the alterative hash is disallowed, the routine returns 0. This
** routine returns 1 if iHType>0 and the alternative hash is allowed,
** and it always returns 1 when iHType==0.
**
** Alternative hash is disallowed for all hash policies except auto,
** sha1 and sha3.
*/
int hname_hash(const Blob *pContent, unsigned int iHType, Blob *pHashOut){
assert( iHType==0 || iHType==1 );
if( iHType==1 ){
switch( g.eHashPolicy ){
case HPOLICY_AUTO:
case HPOLICY_SHA1:
sha3sum_blob(pContent, 256, pHashOut);
return 1;
case HPOLICY_SHA3:
sha1sum_blob(pContent, pHashOut);
return 1;
}
}
if( iHType==0 ){
switch( g.eHashPolicy ){
case HPOLICY_SHA1:
case HPOLICY_AUTO:
sha1sum_blob(pContent, pHashOut);
return 1;
case HPOLICY_SHA3:
case HPOLICY_SHA3_ONLY:
case HPOLICY_SHUN_SHA1:
sha3sum_blob(pContent, 256, pHashOut);
return 1;
}
}
blob_init(pHashOut, 0, 0);
return 0;
}
/*
** Return the default hash policy for repositories that do not currently
** have an assigned hash policy.
**
** Make the default HPOLICY_AUTO if there are SHA1 artficates but no SHA3
** artifacts in the repository. Make the default HPOLICY_SHA3 if there
** are one or more SHA3 artifacts or if the repository is initially empty.
*/
int hname_default_policy(void){
if( db_exists("SELECT 1 FROM blob WHERE length(uuid)>40")
|| !db_exists("SELECT 1 FROM blob WHERE length(uuid)==40")
){
return HPOLICY_SHA3;
}else{
return HPOLICY_AUTO;
}
}
/*
** Names of the hash policies.
*/
static const char *const azPolicy[] = {
"sha1", "auto", "sha3", "sha3-only", "shun-sha1"
};
/* Return the name of the current hash policy.
*/
const char *hpolicy_name(void){
return azPolicy[g.eHashPolicy];
}
/*
** COMMAND: hash-policy*
**
** Usage: fossil hash-policy ?NEW-POLICY?
**
** Query or set the hash policy for the current repository. Available hash
** policies are as follows:
**
** sha1 New artifact names are created using SHA1
**
** auto New artifact names are created using SHA1, but
** automatically change the policy to "sha3" when
** any SHA3 artifact enters the repository.
**
** sha3 New artifact names are created using SHA3, but
** older artifacts with SHA1 names may be reused.
**
** sha3-only Use only SHA3 artifact names. Do not reuse legacy
** SHA1 names.
**
** shun-sha1 Shun any SHA1 artifacts received by sync operations
** other than clones. Older legacy SHA1 artifacts are
** allowed during a clone.
**
** The default hash policy for existing repositories is "auto", which will
** immediately promote to "sha3" if the repository contains one or more
** artifacts with SHA3 names. The default hash policy for new repositories
** is "shun-sha1".
*/
void hash_policy_command(void){
int i;
db_find_and_open_repository(0, 0);
if( g.argc!=2 && g.argc!=3 ) usage("?NEW-POLICY?");
if( g.argc==2 ){
fossil_print("%s\n", azPolicy[g.eHashPolicy]);
return;
}
for(i=HPOLICY_SHA1; i<=HPOLICY_SHUN_SHA1; i++){
if( fossil_strcmp(g.argv[2],azPolicy[i])==0 ){
if( i==HPOLICY_AUTO
&& db_exists("SELECT 1 FROM blob WHERE length(uuid)>40")
){
i = HPOLICY_SHA3;
}
g.eHashPolicy = i;
db_set_int("hash-policy", i, 0);
fossil_print("%s\n", azPolicy[i]);
return;
}
}
fossil_fatal("unknown hash policy \"%s\" - should be one of: sha1 auto"
" sha3 sha3-only shun-sha1", g.argv[2]);
}