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 + -
显示快捷键?