diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/librsgt.c | 9 | ||||
-rw-r--r-- | runtime/librsgt.h | 96 | ||||
-rw-r--r-- | runtime/librsgt_read.c | 231 | ||||
-rw-r--r-- | runtime/lmsig_gt.c | 3 |
4 files changed, 272 insertions, 67 deletions
diff --git a/runtime/librsgt.c b/runtime/librsgt.c index 20c8fd79..e24c3769 100644 --- a/runtime/librsgt.c +++ b/runtime/librsgt.c @@ -325,8 +325,11 @@ writeStateFile(gtfile gf) memcpy(sf.hdr, "GTSTAT10", 8); sf.hashID = hashIdentifier(gf->hashAlg); sf.lenHash = gf->x_prev->digest_length; - write(fd, &sf, sizeof(sf)); - write(fd, gf->x_prev->digest, gf->x_prev->digest_length); + /* if the write fails, we cannot do anything against that. We check + * the condition just to keep the compiler happy. + */ + if(write(fd, &sf, sizeof(sf))){}; + if(write(fd, gf->x_prev->digest, gf->x_prev->digest_length)){}; close(fd); done: return; } @@ -387,7 +390,7 @@ seedIV(gtfile gf) * will always work...). -- TODO -- rgerhards, 2013-03-06 */ if((fd = open("/dev/urandom", O_RDONLY)) > 0) { - read(fd, gf->IV, hashlen); + if(read(fd, gf->IV, hashlen)) {}; /* keep compiler happy */ close(fd); } } diff --git a/runtime/librsgt.h b/runtime/librsgt.h index 26eaf8ee..98384bb9 100644 --- a/runtime/librsgt.h +++ b/runtime/librsgt.h @@ -73,10 +73,39 @@ struct gtfile_s { int tlvIdx; /* current index into tlvBuf */ }; typedef struct gtfile_s *gtfile; - +typedef struct gterrctx_s gterrctx_t; typedef struct imprint_s imprint_t; typedef struct block_sig_s block_sig_t; +/* The following structure describes the "error context" to be used + * for verification and similiar reader functions. While verifying, + * we need some information (like filenames or block numbers) that + * is not readily available from the other objects (or not even known + * to librsgt). In order to provide meaningful error messages, this + * information must be passed in from the external callers. In order + * to centralize information (and make it more manageable), we use + * ths error context here, which contains everything needed to + * generate good error messages. Members of this structure are + * maintained both by library users (the callers) as well as + * the library itself. Who does what simply depends on who has + * the relevant information. + */ +struct gterrctx_s { + FILE *fp; /**< file for error messages */ + char *filename; + uint8_t verbose; + uint64_t recNumInFile; + uint64_t recNum; + uint64_t blkNum; + uint8_t treeLevel; + GTDataHash *computedHash; + GTDataHash *lefthash, *righthash; /* hashes to display if tree hash fails */ + imprint_t *fileHash; + int gtstate; /* status from last relevant GT.*() function call */ + char *errRec; + char *frstRecInBlk; /* This holds the first message seen inside the current block */ +}; + struct imprint_s { uint8_t hashID; int len; @@ -115,7 +144,7 @@ struct rsgtstatefile { #define RSGTE_INVLTYP 3 /* invalid TLV type record (unexcpected at this point) */ #define RSGTE_OOM 4 /* ran out of memory */ #define RSGTE_LEN 5 /* error related to length records */ -#define RSGTE_NO_BLKSIG 6/* block signature record is missing --> invalid block */ +// 6 may be reused! #define RSGTE_INVLD_RECCNT 7/* mismatch between actual records and records given in block-sig record */ #define RSGTE_INVLHDR 8/* invalid file header */ @@ -128,6 +157,57 @@ struct rsgtstatefile { #define RSGTE_INVLD_TREE_HASHID 15 /* invalid tree hash ID (failed verification) */ #define RSGTE_MISS_BLOCKSIG 16 /* block signature record missing when expected */ #define RSGTE_INVLD_TIMESTAMP 17 /* RFC3161 timestamp is invalid */ +#define RSGTE_TS_DERDECODE 18 /* error DER-Decoding a timestamp */ + +/* the following function maps RSGTE_* state to a string - must be updated + * whenever a new state is added. + * Note: it is thread-safe to call this function, as it returns a pointer + * into constant memory pool. + */ +static inline char * +RSGTE2String(int err) +{ + switch(err) { + case 0: + return "success"; + case RSGTE_IO: + return "i/o error"; + case RSGTE_FMT: + return "data format error"; + case RSGTE_INVLTYP: + return "invalid/unexpected tlv record type"; + case RSGTE_OOM: + return "out of memory"; + case RSGTE_LEN: + return "length record problem"; + case RSGTE_INVLD_RECCNT: + return "mismatch between actual record count and number in block signature record"; + case RSGTE_INVLHDR: + return "invalid file header"; + case RSGTE_EOF: + return "EOF"; + case RSGTE_MISS_REC_HASH: + return "record hash missing"; + case RSGTE_MISS_TREE_HASH: + return "tree hash missing"; + case RSGTE_INVLD_REC_HASH: + return "record hash mismatch"; + case RSGTE_INVLD_TREE_HASH: + return "tree hash mismatch"; + case RSGTE_INVLD_REC_HASHID: + return "invalid record hash ID"; + case RSGTE_INVLD_TREE_HASHID: + return "invalid tree hash ID"; + case RSGTE_MISS_BLOCKSIG: + return "missing block signature record"; + case RSGTE_INVLD_TIMESTAMP: + return "RFC3161 timestamp invalid"; + case RSGTE_TS_DERDECODE: + return "error DER-decoding RFC3161 timestamp"; + default: + return "unknown error"; + } +} static inline uint16_t @@ -263,11 +343,19 @@ int rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, uint8_t *bH 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); -int verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs); +int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, size_t lenRec, gterrctx_t *ectx); +int verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, gterrctx_t *ectx); +void rsgt_errctxInit(gterrctx_t *ectx); +void rsgt_errctxExit(gterrctx_t *ectx); +void rsgt_errctxSetErrRec(gterrctx_t *ectx, char *rec); +void rsgt_errctxFrstRecInBlk(gterrctx_t *ectx, char *rec); + /* 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); +extern char *rsgt_read_puburl; /**< url of publication server */ +extern uint8_t rsgt_read_showVerified; + #endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c index d756c336..e32ae454 100644 --- a/runtime/librsgt_read.c +++ b/runtime/librsgt_read.c @@ -48,6 +48,8 @@ typedef unsigned char uchar; #define MAXFNAME 1024 static int rsgt_read_debug = 0; +char *rsgt_read_puburl = "http://verify.guardtime.com/gt-controlpublications.bin"; +uint8_t rsgt_read_showVerified = 0; /* macro to obtain next char from file including error tracking */ #define NEXTC if((c = fgetc(fp)) == EOF) { \ @@ -58,6 +60,130 @@ static int rsgt_read_debug = 0; /* check return state of operation and abort, if non-OK */ #define CHKr(code) if((r = code) != 0) goto done + +/* if verbose==0, only the first and last two octets are shown, + * otherwise everything. + */ +static void +outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) +{ + unsigned i; + if(verbose || len <= 8) { + for(i = 0 ; i < len ; ++i) + fprintf(fp, "%2.2x", blob[i]); + } else { + fprintf(fp, "%2.2x%2.2x%2.2x[...]%2.2x%2.2x%2.2x", + blob[0], blob[1], blob[2], + blob[len-3], blob[len-2], blob[len-1]); + } +} + +static inline void +outputHash(FILE *fp, char *hdr, uint8_t *data, uint16_t len, uint8_t verbose) +{ + fprintf(fp, "%s", hdr); + outputHexBlob(fp, data, len, verbose); + fputc('\n', fp); +} + +void +rsgt_errctxInit(gterrctx_t *ectx) +{ + ectx->fp = NULL; + ectx->filename = NULL; + ectx->recNum = 0; + ectx->recNumInFile = 0; + ectx->blkNum = 0; + ectx->verbose = 0; + ectx->errRec = NULL; + ectx->frstRecInBlk = NULL; +} +void +rsgt_errctxExit(gterrctx_t *ectx) +{ + free(ectx->filename); + free(ectx->frstRecInBlk); +} + +/* note: we do not copy the record, so the caller MUST not destruct + * it before processing of the record is completed. To remove the + * current record without setting a new one, call this function + * with rec==NULL. + */ +void +rsgt_errctxSetErrRec(gterrctx_t *ectx, char *rec) +{ + ectx->errRec = strdup(rec); +} +/* This stores the block's first record. Here we copy the data, + * as the caller will usually not preserve it long enough. + */ +void +rsgt_errctxFrstRecInBlk(gterrctx_t *ectx, char *rec) +{ + free(ectx->frstRecInBlk); + ectx->frstRecInBlk = strdup(rec); +} + +static void +reportError(int errcode, gterrctx_t *ectx) +{ + if(ectx->fp != NULL) { + fprintf(ectx->fp, "%s[%llu:%llu:%llu]: error[%u]: %s\n", + ectx->filename, + (long long unsigned) ectx->blkNum, (long long unsigned) ectx->recNum, + (long long unsigned) ectx->recNumInFile, + errcode, RSGTE2String(errcode)); + if(ectx->frstRecInBlk != NULL) + fprintf(ectx->fp, "\tBlock Start Record.: '%s'\n", ectx->frstRecInBlk); + if(ectx->errRec != NULL) + fprintf(ectx->fp, "\tRecord in Question.: '%s'\n", ectx->errRec); + if(ectx->computedHash != NULL) { + outputHash(ectx->fp, "\tComputed Hash......: ", ectx->computedHash->digest, + ectx->computedHash->digest_length, ectx->verbose); + } + if(ectx->fileHash != NULL) { + outputHash(ectx->fp, "\tSignature File Hash: ", ectx->fileHash->data, + ectx->fileHash->len, ectx->verbose); + } + if(errcode == RSGTE_INVLD_TREE_HASH || + errcode == RSGTE_INVLD_TREE_HASHID) { + fprintf(ectx->fp, "\tTree Level.........: %d\n", (int) ectx->treeLevel); + outputHash(ectx->fp, "\tTree Left Hash.....: ", ectx->lefthash->digest, + ectx->lefthash->digest_length, ectx->verbose); + outputHash(ectx->fp, "\tTree Right Hash....: ", ectx->righthash->digest, + ectx->righthash->digest_length, ectx->verbose); + } + if(errcode == RSGTE_INVLD_TIMESTAMP || + errcode == RSGTE_TS_DERDECODE) { + fprintf(ectx->fp, "\tPublication Server.: %s\n", rsgt_read_puburl); + fprintf(ectx->fp, "\tGT Verify Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } + } +} + +/* obviously, this is not an error-reporting function. We still use + * ectx, as it has most information we need. + */ +static void +reportVerifySuccess(gterrctx_t *ectx) +{ + if(ectx->fp != NULL) { + fprintf(ectx->fp, "%s[%llu:%llu:%llu]: block signature successfully verified\n", + ectx->filename, + (long long unsigned) ectx->blkNum, (long long unsigned) ectx->recNum, + (long long unsigned) ectx->recNumInFile); + if(ectx->frstRecInBlk != NULL) + fprintf(ectx->fp, "\tBlock Start Record.: '%s'\n", ectx->frstRecInBlk); + if(ectx->errRec != NULL) + fprintf(ectx->fp, "\tBlock End Record...: '%s'\n", ectx->errRec); + fprintf(ectx->fp, "\tGT Verify Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } +} + + /** * Read a header from a binary file. * @param[in] fp file pointer for processing @@ -239,7 +365,6 @@ rsgt_tlvrdBLOCK_SIG(FILE *fp, block_sig_t **blocksig, uint16_t tlvlen) 2 + lenInt /* rec-count */ + 4 + bs->sig.der.len /* rfc-3161 */; if(sizeRead != tlvlen) { - printf("length record error!\n"); r = RSGTE_LEN; goto done; } @@ -285,7 +410,6 @@ rsgt_tlvrdRecHash(FILE *fp, imprint_t **imp) 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; @@ -302,7 +426,6 @@ rsgt_tlvrdTreeHash(FILE *fp, imprint_t **imp) uint16_t tlvtype, tlvlen; if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; -printf("read tlvtype %4.4x\n", tlvtype); if(tlvtype != 0x0901) { r = RSGTE_MISS_TREE_HASH; goto done; @@ -320,7 +443,6 @@ rsgt_tlvrdVrfyBlockSig(FILE *fp, block_sig_t **bs) uint16_t tlvtype, tlvlen; if((r = rsgt_tlvrdTL(fp, &tlvtype, &tlvlen)) != 0) goto done; -printf("read tlvtype %4.4x\n", tlvtype); if(tlvtype != 0x0902) { r = RSGTE_MISS_BLOCKSIG; goto done; @@ -372,24 +494,6 @@ rsgt_tlvrd(FILE *fp, uint16_t *tlvtype, uint16_t *tlvlen, void *obj) done: return r; } - -/* if verbose==0, only the first and last two octets are shown, - * otherwise everything. - */ -static void -outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) -{ - unsigned i; - if(verbose || len <= 6) { - for(i = 0 ; i < len ; ++i) - fprintf(fp, "%2.2x", blob[i]); - } else { - fprintf(fp, "%2.2x%2.2x[...]%2.2x%2.2x", - blob[0], blob[1], - blob[len-2], blob[len-1]); - } -} - /* return if a blob is all zero */ static inline int blobIsZero(uint8_t *blob, uint16_t len) @@ -412,14 +516,14 @@ rsgt_printIMPRINT(FILE *fp, char *name, imprint_t *imp, uint8_t verbose) static void rsgt_printREC_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) { - rsgt_printIMPRINT(fp, "[0x0900]Record hash................: ", + rsgt_printIMPRINT(fp, "[0x0900]Record hash: ", imp, verbose); } static void rsgt_printINT_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) { - rsgt_printIMPRINT(fp, "[0x0901]Intermediate aggregate hash: ", + rsgt_printIMPRINT(fp, "[0x0901]Tree hash..: ", imp, verbose); } @@ -608,20 +712,26 @@ rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHas } static int -rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash) +rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, GTDataHash *recHash, gterrctx_t *ectx) { int r = 0; imprint_t *imp; if((r = rsgt_tlvrdRecHash(sigfp, &imp)) != 0) + reportError(r, ectx); goto done; if(imp->hashID != hashIdentifier(gf->hashAlg)) { + reportError(r, ectx); r = RSGTE_INVLD_REC_HASHID; goto done; } if(memcmp(imp->data, recHash->digest, hashOutputLengthOctets(imp->hashID))) { r = RSGTE_INVLD_REC_HASH; + ectx->computedHash = recHash; + ectx->fileHash = imp; + reportError(r, ectx); + ectx->computedHash = NULL, ectx->fileHash = NULL; goto done; } r = 0; @@ -630,38 +740,37 @@ done: } static int -rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, GTDataHash *hash) +rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, GTDataHash *hash, gterrctx_t *ectx) { int r = 0; imprint_t *imp; - if((r = rsgt_tlvrdTreeHash(sigfp, &imp)) != 0) + if((r = rsgt_tlvrdTreeHash(sigfp, &imp)) != 0) { + reportError(r, ectx); goto done; + } if(imp->hashID != hashIdentifier(gf->hashAlg)) { + reportError(r, ectx); r = RSGTE_INVLD_TREE_HASHID; goto done; } -//printf("imp hash:"); -//outputHexBlob(stdout, imp->data, hashOutputLengthOctets(imp->hashID), 1); -//printf("\ntree hash:"); -//outputHexBlob(stdout, hash->digest, hashOutputLengthOctets(imp->hashID), 1); -//printf("\nlenBlkStrtHAsh %d\nblkstrt hash:", gf->lenBlkStrtHash); -//outputHexBlob(stdout, gf->blkStrtHash, gf->lenBlkStrtHash, 1); -//printf("\n"); if(memcmp(imp->data, hash->digest, hashOutputLengthOctets(imp->hashID))) { r = RSGTE_INVLD_TREE_HASH; + ectx->computedHash = hash; + ectx->fileHash = imp; + reportError(r, ectx); + ectx->computedHash = NULL, ectx->fileHash = NULL; goto done; } r = 0; -printf("Tree hash OK\n"); done: return r; } int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, - size_t len) + size_t len, gterrctx_t *ectx) { int r = 0; GTDataHash *x; /* current hash */ @@ -671,13 +780,15 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, hash_m(gf, &m); hash_r(gf, &recHash, rec, len); if(gf->bKeepRecordHashes) { - r = rsgt_vrfy_chkRecHash(gf, sigfp, recHash); + r = rsgt_vrfy_chkRecHash(gf, sigfp, recHash, ectx); 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) { - r = rsgt_vrfy_chkTreeHash(gf, sigfp, x); + ectx->treeLevel = 0; + ectx->lefthash = m; + ectx->righthash = recHash; + r = rsgt_vrfy_chkTreeHash(gf, sigfp, x, ectx); if(r != 0) goto done; } /* add x to the forest as new leaf, update roots list */ @@ -690,14 +801,16 @@ rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, unsigned char *rec, break; } else if(t != NULL) { /* hash interim node */ + ectx->treeLevel = j+1; + ectx->righthash = t; 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) { - r = rsgt_vrfy_chkTreeHash(gf, sigfp, t); - if(r != 0) goto done; + ectx->lefthash = gf->roots_hash[j]; + r = rsgt_vrfy_chkTreeHash(gf, sigfp, t, ectx); + if(r != 0) goto done; /* mem leak ok, we terminate! */ } + GTDataHash_free(gf->roots_hash[j]); } } if(t != NULL) { @@ -749,11 +862,6 @@ verifySigblkFinish(gtfile gf, GTDataHash **pRoot) free(gf->blkStrtHash); gf->blkStrtHash = NULL; *pRoot = root; - // We do not need the following as we take this from the block params - // (but I leave it in in order to aid getting to common code) - //gf->lenBlkStrtHash = gf->x_prev->digest_length; - //gf->blkStrtHash = malloc(gf->lenBlkStrtHash); - //memcpy(gf->blkStrtHash, gf->x_prev->digest, gf->lenBlkStrtHash); r = 0; done: gf->bInBlk = 0; @@ -764,7 +872,7 @@ done: * Merkle tree root for the current block. */ int -verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs) +verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, gterrctx_t *ectx) { int r; int gtstate; @@ -777,29 +885,34 @@ verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, uint64_t nRecs) goto done; if((r = rsgt_tlvrdVrfyBlockSig(sigfp, &file_bs)) != 0) goto done; -printf("got sig block, now doing checks \n"); - if(nRecs != bs->recCount) { + if(ectx->recNum != bs->recCount) { r = RSGTE_INVLD_RECCNT; goto done; } -printf("len DER timestamp: %d, data %p\n", (int) file_bs->sig.der.len, file_bs->sig.der.data); gtstate = GTTimestamp_DERDecode(file_bs->sig.der.data, file_bs->sig.der.len, ×tamp); -printf("result of GTTimestamp_DERDecode: %d\n", gtstate); + if(gtstate != GT_OK) { + r = RSGTE_TS_DERDECODE; + ectx->gtstate = gtstate; + goto done; + } + gtstate = GTHTTP_verifyTimestampHash(timestamp, root, NULL, - NULL, NULL, - "http://verify.guardtime.com/gt-controlpublications.bin", - 0, &vrfyInf); -printf("result of HTTP_vrfy: %d: %s\n", gtstate, GTHTTP_getErrorString(gtstate)); + NULL, NULL, rsgt_read_puburl, 0, &vrfyInf); if(! (gtstate == GT_OK && vrfyInf->verification_errors == GT_NO_FAILURES) ) { - r = RSGTE_INVLD_TIMESTAMP; goto done; + r = RSGTE_INVLD_TIMESTAMP; + ectx->gtstate = gtstate; + goto done; } -printf("root timestamp OK\n"); r = 0; + if(rsgt_read_showVerified) + reportVerifySuccess(ectx); done: + if(r != 0) + reportError(r, ectx); if(timestamp != NULL) GTTimestamp_free(timestamp); return r; diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c index 54a795a1..d61f3a86 100644 --- a/runtime/lmsig_gt.c +++ b/runtime/lmsig_gt.c @@ -118,9 +118,10 @@ SetCnfParam(void *pT, struct nvlst *lst) static rsRetVal -OnFileOpen(void *pT, uchar *fn, gtfile *pgf) +OnFileOpen(void *pT, uchar *fn, void *pGF) { lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + gtfile *pgf = (gtfile*) pGF; DEFiRet; dbgprintf("DDDD: onFileOpen: %s\n", fn); |