pgprngpub.c

来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,518 行 · 第 1/4 页

C
2,518
字号
	(void)sig;
	pgpAssert(0);
	return 0;
#else
	pgpAssert(OBJISSIG(sig));
	pgpAssert(sig->g.mask & set->mask);
	(void)set;
	if (sig->s.by != NULL) {
		if (set->mask & sig->s.by->g.mask) {
			if (!(sig->s.by->g.flags & (RINGOBJF_TRUST)))
				ringMntValidateKey (set, sig->s.by);
			if (sig->s.by->k.trust & PGP_KEYTRUSTF_REVOKED)
				return 0;
			else
				return ringKeyCalcTrust (set, sig->s.by);
		}
		else
			return 0;
	}
	else
		return 0;
#endif
}


int
ringSigType(struct RingSet const *set, union RingObject const *sig)
{
	pgpAssert(OBJISSIG(sig));
	pgpAssert(sig->g.mask & set->mask);
	(void)set;
	return sig->s.type;
}

word32
ringSigTimestamp(struct RingSet const *set, union RingObject const *sig)
{
	pgpAssert(OBJISSIG(sig));
	pgpAssert(sig->g.mask & set->mask);
	(void)set;
	return sig->s.tstamp;
}


/** Filtering functions to get sets from sets **/

/* The generic one - according to the predicate */

int
ringSetFilter(struct RingSet const *src, struct RingSet *dest,
	     int (*predicate)(void *arg, struct RingIterator *iter,
		union RingObject *object, unsigned level),
	void *arg)
{
	struct RingIterator *iter;
	union RingObject *obj;
	unsigned level;
	int i;
	unsigned total = 0;

	if (!src || !dest)
		return 0;

	pgpAssert(!RINGSETISMUTABLE(src)); 	
pgpAssert(RINGSETISMUTABLE(dest));

	iter = ringIterCreate(src);
	if (!iter)
		return ringSetError(src)->error;
	level = 1;
	for (;;) {
		i = ringIterNextObject(iter, level);
		if (i > 0) {
			obj = ringIterCurrentObject(iter, level);
			i = predicate(arg, iter, obj, level);
			if (i < 0) {
				ringIterDestroy(iter);
				return i;
			}
			if (i) {
				/* Calculate total number of keys */
				total += (level == 1);
				/* ringSetAddObject(dest, obj) */
				obj->g.mask |= dest->mask;
				++level; /* Recurse! */
				ringIterRewind(iter, level);
			}
		} else {
			if (i < 0 || !--level)
				break;
		}
	}
	ringIterDestroy(iter);
	/* Return error or number of keys found */
	return (i < 0) ? i : (total < INT_MAX) ? total : INT_MAX;
}

/*
 * Return pointer to first instance of (s1,l1) in (s0,l0),
 * ignoring case. Uses a fairly simple-minded algorithm.
 * Search for the first char of s1 in s0, and when we have it,
 * scan for the rest.
 *
 * Is it worth mucking with Boyer-Moore or the like?
 */
static char const *
xmemimem(char const *s0, size_t l0, char const *s1, size_t l1)
{
	char c0, c1, c2;
	size_t l;

	/*
	* The trivial cases - this means that NULL inputs are very legal
	* if the corresponding lengths are zero.
	*/
	if (l0 < l1)
		return NULL;
	if (!l1)
		return s0;
	l0 -= l1;

	c1 = tolower((unsigned char)*s1);
	do {
		c0 = tolower((unsigned char)*s0);
		if (c0 == c1) {
			l = 0;
			do {
				if (++l == l1)
					return s0;
				c0 = tolower((unsigned char)s0[l]);
				c2 = tolower((unsigned char)s1[l]);
			} while (c0 == c2);
		}
		s0++;
	} while (l0--);
	return NULL;
}

struct KeySpec {
	char const *keyid, *name;
	size_t keyidlen, namelen;
	int use;
};

