summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2013-03-17 13:06:02 +0100
committerRainer Gerhards <rgerhards@adiscon.com>2013-03-17 13:06:02 +0100
commitd2467c38d42f590deecd807741324fc0e5522a8a (patch)
treec9d56cee1e0119ba9bd5f516a68bd7e1269712fe
parent44b4922825df794f678cd4ad18d940ff114b943f (diff)
downloadrsyslog-d2467c38d42f590deecd807741324fc0e5522a8a.tar.gz
rsyslog-d2467c38d42f590deecd807741324fc0e5522a8a.tar.bz2
rsyslog-d2467c38d42f590deecd807741324fc0e5522a8a.zip
logsig: milestone/verfier: record hashes are verified
-rw-r--r--runtime/librsgt.c6
-rw-r--r--runtime/librsgt.h33
-rw-r--r--runtime/librsgt_read.c126
-rw-r--r--runtime/lmsig_gt.c12
-rw-r--r--tools/rsgtutil.c95
5 files changed, 266 insertions, 6 deletions
diff --git a/runtime/librsgt.c b/runtime/librsgt.c
index 83a2fe05..e49011f4 100644
--- a/runtime/librsgt.c
+++ b/runtime/librsgt.c
@@ -514,7 +514,7 @@ bufAddLevel(uchar *buf, size_t *len, uint8_t level)
}
-static void
+void
hash_m(gtfile gf, GTDataHash **m)
{
#warning Overall: check GT API return states!
@@ -527,7 +527,7 @@ hash_m(gtfile gf, GTDataHash **m)
GTDataHash_create(gf->hashAlg, concatBuf, len, m);
}
-static inline void
+void
hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len)
{
// r = hash(canonicalize(rec));
@@ -535,7 +535,7 @@ hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len)
}
-static void
+void
hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r,
uint8_t level)
{
diff --git a/runtime/librsgt.h b/runtime/librsgt.h
index d9d221ea..35ee96b5 100644
--- a/runtime/librsgt.h
+++ b/runtime/librsgt.h
@@ -120,6 +120,12 @@ struct rsgtstatefile {
given in block-sig record */
#define RSGTE_INVLHDR 8/* invalid file header */
#define RSGTE_EOF 9 /* specific EOF */
+#define RSGTE_MISS_REC_HASH 10 /* record hash missing when expected */
+#define RSGTE_MISS_TREE_HASH 11 /* tree hash missing when expected */
+#define RSGTE_INVLD_REC_HASH 12 /* invalid record hash (failed verification) */
+#define RSGTE_INVLD_TREE_HASH 13 /* invalid tree hash (failed verification) */
+#define RSGTE_INVLD_REC_HASHID 14 /* invalid record hash ID (failed verification) */
+#define RSGTE_INVLD_TREE_HASHID 15 /* invalid tree hash ID (failed verification) */
static inline uint16_t
@@ -180,6 +186,26 @@ hashAlgName(uint8_t hashID)
default:return "[unknown]";
}
}
+static inline enum GTHashAlgorithm
+hashID2Alg(uint8_t hashID)
+{
+ switch(hashID) {
+ case 0x00:
+ return GT_HASHALG_SHA1;
+ case 0x02:
+ return GT_HASHALG_RIPEMD160;
+ case 0x03:
+ return GT_HASHALG_SHA224;
+ case 0x01:
+ return GT_HASHALG_SHA256;
+ case 0x04:
+ return GT_HASHALG_SHA384;
+ case 0x05:
+ return GT_HASHALG_SHA512;
+ default:
+ return 0xff;
+ }
+}
static inline char *
sigTypeName(uint8_t sigID)
{
@@ -233,5 +259,12 @@ void rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose);
void rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose);
int rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, uint8_t *bHasRecHashes, uint8_t *bHasIntermedHashes);
int rsgt_chkFileHdr(FILE *fp, char *expect);
+gtfile rsgt_vrfyConstruct_gf(void);
+void rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes);
+int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t lenRec);
+/* TODO: replace these? */
+void hash_m(gtfile gf, GTDataHash **m);
+void hash_r(gtfile gf, GTDataHash **r, const unsigned char *rec, const size_t len);
+void hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level);
#endif /* #ifndef INCLUDED_LIBRSGT_H */
diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c
index 961e50c5..ca52cb93 100644
--- a/runtime/librsgt_read.c
+++ b/runtime/librsgt_read.c
@@ -277,6 +277,22 @@ rsgt_tlvrdIMPRINT(FILE *fp, imprint_t **imprint, uint16_t tlvlen)
done: return r;
}
+static int
+rsgt_tlvrdRecHash(FILE *fp, imprint_t **imp)
+{
+ int r;
+ uint16_t tlvtype, tlvlen;
+
+ if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done;
+printf("read tlvtype %4.4x\n", tlvtype);
+ if(tlvtype != 0x0900) {
+ r = RSGTE_MISS_REC_HASH;
+ goto done;
+ }
+ if((r = rsgt_tlvrdIMPRINT(fp, imp, tlvlen)) != 0) goto done;
+ r = 0;
+done: return r;
+}
/**;
* Read the next "object" from file. This usually is
@@ -528,3 +544,113 @@ rsgt_chkFileHdr(FILE *fp, char *expect)
done:
return r;
}
+
+gtfile
+rsgt_vrfyConstruct_gf(void)
+{
+ gtfile gf;
+ if((gf = calloc(1, sizeof(struct gtfile_s))) == NULL)
+ goto done;
+ gf->x_prev = NULL;
+
+done: return gf;
+}
+
+void
+rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes)
+{
+printf("bs->hashID %d\n", bs->hashID);
+ gf->hashAlg = hashID2Alg(bs->hashID);
+ gf->bKeepRecordHashes = bHasRecHashes;
+ gf->bKeepTreeHashes = bHasIntermedHashes;
+ free(gf->IV);
+ gf->IV = malloc(getIVLen(bs));
+ memcpy(gf->IV, bs->iv, getIVLen(bs));
+}
+
+static int
+rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash)
+{
+ int r = 0;
+ imprint_t *imp;
+
+ if(!gf->bKeepRecordHashes)
+ goto done;
+ if((r = rsgt_tlvrdRecHash(sigfp, &imp)) != 0)
+ goto done;
+ if(imp->hashID != hashIdentifier(gf->hashAlg)) {
+ r = RSGTE_INVLD_REC_HASHID;
+ goto done;
+ }
+printf("imp hash:");
+outputHexBlob(stdout, imp->data, hashOutputLengthOctets(imp->hashID), 1);
+printf("\nrec hash:");
+outputHexBlob(stdout, recHash->digest, hashOutputLengthOctets(imp->hashID), 1);
+printf("\n");
+ if(memcmp(imp->data, recHash->digest,
+ hashOutputLengthOctets(imp->hashID))) {
+ r = RSGTE_INVLD_REC_HASH;
+ goto done;
+ }
+printf("record hash is OK\n");
+ r = 0;
+done:
+ return r;
+}
+
+int
+rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec,
+ size_t len)
+{
+ int r = 0;
+ GTDataHash *x; /* current hash */
+ GTDataHash *m, *recHash, *t;
+ uint8_t j;
+
+printf("hasRecHash %d, verify: %s", gf->bKeepRecordHashes, rec);
+ hash_m(gf, &m);
+ hash_r(gf, &recHash, rec, len);
+ if(gf->bKeepRecordHashes) {
+ r = rsgt_vrfy_chkRecHash(gf, sigfp, recHash);
+ if(r != 0) goto done;
+ }
+ hash_node(gf, &x, m, recHash, 1); /* hash leaf */
+ /* persists x here if Merkle tree needs to be persisted! */
+ //if(gf->bKeepTreeHashes)
+ //tlvWriteHash(gf, 0x0901, x);
+ /* add x to the forest as new leaf, update roots list */
+ t = x;
+ for(j = 0 ; j < gf->nRoots ; ++j) {
+ if(gf->roots_valid[j] == 0) {
+ gf->roots_hash[j] = t;
+ gf->roots_valid[j] = 1;
+ t = NULL;
+ break;
+ } else if(t != NULL) {
+ /* hash interim node */
+ hash_node(gf, &t, gf->roots_hash[j], t, j+2);
+ gf->roots_valid[j] = 0;
+ GTDataHash_free(gf->roots_hash[j]);
+ // TODO: check if this is correct location (paper!)
+ //if(gf->bKeepTreeHashes)
+ //tlvWriteHash(gf, 0x0901, t);
+ }
+ }
+ if(t != NULL) {
+ /* new level, append "at the top" */
+ gf->roots_hash[gf->nRoots] = t;
+ gf->roots_valid[gf->nRoots] = 1;
+ ++gf->nRoots;
+ assert(gf->nRoots < MAX_ROOTS);
+ t = NULL;
+ }
+ gf->x_prev = x; /* single var may be sufficient */
+ ++gf->nRecords;
+
+ /* cleanup */
+ /* note: x is freed later as part of roots cleanup */
+ GTDataHash_free(m);
+ GTDataHash_free(recHash);
+done:
+ return r;
+}
diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c
index 021cd9f8..54a795a1 100644
--- a/runtime/lmsig_gt.c
+++ b/runtime/lmsig_gt.c
@@ -130,12 +130,20 @@ dbgprintf("DDDD: onFileOpen: %s\n", fn);
RETiRet;
}
+/* Note: we assume that the record is terminated by a \n.
+ * As of the GuardTime paper, \n is not part of the signed
+ * message, so we subtract one from the record size. This
+ * may cause issues with non-standard formats, but let's
+ * see how things evolve (the verifier will not work in
+ * any case when the records are not \n delimited...).
+ * rgerhards, 2013-03-17
+ */
static rsRetVal
OnRecordWrite(void *pF, uchar *rec, rs_size_t lenRec)
{
DEFiRet;
-dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec, rec);
- sigblkAddRecord(pF, rec, lenRec);
+dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec-1, rec);
+ sigblkAddRecord(pF, rec, lenRec-1);
RETiRet;
}
diff --git a/tools/rsgtutil.c b/tools/rsgtutil.c
index 3286163b..d94a63cd 100644
--- a/tools/rsgtutil.c
+++ b/tools/rsgtutil.c
@@ -34,7 +34,8 @@
typedef unsigned char uchar;
-static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS
+static enum { MD_DUMP, MD_DETECT_FILE_TYPE, MD_SHOW_SIGBLK_PARAMS,
+ MD_VERIFY
} mode = MD_DUMP;
static int verbose = 0;
@@ -143,6 +144,91 @@ detectFileType(char *name)
err: fprintf(stderr, "error %d processing file %s\n", r, name);
}
+static inline int
+doVerifyRec(FILE *logfp, FILE *sigfp, block_sig_t *bs, gtfile gf)
+{
+ int r;
+ size_t lenRec;
+ char rec[128*1024];
+
+ fgets(rec, sizeof(rec), logfp);
+ lenRec = strlen(rec);
+ if(rec[lenRec-1] == '\n')
+ --lenRec;
+
+ r = rsgt_vrfy_nextRec(bs, gf, sigfp, (unsigned char*)rec, lenRec);
+ return r;
+}
+
+/* note: here we need to have the LOG file name, not signature! */
+static void
+verify(char *name)
+{
+ FILE *logfp = NULL, *sigfp = NULL;
+ block_sig_t *bs;
+ gtfile gf;
+ uint8_t bHasRecHashes, bHasIntermedHashes;
+ uint8_t bInBlock;
+ int r = 0;
+ uint64_t nRecs;
+ char sigfname[4096];
+
+ if(!strcmp(name, "-")) {
+ fprintf(stderr, "verify mode cannot work on stdin\n");
+ goto err;
+ } else {
+ snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name);
+ sigfname[sizeof(sigfname)-1] = '\0';
+ if((logfp = fopen(name, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ if((sigfp = fopen(sigfname, "r")) == NULL) {
+ perror(name);
+ goto err;
+ }
+ }
+ if((r = rsgt_chkFileHdr(sigfp, "LOGSIG10")) != 0) goto err;
+
+ gf = rsgt_vrfyConstruct_gf();
+ if(gf == NULL) {
+ fprintf(stderr, "error initializing signature file structure\n");
+ goto err;
+ }
+
+ bInBlock = 0;
+
+ while(!feof(logfp)) {
+ if(bInBlock == 0) {
+ if((r = rsgt_getBlockParams(sigfp, 1, &bs, &bHasRecHashes,
+ &bHasIntermedHashes)) != 0)
+ goto err;
+ rsgt_vrfyBlkInit(gf, bs, bHasRecHashes,
+ bHasIntermedHashes);
+ nRecs = 0;
+ bInBlock = 1;
+ }
+ ++nRecs;
+ if((r = doVerifyRec(logfp, sigfp, bs, gf)) != 0)
+ goto err;
+ if(nRecs == bs->recCount) {
+ // verifyBLOCK_SIG(bs, gf);
+ bInBlock = 0;
+ }
+ }
+
+ fclose(logfp);
+ fclose(sigfp);
+ return;
+err:
+ if(logfp != NULL)
+ fclose(logfp);
+ if(sigfp != NULL)
+ fclose(sigfp);
+ if(r != RSGTE_EOF)
+ fprintf(stderr, "error %d processing file %s [%s]\n", r, name, sigfname);
+}
+
static void
processFile(char *name)
{
@@ -156,6 +242,9 @@ processFile(char *name)
case MD_SHOW_SIGBLK_PARAMS:
showSigblkParams(name);
break;
+ case MD_VERIFY:
+ verify(name);
+ break;
}
}
@@ -167,6 +256,7 @@ static struct option long_options[] =
{"version", no_argument, NULL, 'V'},
{"detect-file-type", no_argument, NULL, 'T'},
{"show-sigblock-params", no_argument, NULL, 'B'},
+ {"verify", no_argument, NULL, 't'}, /* 't' as in "test signatures" */
{NULL, 0, NULL, 0}
};
@@ -196,6 +286,9 @@ main(int argc, char *argv[])
case 'T':
mode = MD_DETECT_FILE_TYPE;
break;
+ case 't':
+ mode = MD_VERIFY;
+ break;
case '?':
break;
default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt);