diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2013-03-17 13:06:02 +0100 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2013-03-17 13:06:02 +0100 |
commit | d2467c38d42f590deecd807741324fc0e5522a8a (patch) | |
tree | c9d56cee1e0119ba9bd5f516a68bd7e1269712fe | |
parent | 44b4922825df794f678cd4ad18d940ff114b943f (diff) | |
download | rsyslog-d2467c38d42f590deecd807741324fc0e5522a8a.tar.gz rsyslog-d2467c38d42f590deecd807741324fc0e5522a8a.tar.bz2 rsyslog-d2467c38d42f590deecd807741324fc0e5522a8a.zip |
logsig: milestone/verfier: record hashes are verified
-rw-r--r-- | runtime/librsgt.c | 6 | ||||
-rw-r--r-- | runtime/librsgt.h | 33 | ||||
-rw-r--r-- | runtime/librsgt_read.c | 126 | ||||
-rw-r--r-- | runtime/lmsig_gt.c | 12 | ||||
-rw-r--r-- | tools/rsgtutil.c | 95 |
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); |