/*
 * Allowed formats for the keyspec are:
 * NULL, "", "*" - Match everything
 * "0x123c" - match everything with a keyID containing "123c"
 * "Name" - match everything with a name containing "name" (case-insensitive)
 * "0x123c:name" - match everything satisfying both requirements
 *
 * This returns pointers to "keyidspec" and "uidspec", the portions of the
 * input keyspec string which should match the keyID and userID portions,
 * or NULL if there are no such portions (which means "always mauch").
 *
 * This function cannot have any errors. At worst, the entire string
 * is taken to be a uid match. Some corner cases:
 *
 * 0	-> No keyidspec, namespec of "0"
 * 0x	-> Empty keyidspec, no namespec
 * 0x:	-> Empty keyidspec, empty namespec
 *	0x12345678:	->	Keyidspec of "12345678", empty namespec
 *	0x12345678:foo	->	Keyidspec of "12345678", namespec of "foo"
 *	0x12345678;foo	->	No keyidspec, namespec of "0x12345678;foo"
 *	0x12345678		->	Keyidspec of "12345678", no namespec
 *
 * Keyid's are now allowed to be up to 16 characters long. If they are
 * 8 chars or less, they are only matched against the low 32 bits of
 * the key's keyid. If greater, they are matched against the full 64
 * bits of keyid.
 */
static void
keyspecSplit(char const *string, int use, struct KeySpec *spec)
{
		unsigned i;

		spec->use = use;

		spec->keyidlen = spec->namelen = 0;	/* Match anything */

		/* NULL is nothing */
		if (!string)
			return;

		/* Does it look like it might start with a keyID spec? */
		if (string[0] == '0' && (string[1] == 'x' || string[1] == 'X')) {
				i = 2;
				/* Accept no more than 16 hex digits */
				while (isxdigit(string[i]) && ++i != 2+16)
					;
				/* Then check for proper termination: NULL or : */
				if (!string[i]) {
					spec->keyid = string+2;
					spec->keyidlen = i-2;
					return;
				} else if (string[i] == ':') {
					spec->keyid = string+2;
					spec->keyidlen = i-2;
			string += i+1;
		} /* Otherwise forget it, it's all namespec */
	}

	/* If not "*", it's a pattern */
	if (string[0] != '*' || string[1]) {
		spec->name = string;
		spec->namelen = strlen(string);
	}
}

/*
 * Return true if string "arg" (terminated by null or ':') appears in
 * the hex expansion of the given keyID. Case-insensitive.
 */
static int
matchKeyID(byte const keyID[8], char const *pat, size_t len)
{
	char buf[16];
	char const hex[16] = {
		'0', '1', '2', '3', '4', '5', '6', '7',
		'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
	};

	buf[ 8] = hex[ keyID[4] >> 4 ];
	buf[ 9] = hex[ keyID[4] & 15 ];
	buf[10] = hex[ keyID[5] >> 4 ];
	buf[11] = hex[ keyID[5] & 15 ];
	buf[12] = hex[ keyID[6] >> 4 ];
	buf[13] = hex[ keyID[6] & 15 ];
	buf[14] = hex[ keyID[7] >> 4 ];
	buf[15] = hex[ keyID[7] & 15 ];

	if (len <= 8) {
		return xmemimem(buf+8, 8, pat, len) != NULL;
	}

	/* Here if input keyID was > 8 chars, we look at full 64 bit keyid */
	buf[0] = hex[ keyID[0] >> 4 ];
	buf[1] = hex[ keyID[0] & 15 ];
	buf[2] = hex[ keyID[1] >> 4 ];
	buf[3] = hex[ keyID[1] & 15 ];
	buf[4] = hex[ keyID[2] >> 4 ];
	buf[5] = hex[ keyID[2] & 15 ];
	buf[6] = hex[ keyID[3] >> 4 ];
	buf[7] = hex[ keyID[3] & 15 ];

	return xmemimem(buf, 16, pat, len) != NULL;
}

static int
predicateFilterName(void *arg, struct RingIterator *iter,
	union RingObject *obj, unsigned level)
{
	struct KeySpec const *spec;
	struct RingSet const *set;
	byte id8[8];
	char const *nam;
	size_t len;
	int i;


	if (level > 1)
		return 1;	/* All children included if top level is */

	pgpAssert(OBJISKEY(obj));

	spec = (struct KeySpec const *)arg;
	set = ringIterSet(iter);

	/* Check for usage */
	if (spec->use && (ringKeyUse(set,obj)&spec->use) != spec->use)
		return 0;	/* Doesn't have required usage */

	if (spec->keyidlen) {
		ringKeyID8(set, obj, NULL, id8);
		if (!matchKeyID(id8, spec->keyid, spec->keyidlen)) {
			union RingObject *subkey = ringKeySubkey(set, obj);
			if (!subkey)
				return 0;
			ringKeyID8(set, subkey, NULL, id8);
			if (!matchKeyID(id8, spec->keyid, spec->keyidlen))
				return 0;
			}
		}

	/*
	* This isn't quite consistent, because it'll accept a key
	* with *no* names if the name specification is empty.
	*/
	if (spec->namelen == 0)
		return 1;	/* Match! */
	/*
	* Search names for a matching name. If *one* is found,
	* the entire key, including all names, is taken
	*/
	while ((i = ringIterNextObject(iter, 2)) > 0) {
		obj = ringIterCurrentObject(iter, 2);
		if (ringObjectType(obj) != RINGTYPE_NAME)
			continue;
		nam = ringNameName(set, obj, &len);
		if (!nam)
			return ringSetError(set)->error;
		if (xmemimem(nam, len, spec->name, spec->namelen))
			return 1;	 /* Match, take it! */
	}
	return i;	 /* No match or error */
}

/*
 * Perform filtering based on a keyspec.
 * Use is a PGP_PKUSE value to specify the purpose of the key.
 * Pass 0 to match all keys. PGP_PKUSE_SIGN_ENCRYPT gets only ones which
 * can do both things.
 */
int
ringSetFilterSpec(struct RingSet const *src, struct RingSet *dest,
	char const *string, int use)
{
	struct KeySpec spec;

	keyspecSplit(string, use, &spec);
	return ringSetFilter(src, dest, predicateFilterName, (void *)&spec);
}


/*
 * Find the most recent secret key matching a keyspec.
 * Limit keys to those which have the specified use. Pass 0 to match
 * all uses. If tstamp is nonzero, also checks for expiration of keys.
 */
union RingObject *
ringLatestSecret(struct RingSet const *set, char const *string,
		word32 tstamp, int use)
{
	struct KeySpec spec;
	struct RingIterator *iter;
	union RingObject *obj, *best = NULL;
	word32 objtime, besttime = 0;
	word32 exptime;
	int i;

	if (!set)
		return NULL;

	iter = ringIterCreate(set);
	if (!iter)
		return NULL;	/* How to distinguish from no luck? */
	keyspecSplit(string, use, &spec);

	while (ringIterNextObject(iter, 1) > 0) {
		obj = ringIterCurrentObject(iter, 1);
		pgpAssert(obj);
		if (!ringKeyIsSec(set, obj))
			continue;
		if (ringKeyRevoked(set, obj))
			continue;
		i = predicateFilterName((void *)&spec, iter, obj, 1);
		if (!i)
			continue;
		if (i < 0) {
			ringObjectRelease(best);
			best = NULL;
			break;
		}

		objtime = ringKeyCreation(set, obj);
		exptime = ringKeyExpiration(set, obj);
		if (besttime <= objtime && (!tstamp || !exptime ||
					tstamp <= exptime)) {
			ringObjectRelease(best);	/* OK if best = NULL */
			best = obj;
			ringObjectHold(best);
			besttime = objtime;
		}
	}
	ringIterDestroy(iter);
	return best;
}


/*
 * Return key self-sig subpacket information. Searches all sigs below
 * the key for a self sig, finds most recent one with desired info.
 * nth is 0 to find first matching packet, 1 for second, etc.
 * The rule is that if we find two different sigs which both have
 * packets of the matching type, we take the more recent sig. It is
 * assumed to supercede the earlier one, even if the earlier had more
 * packets of that type.
 *
 * key			key to use
 * set			set containing key
 * subpacktype	subpacket type to search for
 * nth			nth matching subpacket to find
 * *plen		return length of data
 * *pcritical	return criticality field of subpacket
 * *phashed		return whether subpacket was in hashed region
 * *pcreation	return creation time of matching signature
 * *pmatches	return number of matches of this subpack type
 * *error		 return error code
 *
 * Function returns pointer to the data, of length *plen, or NULL with *error
 * set for error code. If matching packet is not found, return NULL
 * with *error = 0.
 */
byte const *
ringKeyFindSubpacket (RingObject *obj, struct RingSet *set,
	int subpacktype, unsigned nth,
	size_t *plen, int *pcritical, int *phashed, word32 *pcreation,
	unsigned *pmatches, int *error)
{
	RingObject		*sig, *bestsig;
	RingIterator	*iter;
	word32			bestcreation;
	int				bestmatches;
	byte const		*p, *bestp;
	size_t			bestlen;
	int				bestcritical;
	int				besthashed;

	pgpAssert(OBJISKEY(obj));
	pgpAssert (obj->g.mask & set->mask);
	
	bestlen = bestcritical = besthashed = bestmatches = bestcreation = 0;
	sig = NULL;
	bestp = NULL;
	bestsig = NULL;

	if (error)
		*error = 0;
	iter = ringIterCreate (set);
	if (!iter) {
		if (error)
			*error = ringSetError(set)->error;
		return NULL;
	}
	ringIterSeekTo (iter, obj);
	while (TRUE) {
		int		level;
		word32	creation;
		int		matches;
		size_t	len;

		if ((level = ringIterNextObjectAnywhere (iter)) <= 0)
			break;
		sig = ringIterCurrentObject (iter, level);
		/*
		* Abort when we come to a subkey or another key. Sigs on subkeys
		* shouldn't count, I don't think.
		*/
		if (OBJISKEY(sig))
			break;
		if (!OBJISSIG(sig))
			continue;
		/* Only count self-sigs that have been validated */
		if (ringSigMaker (set, sig, set) != obj)
			continue;
		if ((ringSigType (set, sig) & 0xf0) != PGP_SIGTYPE_KEY_GENERIC)
			continue;
		if (!ringSigChecked (set, sig) || ringSigRevoked (set, sig))
			continue;
		/* Here we have a self signature */
		p = (byte const *)ringFetchObject(set, sig, &len);
		if (!p) {
			if (error)
				*error = ringSetError(set)->error;
			return NULL;
		}
		p = ringSigFindSubpacket (p, subpacktype, 0, NULL, NULL,
			NULL, &creation, (unsigned int *) &matches);
		if (p) {
			if (creation > bestcreation) {
				bestcreation = creation;
				bestmatches = matches;
				if (bestsig)
					ringObjectRelease (bestsig);
				bestsig = sig;
				ringObjectHold (bestsig);
			}
		}
	}
	ringIterDestroy (iter);
	if (bestsig) {
		/* This had the most recent sig with the type we need */
		p = (byte const *)ringFetchObject(set, bestsig, &bestlen);
		pgpAssert (p);
		bestp = ringSigFindSubpacket (p, subpacktype, nth, &bestlen,
			&bestcritical, &besthashed, NULL, NULL);
		ringObjectRelease (bestsig);
	}
	if (plen)
		*plen = bestlen;
	if (pcritical)
		*pcritical = bestcritical;
	if (phashed)
		*phashed = besthashed;
	if (pcreation)
		*pcreation = bestcreation;
	if (pmatches)
		*pmatches = bestmatches;
	return bestp;
}


/*
 * Find a key recovery key for the given key, if one exists. nth tells
 * which one to find. *pkeys is set to the number of key recovery keys,
 * *pclass is set to the class byte associated with the recovery key.
 */
union RingObject *
ringKeyRecoveryKey (union RingObject *obj, struct RingSet *set, unsigned nth,
					byte *pclass, unsigned *pkeys, int *error)
{
	RingObject	*rkey;			/* Message recovery key */
	byte const	*krpdata;	 	/* Pointer to key recovery data */
	size_t			krdatalen;		/* Length of krdata */
	int				critical;		 /* True if recovery field was critical */
	unsigned		matches;		/* Number of mrk's found */
	byte			fingerp[20];	/* Fingerprint of mrk */
	byte			krdata[22];		/* Copy of key recovery data packet */

	pgpAssert(OBJISKEY(obj));
	pgpAssert (obj->g.mask & set->mask);
	pgpAssert (error);

	*error = 0;
	
	krpdata = ringKeyFindSubpacket (obj, set, SIGSUB_KEY_RECOVERY_KEY, nth,
		&krdatalen, &critical, NULL, NULL, &matches, error);
	if (!krpdata) {
		return NULL;
	}
	if (pkeys)
		*pkeys = matches;
		/*
	* krdata is 1 byte of class, 1 of pkalg, 20 bytes of fingerprint.
	* Last 8 of 20 are keyid. Make a copy because data is volatile when
	* we do other operations. Return class info even if we don't have the
	* MRK, it will help caller decide how serious this is.
		*/
	pgpAssert (krdatalen >= sizeof(krdata));
	pgpCopyMemory (krpdata, krdata, sizeof(krdata));
	if (pclass) {
		*pclass = krdata[0];
	}

	/* Do we have MRK? */
	rkey = ringKeyById8 (set, krdata[1], krdata+2+20-8);
	if (!rkey) {
		/* Have a problem - we don't have the KR key */
		*error = PGPERR_NO_RECOVERYKEY;
		return NULL;
	}
	ringKeyFingerprint20 (set, rkey, fingerp);
	if (memcmp (fingerp, krdata+2, 20) != 0) {
		/* Have a bogus key that matches in keyid but wrong fingerprint */
		*error = PGPERR_NO_RECOVERYKEY;		/* XXX different error? */
		return NULL;
	}
	/* Else success */
	return rkey;
}
	

/* Check obj for consistency, make sure valid mask bits are in
 * parent mask
 */
void
ringObjCheck (union RingObject *obj, ringmask validmask,
			ringmask parentmask)
{
	pgpAssert (!((obj->g.mask&validmask)&~parentmask));
	if (OBJISSIG(obj)) {
	/* sig should point to top-level key */
	pgpAssert (OBJISTOPKEY(obj->s.by));
	}
	if (OBJISBOT(obj))
		return;
	for (obj=obj->g.down; obj; obj=obj->g.next) {
		ringObjCheck (obj, validmask, parentmask & obj->g.mask);
	}
}

/* Perform a consistency check on ring pool data structures */
void
ringPoolConsistent (struct RingPool *pool, int *pnsets, int *pnfiles)
{
	ringmask allocmask;
	ringmask filemask = pool->filemask;
	ringmask mask;
	struct RingSet *set;
	union RingObject *obj;

	allocmask = (ringmask)0;
	for (set = pool->sets; set; set = set->next)
		allocmask |= set->mask;

	pgpAssert (!(allocmask & filemask));
	mask = allocmask | filemask;

	for (obj=pool->keys; obj; obj=obj->g.next) {
		ringObjCheck (obj, mask, mask);
	}
	if (pnsets) {
		int nsets = 0;
		while (allocmask) {
			++nsets;
			allocmask &= allocmask-1;
		}
		*pnsets = nsets;
	}
	if (pnfiles) {
		int nfiles = 0;
		while (filemask) {
			++nfiles;
			filemask &= filemask-1;
		}
		*pnfiles = nfiles;
	}
}


/*
 * Local Variables:
 * tab-width: 4
 * End:
 * vi: ts=4 sw=4
 * vim: si
 */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?