📄 pgptrustprop.c
字号:
* or you set the confidence level on the new userid. But this does not
* happen because such userids are not included in the confident set.
*
* A worse problem is that Snidely could get Nell's userid on his key, get
* some low-confidence signer to sign Nell, and get an even lower conf-
* idence signer to sign his own. Then we would use Nell's confidence
* on Snidely's key, and if there were also a good signer on Snidely's userid,
* which we hadn't run into yet, we would again count Snidely's signatures
* too highly.
*
* So this suggests a safer heuristic which is to take either the lowest
* confidence, or else perhaps to downgrade the confidence by the
* validities. However doing this if all the validities are accurate will
* end up squaring them. Maybe we could just downgrade by the relative
* validities, best to worst...
*
* (For now we have an #if below to always use the lowest confidence)
*
* An idea suggested by Mark is to base the confidence on the particular
* userid which is the next step in the path. However this complicates the
* union operation.
* Either a path no longer has a well defined confidence, preventing
* Maurer's algorithm from working, or else we have
* to define a path as being from userid to userid, and if a key has two
* userids (different email addresses, say, home and work) we would end up
* counting its contribution to trust twice. (This is essentially because
* the assumption of independence fails spectacularly in this case!)
*
* Keep in mind that despite all these difficulties, if there is only one
* userid then there is no problem! We just use the confidence on that userid.
*/
static double
pathKeyConfidence (PGPKeyDBObj *key)
{
PGPKeyDBObj * userid;
PGPKeyInfo * kinfo;
PGPUserIDInfo * uinfo;
double minconfidence = 1.0,
confidence,
errorchance,
useridconfidence,
keyconfidence,
maxvalidconfidence = 0.;
PGPUInt16 maxvalidity = 0;
PGPBoolean allvalid = TRUE; /* True if all userids have validity */
PGPBoolean validuserid = FALSE;/* True if at least one valid userid */
kinfo = pgpKeyToKeyInfo( key );
for (userid = key->down; IsntNull(userid); userid = userid->next) {
if( !pgpKeyDBObjIsReal( userid ) )
continue;
if( !OBJISUSERID(userid) )
continue;
uinfo = pgpUserIDToUserIDInfo( userid );
if( uinfo->confidence == PGP_NEWTRUST_UNDEFINED )
continue;
validuserid = TRUE;
errorchance = pgpTrustToDouble (
pgpTrustToIntern(uinfo->confidence));
confidence = 1. - 1. / errorchance;
if( confidence < minconfidence )
minconfidence = confidence;
if( uinfo->valid == 0 ) {
allvalid = 0;
} else if( uinfo->valid > maxvalidity ||
(uinfo->valid == maxvalidity &&
confidence < maxvalidconfidence) ) {
maxvalidity = uinfo->valid;
maxvalidconfidence = confidence;
}
}
/* If called with no valid userid, minconfidence is 0 */
if( !validuserid )
minconfidence = 0.;
/*
* If all userids have known validity, use the most valid userid's
* confidence. Else choose the lowest established confidence of
* any userid.
*/
#if 0
/* Too risky to use this heuristic, just take minimum confidence */
if( allvalid )
useridconfidence = maxvalidconfidence;
else
#endif
useridconfidence = minconfidence;
keyconfidence = 0.;
if( kinfo->signedTrust != 0 ) {
errorchance = pgpTrustToDouble (
pgpTrustToIntern(kinfo->signedTrust));
keyconfidence = 1. - 1. / errorchance;
}
return pgpMax (keyconfidence, useridconfidence);
}
/* Prototype */
static PathList **
sFindPathsKey (PGPKeyDBObj *start, PathList **ppath,
Path **predh, Path **predt, unsigned depth,
unsigned maxdepth, PGPUInt32 timenow, PGPKeyDB *db, PGPKeyDB *altdb);
/* Helper routines for sFindPathsUserID */
/* Return the newest cryptograhpically-good signature by the given key on
* the given userid. This does not check for expirations or regexps.
*/
static PGPKeyDBObj *
sNewestGoodSig (PGPKeyDBObj *userid, PGPKeyDBObj *key)
{
PGPKeyDBObj *bestsig = NULL,
*sig;
PGPSigInfo *sinfo;
PGPSigInfo *bestsiginfo = NULL;
pgpAssert (OBJISUSERID(userid));
pgpAssert (OBJISKEY(key));
for (sig = userid->down; IsntNull(sig); sig = sig->next) {
if( !pgpKeyDBObjIsReal( sig ) )
continue;
if( OBJISSIG(sig) ) {
sinfo = pgpSigToSigInfo( sig );
if ((sinfo->trust & PGP_SIGTRUSTF_CHECKED) && sinfo->by == key ) {
/* Good sig by key, see if it is newer than bestsig */
if( IsNull(bestsig)
|| bestsiginfo->tstamp < sinfo->tstamp ) {
bestsig = sig;
bestsiginfo = sinfo;
}
}
}
}
return bestsig;
}
/*
* Return true if there is a signature by the key on an earlier sibling
* of this userid. This alerts us to a condition which could cause duplicate
* paths, which will waste resources. key and localkey will be distinct
* in the case where signer is in an alternate keydb. In that case localkey
* is the key in the sig's db and key is the key in the alternate db.
* Otherwise they are the same.
*/
static int
sEarlierValidSig (PGPKeyDBObj *userid, PGPKeyDBObj *key,
PGPKeyDBObj *localkey, PGPUInt32 timenow)
{
PGPKeyDBObj *parent,
*sibuserid,
*sig;
PGPSigInfo *sinfo;
pgpAssert (OBJISUSERID(userid));
pgpAssert (OBJISKEY(key));
pgpAssert (OBJISKEY(localkey));
parent = userid->up;
pgpAssert (OBJISKEY(parent));
for (sibuserid=parent->down; sibuserid!=userid;
sibuserid=sibuserid->next) {
if( !pgpKeyDBObjIsReal( sibuserid ) )
continue;
if( !OBJISUSERID(sibuserid) )
continue;
for (sig=sibuserid->down; IsntNull(sig); sig=sig->next) {
if( !pgpKeyDBObjIsReal( sig ) )
continue;
if( OBJISSIG(sig) ) {
sinfo = pgpSigToSigInfo( sig );
if( (sinfo->trust & PGP_SIGTRUSTF_CHECKED)
&& !(sinfo->trust & PGP_SIGTRUSTF_REVOKEDBYCRL)
&& sinfo->by == localkey
&& mntSigOKWithRegexpOnSigner( sig, key )
&& mntSigIsValidTime(timenow, sinfo->tstamp,
sinfo->sigvalidity) ) {
/* Have an earlier signature we are looking for */
return TRUE;
}
}
}
}
return FALSE;
}
/*
* See if the specified key is in the special "pairing" relationship
* with the specified userid. We are called with key signing userid.
* The main point is whether key has a userid which matches. If so,
* we return that userid. It is also necessary that userid be
* self-signed, so we will check for that here. The signing key must
* be valid as well.
*/
static PGPKeyDBObj *
sPairedUserID (PGPKeyDBObj *userid, PGPKeyDBObj *key)
{
PGPKeyDBObj *keyuserid; /* Userids on key */
PGPKeyDBObj *useridkey; /* Key parent of userid */
PGPKeyDBObj *useridsig; /* Sigs on userid */
PGPByte const *smatchuserid; /* Actual userid of *userid */
PGPByte const *suserid; /* Actual userid of *keyuserid */
PGPSize lmatchuserid; /* Length of smatchuserid */
PGPSize luserid; /* Length of suserid */
PGPKeyInfo *kinfo;
PGPSigInfo *uidsiginfo;
pgpAssert (OBJISKEY(key));
pgpAssert (OBJISUSERID(userid));
kinfo = pgpKeyToKeyInfo( key );
/* Check for key validity */
if( kinfo->trust & (PGP_KEYTRUSTF_REVOKED | PGP_KEYTRUSTF_EXPIRED) )
return NULL;
/* Check that userid is self-signed */
useridkey = userid->up;
pgpAssert (OBJISKEY(useridkey));
for (useridsig = userid->down; IsntNull(useridsig);
useridsig = useridsig->next) {
if( !pgpKeyDBObjIsReal( useridsig ) )
continue;
if( !OBJISSIG(useridsig) )
continue;
uidsiginfo = pgpSigToSigInfo( useridsig );
/* XXX Should check expiration */
if( (uidsiginfo->trust & PGP_SIGTRUSTF_CHECKED)
&& !(uidsiginfo->trust & PGP_SIGTRUSTF_REVOKEDBYCRL)
&& (uidsiginfo->type & 0xF0) == PGP_SIGTYPE_KEY_GENERIC ) {
break; /* Exit with useridsig nonnull on success */
}
}
if( IsNull(useridsig) ) /* NULL useridsig means no self sig */
return NULL;
/* Get userid string of userid */
smatchuserid = pgpFetchObject( userid, &lmatchuserid );
/* See if any userids on keyuserid match */
for (keyuserid = key->down; IsntNull(keyuserid);
keyuserid = keyuserid->next) {
if( !pgpKeyDBObjIsReal( keyuserid ) )
continue;
if( !OBJISUSERID(keyuserid) )
continue;
suserid = pgpFetchObject( keyuserid, &luserid );
if( luserid == lmatchuserid &&
pgpMemoryEqual( suserid, smatchuserid, luserid ) ) {
/* Have a matching userid! */
return keyuserid;
}
}
return NULL;
}
/*
* Find all the paths from the starting userid back to an axiomatically
* trusted key.
* pred holds the path which leads to the starting key from some target.
* lastseg points at the last segment of the path, which already has
* the key this userid is on as the dest. We just have to fill it in
* and recurse, or check for buckstop keys.
*
* We reject paths which are to keys which have
* already been processed as signing this key. This can happen
* if there are two userids on a key, both signed by another key. Otherwise
* these would be treated as two different paths, but they
* end up being identical. The Maurer algorithm correctly accounts for this,
* but it is a waste. What we do is, for each signature, we check
* all our earlier userid siblings and see if they have a signature from the
* same key. If so we skip this si
*
* Returns tail pointer for updated PathList
*
* userid starting userid object for search backwards through web of trust
* ppath tail pointer of PathList to add our new paths to
* predh pointer to the (head of the) Path we will add to
* predt tail pointer to the Path we will add to (&newseg->tail)
* depth length of path so far, counting newseg (e. 1 at outer level)
* maxdepth maximum length of paths we allow
* timenow current timestamp
* db PGPKeyDB to use for memory allocations
* skipsigs list of signatures, chained by next, not to follow key of
*
*/
static PathList **
sFindPathsUserID (PGPKeyDBObj *userid, PathList **ppath,
Path **predh, Path **predt, unsigned depth,
unsigned maxdepth, PGPUInt32 timenow, PGPKeyDB *db, PGPKeyDB *altdb,
PGPKeyDBObj *skipsigs)
{
PGPKeyDBObj *sig,
*sigkey,
*localsigkey,
*paireduserid,
*ssig;
Path *newseg;
Path **newpredt;
double confidence;
PGPSigInfo *sinfo;
PGPSigInfo *ssinfo;
PGPKeyInfo *sigkeyinfo;
int tryalt;
for( sig = userid->down; sig; sig = sig->next )
{
if( !pgpKeyDBObjIsReal( sig ) )
continue;
if( !OBJISSIG(sig) )
continue;
sinfo = pgpSigToSigInfo( sig );
/* Skip if invalid sig */
if( !(sinfo->trust & PGP_SIGTRUSTF_CHECKED)
|| (sinfo->trust & PGP_SIGTRUSTF_REVOKEDBYCRL)
|| !mntSigIsValidTime(timenow, sinfo->tstamp, sinfo->sigvalidity)
|| (sinfo->type & 0xF0) != PGP_SIGTYPE_KEY_GENERIC )
continue;
/* Try tracing sigs back through altdb too */
for( tryalt = 0;
tryalt <= (IsntNull(altdb) && (PGPPeekKeyDBObjKeyDB(sig)!=altdb));
++tryalt )
{
if( tryalt == 0 )
{
sigkey = sinfo->by;
localsigkey = sigkey;
} else {
PGPKeyID keyID;
sigkey = NULL;
if( IsntPGPError( pgpGetKeyIDOfCertifier( sig, &keyID) ) )
pgpGetKeyByKeyID(altdb, &keyID, TRUE, FALSE, &sigkey );
if( IsNull( sigkey ) )
continue;
localsigkey = sinfo->by;
}
/* Skip if signing key not trusted */
if( !pgpKeyDBObjIsReal( sigkey ) )
continue;
if ( !sKeyHasConfidence (sigkey) )
continue;
if( !mntSigOKWithRegexpOnSigner( sig, sigkey ) )
continue;
/* Skip if there is a newer valid sig by the same key */
if( sNewestGoodSig (userid, localsigkey) != sig )
continue;
/* Skip if we have already done this key on an earlier userid*/
/* (I.e. sigkey signed more than one userid on this key) */
if( depth>1 && sEarlierValidSig (userid, sigkey, localsigkey,
timenow) )
continue;
/*
* Skip if signing key is in skipsigs list. This is for the
* "paired keys" feature.
*/
for (ssig = skipsigs; ssig; ssig = ssig->next) {
if( !pgpKeyDBObjIsReal( ssig ) )
continue;
ssinfo = pgpSigToSigInfo( ssig );
if( OBJISSIG(ssig) ) {
if( localsigkey == ssinfo->by )
{
/* Exit from loop with ssig nonnull on match */
break;
}
}
}
if( IsntNull(ssig) )
{
/* Non-null ssig means it was on skip list */
continue;
}
/* OK, investigate this signer further */
sigkeyinfo = pgpKeyToKeyInfo( sigkey );
/* Don't count revoked or expired keys (new test!) */
if( sigkeyinfo->trust & (PGP_KEYTRUSTF_REVOKED |
PGP_KEYTRUSTF_EXPIRED) )
continue;
if( (sigkeyinfo->trust & PGP_KEYTRUSTF_BUCKSTOP)
/*&& !(sigkeyinfo->trust & PGP_KEYTRUSTF_DISABLED)*/ ) {
/* Found a path */
ppath = pathListAddPathClone (ppath, *predh, db);
if( IsNull(ppath) ) {
/* Out of memory */
return NULL;
}
} else if( depth < maxdepth &&
!LOOKINGAT(sigkeyinfo) &&
(confidence = pathKeyConfidence(sigkey)) != 0 ) {
/* Recurse */
newseg = pathAlloc(db);
if( IsNull(newseg) ) {
/* Out of memory */
return NULL;
}
newseg->dest = userid->up;
newseg->src = sigkey;
newseg->next = NULL;
newseg->confidence = confidence;
newpredt = pathAddSeg (predt, newseg);
ppath = sFindPathsKey (sigkey, ppath, predh, newpredt,
depth+1, maxdepth, timenow, db, altdb);
pathFree (newseg, db);
*predt = NULL;
if( IsNull(ppath) ) {
/* Out of memory */
return NULL;
}
} else if( depth < maxdepth &&
!LOOKINGAT(sigkeyinfo) &&
(paireduserid =
sPairedUserID(userid, sigkey)) != NULL ) {
/*
* We have a signature by a sibling key, one which matches our
* userid. Recurse from that userid. The resulting path element
* will have as source, signer of that paired userid, and as
* destination, this key. Even though there is no "actual"
* signature in such a form, there is an implied signature.
* We won't increment depth in this case.
* Here we use the skipsigs feature - we don't want to follow
* any sigs from our pair which we are also following here.
* This is the only place it is used (at this writing!).
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -