From 7e7266c9af883ce49b3516a5bd099d218e8e3fac Mon Sep 17 00:00:00 2001
From: Demi Marie Obenour <demi@invisiblethingslab.com>
Date: Sun, 6 Feb 2022 15:52:48 -0500
Subject: [PATCH] Require creation time to be unique and hashed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

According to RFC 4880 §5.2.3.4 the signature creation time MUST be a
hashed subpacket.  Enforce this requirement in RPM.  Also set the saved
flags to PGPDIG_SAVED_TIME | PGPDIG_SAVED_ID |
PGPDIG_SAVED_CREATION_TIME for v3 signatures, and do not overwrite an
already saved key ID with one taken from a v3 signature.
---
 rpmio/digest.h |  4 +++-
 rpmio/rpmpgp.c | 29 +++++++++++++++++++----------
 2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/rpmio/digest.h b/rpmio/digest.h
index 3b72a28..ec7f339 100644
--- a/rpmio/digest.h
+++ b/rpmio/digest.h
@@ -36,9 +36,11 @@ struct pgpDigParams_s {
     uint32_t hashlen;
     uint8_t signhash16[2];
     pgpKeyID_t signid;
-    uint8_t saved;
+    uint8_t saved;		/*!< Various flags.  `PGPDIG_SAVED_*` are never reset.
+				 * `PGPDIG_SIG_HAS_*` are reset for each signature. */
 #define	PGPDIG_SAVED_TIME	(1 << 0)
 #define	PGPDIG_SAVED_ID		(1 << 1)
+#define	PGPDIG_SIG_HAS_CREATION_TIME	(1 << 2)
 
     pgpDigAlg alg;
 };
diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
index f9e2658..0200900 100644
--- a/rpmio/rpmpgp.c
+++ b/rpmio/rpmpgp.c
@@ -473,16 +473,16 @@ static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype,
 	    for (i = 1; i < plen; i++)
 		pgpPrtVal(" ", pgpKeyServerPrefsTbl, p[i]);
 	    break;
-	case PGPSUBTYPE_SIG_CREATE_TIME:
+	case PGPSUBTYPE_SIG_CREATE_TIME:  /* signature creation time */
+	    if (plen-1 != sizeof(_digp->time))
+		break; /* other lengths not understood */
+	    if (_digp->saved & PGPDIG_SIG_HAS_CREATION_TIME)
+		return 1; /* duplicate timestamps not allowed */
 	    impl = *p;
-	    if (!(_digp->saved & PGPDIG_SAVED_TIME) &&
-		(sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE))
-	    {
-		if (plen-1 != sizeof(_digp->time))
-		    break;
-		_digp->saved |= PGPDIG_SAVED_TIME;
+	    if (!(_digp->saved & PGPDIG_SAVED_TIME))
 		_digp->time = pgpGrab(p+1, sizeof(_digp->time));
-	    }
+	    _digp->saved |= PGPDIG_SAVED_TIME | PGPDIG_SIG_HAS_CREATION_TIME;
+	    break;
 	case PGPSUBTYPE_SIG_EXPIRE_TIME:
 	case PGPSUBTYPE_KEY_EXPIRE_TIME:
 	    pgpPrtTime(" ", p+1, plen-1);
@@ -598,6 +598,9 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,
     unsigned int plen;
     int rc = 1;
 
+    /* Reset the saved flags */
+    _digp->saved &= PGPDIG_SAVED_TIME | PGPDIG_SAVED_ID;
+
     if (pgpVersion(h, hlen, &version))
 	return rc;
 
@@ -625,8 +628,11 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,
 	    _digp->hashlen = v->hashlen;
 	    _digp->sigtype = v->sigtype;
 	    _digp->hash = memcpy(xmalloc(v->hashlen), &v->sigtype, v->hashlen);
-	    _digp->time = pgpGrab(v->time, sizeof(v->time));
-	    memcpy(_digp->signid, v->signid, sizeof(_digp->signid));
+	    if (!(_digp->saved & PGPDIG_SAVED_TIME))
+		_digp->time = pgpGrab(v->time, sizeof(v->time));
+	    if (!(_digp->saved & PGPDIG_SAVED_ID))
+		memcpy(_digp->signid, v->signid, sizeof(_digp->signid));
+	    _digp->saved = PGPDIG_SAVED_TIME | PGPDIG_SIG_HAS_CREATION_TIME | PGPDIG_SAVED_ID;
 	    _digp->pubkey_algo = v->pubkey_algo;
 	    _digp->hash_algo = v->hash_algo;
 	    memcpy(_digp->signhash16, v->signhash16, sizeof(_digp->signhash16));
@@ -664,6 +670,9 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen,
 	    return 1;
 	p += plen;
 
+	if (!(_digp->saved & PGPDIG_SIG_HAS_CREATION_TIME))
+	    return 1; /* RFC 4880 §5.2.3.4 creation time MUST be hashed */
+
 	if (pgpGet(p, 2, hend, &plen))
 	    return 1;
 	p += 2;
-- 
1.8.3.1

