Signing a file given its hash only

Jay Acuna mysidia at gmail.com
Thu May 22 01:16:19 CEST 2025


On Wed, May 21, 2025 at 3:36 PM Björn Persson <Bjorn at rombobjörn.se> wrote:
> Like the vulnerability you introduced right here:
>..
> That script ignores the result of the signature verification. It will
> return success if sha256sum finds that the hash matches the file, even
> if the signature is invalid.

This is meant to show the concept; you would need to adjust and
test the pass/failure conditions for your application.

It's still obviously better than attempting to modify internal
OpenPGP protocols to "break apart" the signing process.  Using
PGP to sign the file listing the filename and hash + other data does provide
everything necessary to have a verify wrapper confirm the integrity of
the signed thing based on the signed hash text.



We can avoid relying on the shell altogether, then:


/* gcc -o verify1 verify1.c -lssl -lcrypto */
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <openssl/evp.h>
#include <openssl/sha.h>

int check_hash(const char* filename, const char *hashvalue)
{
   int fd;
   char buffer[4096+2] = "";

   fprintf(stderr,"check_hash(%s,%s)S\n", filename, hashvalue);

   if ((fd = open(filename, O_RDONLY))  == -1) {  perror("open");
return -1;   }
   unsigned char digest[SHA256_DIGEST_LENGTH] = "";
   EVP_MD_CTX* mdctx = NULL;
   const EVP_MD* md = NULL;
   unsigned int dsize = 0;

    if (!(md = EVP_get_digestbyname("SHA256"))) {
        fputs("SHA256 algorithm missing.\n",stderr);
        return -1;
    }

    mdctx = EVP_MD_CTX_new();
    if (!mdctx || EVP_DigestInit_ex2(mdctx, md, NULL) != 1) {
        fputs("EVP_DigestInit_ex fail\n",stderr);
        EVP_MD_CTX_free(mdctx);
        return 1;
    }

    ssize_t len;
    while( (len = read(fd, buffer, 4096)) > 0 ) {
        if (EVP_DigestUpdate(mdctx, buffer, len) != 1) {
            fputs("EVP_DigestUpdate failed\n",stderr);
            EVP_MD_CTX_free(mdctx);
            return -1;
        }
if (len < 4096) break;
    }

    if (EVP_DigestFinal_ex(mdctx, digest, &dsize) != 1) {
        fputs("Final failed\n",stderr);
        EVP_MD_CTX_free(mdctx);
        return -1;
    }
    EVP_MD_CTX_free(mdctx);

    printf("check_hash calculated: ");
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        printf("%02x", digest[i]);
    }
    puts("\n");
    close(fd);

    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
         if (!hashvalue || !hashvalue[2*i] || !hashvalue[2*i+1] )
return -1;
sprintf(buffer, "%02x", digest[i]);
if ( hashvalue[2*i] != buffer[0] || hashvalue[2*i+1] != buffer[1] )
return -1;
    }
   return 0;
}

int main(int argc, char* argv[]) {
  for(int i = 1; i < argc; i++) {
      pid_t p; int st,  filedes[2];
      int hashcount = 0;
      if (pipe(filedes) == -1) { perror("pipe"); abort(); }
      switch(p = fork()) {
       case -1: perror("fork");  abort(); break;
       case  0:
                close(filedes[0]);
                dup2(filedes[1], STDOUT_FILENO);
                char* filename;
                if ( asprintf(&filename, "%s.signedhash", argv[i]) == -1 ) {
                    perror("asprintf"); abort(); }
                execlp("gpg", "gpg", "-d", filename, NULL);
                perror("exec");
                return -1;
       default:
                waitpid(p, &st, 0);
                while( !WIFEXITED(st) && !WIFSIGNALED(st) ) {
                   sleep(1);
                   waitpid(p, &st, 0); }
                if (WIFSIGNALED(st)) { exit(1); }
                if (WEXITSTATUS(st) != 0) {
                    fputs("GPG Verify failed\n", stderr);
                    exit(1); }
                char buffer[4096+2] = "", line[4096+2] = "", *p1, *p2;
                ssize_t len, li=0;

                while ( (len = read(filedes[0], buffer, 4096))  ) {
                    for (ssize_t i = 0; i < len; i++) {
                          if (*(buffer+i) == '\n') {
                              *(line+li) = '\0';
                              if ( (p1 = strchr(line, ' ')) && (p2 =
strrchr(p1, ' ')) ) {
                                  *p1 = '\0';
                                  hashcount++ ;

                                  if ( check_hash(p2 + 1, line) == -1  ) {
                                      fputs("Hash check failed.\n", stderr);
                                  } else fputs("Hash check passes.\n", stderr);
                              }
                              li=0;
                          } else {
                              *(line+li) = *(buffer+i);
                              if(++li >= 4096) {
                                 *(line+li) = 0;
                                 li = 0;
                              }
                          }
                    }
                    if (len < 4096) {
                        break;
                    }
                }
                if (hashcount != 1) {
                    fputs("Signed text does not contain the correct
number of SHA256 hashes\n", stderr);
                    exit(1);
                }
  }
}
}








> Björn Persson
-JA



More information about the Gnupg-users mailing list