]> the.earth.li Git - onak.git/commitdiff
Add SHA1x hash support
authorJonathan McDowell <noodles@earth.li>
Tue, 5 Nov 2013 04:52:38 +0000 (20:52 -0800)
committerJonathan McDowell <noodles@earth.li>
Tue, 5 Nov 2013 04:52:38 +0000 (20:52 -0800)
A hash variant that uses 8 SHA-1 instances and results in a double width
hash (320 bits instead of SHA-1's 160 bits). This was used in older
versions of PGP and there are quite a few keys on the keyservers with
signatures using it. Added mainly to enable the sig hash checking
routine to do some sanity checking on these signatures.

Makefile.in
sha1x.c [new file with mode: 0644]
sha1x.h [new file with mode: 0644]
sigcheck.c

index 90db9f921dfc7ac989cbf1fd802fc2fef5e0243a..da116ac19ca25baad2f39c0dd1689319c397a1b4 100644 (file)
@@ -21,7 +21,8 @@ PROGS = add lookup hashquery gpgwww onak splitkeys onak-mail.pl stripkey \
        wotsap
 CORE_OBJS = armor.o charfuncs.o decodekey.o getcgi.o hash.o marshal.o \
        keyid.o keyindex.o ll.o mem.o onak-conf.o parsekey.o sigcheck.o \
-       log.o photoid.o wordlist.o cleanup.o merge.o sendsync.o keyarray.o
+       log.o photoid.o wordlist.o cleanup.o merge.o sendsync.o keyarray.o \
+       sha1x.o
 ifeq (x@NETTLE_LIBS@, x)
 CORE_OBJS += md5.o sha1.o
 endif
@@ -29,6 +30,7 @@ SRCS = armor.c parsekey.c merge.c keyid.c md5.c sha1.c main.c getcgi.c mem.c \
        keyindex.c stats.c lookup.c add.c keydb_$(DBTYPE).c ll.c hash.c \
        gpgwww.c onak-conf.c charfuncs.c sendsync.c log.c photoid.c sigcheck.c \
        wordlist.c cleankey.c cleanup.c keyarray.c hashquery.c marshal.c \
+       sha1x.c \
        $(foreach be,@BACKENDS@,keydb_$(be).c)
 PROGS_LDFLAGS_EXTRA =
 
diff --git a/sha1x.c b/sha1x.c
new file mode 100644 (file)
index 0000000..1242440
--- /dev/null
+++ b/sha1x.c
@@ -0,0 +1,144 @@
+/*
+ * sha1x.c - Double width SHA-1 as per PGP 5.5
+ *
+ * Copyright 2013 Jonathan McDowell <noodles@earth.li>
+ *
+ * This is based on the description / code from PGP 5.5, where it is called
+ * "SHA Double". I have seen reference to SHA1X elsewhere, which is a more
+ * concise name, so I have used that here.
+ *
+ * I can't imagine there is a good reason to use this code other than for
+ * verifying signatures on ancient PGP keys.
+ *
+ * Placed into the public domain.
+ */
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifdef HAVE_NETTLE
+#include <nettle/sha.h>
+#else
+#include "sha1.h"
+#endif
+#include "sha1x.h"
+
+#define BUFSIZE 64
+
+void sha1x_init(struct sha1x_ctx *ctx)
+{
+       unsigned char zeros[3];
+
+       zeros[0] = zeros[1] = zeros[2] = 0;
+       sha1_init(&ctx->a);
+       sha1_init(&ctx->b);
+       sha1_init(&ctx->c);
+       sha1_init(&ctx->d);
+
+       sha1_update(&ctx->b, 1, zeros);
+       sha1_update(&ctx->c, 2, zeros);
+       sha1_update(&ctx->d, 3, zeros);
+
+       /* We start at 0, so even */
+       ctx->odd = false;
+}
+
+void sha1x_update(struct sha1x_ctx *ctx, unsigned length, const uint8_t *data)
+{
+       uint8_t evenbuf[BUFSIZE], *evenp;
+       uint8_t oddbuf[BUFSIZE], *oddp;
+       bool newodd;
+
+       oddp = oddbuf;
+       evenp = evenbuf;
+
+       /* Track whether our first byte next time round is even or odd */
+       newodd = ctx->odd ^ (length & 1);
+
+       /* If our first byte is odd this time, add it to the odd buffer */
+       if (ctx->odd && length != 0) {
+               *oddp++ = *data++;
+               length--;
+       }
+       ctx->odd = newodd;
+
+       while (length != 0) {
+               while (length != 0 && oddp < oddbuf + BUFSIZE) {
+                       *evenp++ = *data++;
+                       length--;
+                       if (length == 0) {
+                               break;
+                       }
+                       *oddp++ = *data++;
+                       length--;
+               }
+               sha1_update(&ctx->a, evenp - evenbuf, evenbuf);
+               sha1_update(&ctx->b, evenp - evenbuf, evenbuf);
+               sha1_update(&ctx->c, oddp - oddbuf, oddbuf);
+               sha1_update(&ctx->d, oddp - oddbuf, oddbuf);
+
+               oddp = oddbuf;
+               evenp = evenbuf;
+       }
+}
+
+void sha1x_digest(struct sha1x_ctx *ctx, unsigned length, uint8_t *digest)
+{
+       uint8_t sha1final[8][SHA1_DIGEST_SIZE];
+       uint8_t zeros[7];
+       struct sha1_ctx e, f, g, h;
+       int i;
+
+       sha1_digest(&ctx->a, SHA1_DIGEST_SIZE, sha1final[0]);
+       sha1_digest(&ctx->b, SHA1_DIGEST_SIZE, sha1final[1]);
+       sha1_digest(&ctx->c, SHA1_DIGEST_SIZE, sha1final[2]);
+       sha1_digest(&ctx->d, SHA1_DIGEST_SIZE, sha1final[3]);
+
+       /* XOR sha1-c into sha1-a & sha1-d into sha1-b */
+       for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+               sha1final[0][i] ^= sha1final[2][i];
+               sha1final[1][i] ^= sha1final[3][i];
+       }
+
+       sha1_init(&e);
+       sha1_init(&f);
+       sha1_init(&g);
+       sha1_init(&h);
+
+       memset(zeros, 0, sizeof(zeros));
+       sha1_update(&e, 4, zeros);
+       sha1_update(&f, 5, zeros);
+       sha1_update(&g, 6, zeros);
+       sha1_update(&h, 7, zeros);
+
+       sha1_update(&e, SHA1_DIGEST_SIZE, sha1final[0]);
+       sha1_update(&f, SHA1_DIGEST_SIZE, sha1final[0]);
+       sha1_update(&g, SHA1_DIGEST_SIZE, sha1final[1]);
+       sha1_update(&h, SHA1_DIGEST_SIZE, sha1final[1]);
+
+       sha1_digest(&e, SHA1_DIGEST_SIZE, sha1final[4]);
+       sha1_digest(&f, SHA1_DIGEST_SIZE, sha1final[5]);
+       sha1_digest(&g, SHA1_DIGEST_SIZE, sha1final[6]);
+       sha1_digest(&h, SHA1_DIGEST_SIZE, sha1final[7]);
+
+       /* XOR sha1-g into sha1-e & sha1-h into sha1-f */
+       for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+               sha1final[4][i] ^= sha1final[6][i];
+               sha1final[5][i] ^= sha1final[7][i];
+       }
+
+       if (length > SHA1X_DIGEST_SIZE) {
+               length = SHA1X_DIGEST_SIZE;
+       }
+
+       for (i = 0; i < length; i++) {
+               if (i < SHA1_DIGEST_SIZE) {
+                       digest[i] = sha1final[4][i];
+               } else {
+                       digest[i] = sha1final[6][i - SHA1_DIGEST_SIZE];
+               }
+       }
+}
diff --git a/sha1x.h b/sha1x.h
new file mode 100644 (file)
index 0000000..f1d5dca
--- /dev/null
+++ b/sha1x.h
@@ -0,0 +1,40 @@
+/*
+ * sha1x.h - Double width SHA-1 as per PGP 5.5
+ *
+ * Copyright 2013 Jonathan McDowell <noodles@earth.li>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the 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. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __SHA1X_H__
+#define __SHA1X_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef HAVE_NETTLE
+#include <nettle/sha.h>
+#else
+#include "sha1.h"
+#endif
+
+#define SHA1X_DIGEST_SIZE (2 * SHA1_DIGEST_SIZE)
+
+struct sha1x_ctx {
+       struct sha1_ctx a, b, c, d;
+       bool odd;
+       unsigned char result[SHA1X_DIGEST_SIZE];
+};
+
+void sha1x_init(struct sha1x_ctx *ctx);
+void sha1x_update(struct sha1x_ctx *ctx, unsigned length, const uint8_t *data);
+void sha1x_digest(struct sha1x_ctx *ctx, unsigned length, uint8_t *digest);
+
+#endif /* __SHA1X_H__ */
index cbc28b87ccfbeacaade0f5a2c88d2a76c1e5a788..900d3514fab414bd30ee85580b58a5c7108c9067 100644 (file)
@@ -34,6 +34,7 @@
 #include "md5.h"
 #include "sha1.h"
 #endif
+#include "sha1x.h"
 
 int check_packet_sighash(struct openpgp_publickey *key,
                        struct openpgp_packet *packet,
@@ -43,6 +44,7 @@ int check_packet_sighash(struct openpgp_publickey *key,
        uint8_t *sighash;
        size_t siglen, unhashedlen;
        struct sha1_ctx sha1_context;
+       struct sha1x_ctx sha1x_context;
        struct md5_ctx md5_context;
 #ifdef NETTLE_WITH_RIPEMD160
        struct ripemd160_ctx ripemd160_context;
@@ -187,6 +189,13 @@ int check_packet_sighash(struct openpgp_publickey *key,
                logthing(LOGTHING_INFO, "RIPEMD160 support not available.");
                return -1;
 #endif
+       case OPENPGP_HASH_SHA1X:
+               sha1x_init(&sha1x_context);
+               for (i = 0; i < chunks; i++) {
+                       sha1x_update(&sha1x_context, hashlen[i], hashdata[i]);
+               }
+               sha1x_digest(&sha1x_context, 20, hash);
+               break;
        case OPENPGP_HASH_SHA224:
 #ifdef NETTLE_WITH_SHA224
                sha224_init(&sha224_context);