pgprngmnt.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,034 行 · 第 1/5 页
C
2,034 行
/*
* pgpRngMnt.c - Perform the maintenance pass over a keyring.
* This is a lot more efficient that the way PGP 2.x did it
* and, I modestly think, easier to understand to boot.
*
* Copyright (C) 1994-1997 Pretty Good Privacy, Inc. All rights reserved.
*
* Written by Colin Plumb
*
* $Id: pgpRngMnt.c,v 1.33.2.10 1997/06/07 09:50:36 mhw Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h> /* For strlen() */
#include <time.h>
#include <stddef.h>
#include "pgpDebug.h"
#include "pgpMakeSig.h"
#include "pgpPktByte.h"
#include "pgpRngMnt.h"
#include "pgpRngPars.h"
#include "pgpRngPriv.h"
#include "pgpRngPkt.h"
#include "pgpRngRead.h"
#include "pgpTrust.h"
#include "pgpTrstPkt.h"
#include "pgpTimeDate.h"
#include "pgpHash.h"
#include "pgpErr.h"
#include "pgpPubKey.h"
#include "pgpRngRead.h" /* for ringFetchObject */
#include "pgpSig.h"
#include "pgpSigSpec.h"
#include "pgpUsuals.h"
#include "pgpTypes.h"
#ifndef NULL
#define NULL 0
#endif
/*
* This symbol marks code which prints the progress of the maintenance
* pass. That code assumes stdio, which is not portable across GUIs, so
* something will need to be done about it. The prints are left in the
* source to make the location of the replacements obvious. The code
* assumes a non-existent "FILE *f" to print to, so just #defining this
* symbol will NOT work.
*/
#undef PRINT_PROGRESS
/*
* The trust models...
* All models start from a number of axiomatic "the buck stops here"
* keys. These are the keys owned by the PGP user. This is the
* initial set of trusted keys. Each key has a trust value - a
* level of confidence that the user places in signatures made by
* that key.
*
*
* -- The old trust model (PGPTRUSTMODEL==0) --
*
* Each key has an introducer trust, either ultimate, complete, or
* partial. These have scores of 1, 1/x and 1/y. For each key on the
* axiomatically trusted list, and each signature made by that key on
* a name, the introducer trust is added to the name's validity score.
* If that score reaches 1, the name's key is added to the next introducer
* list and its introducer trust is used for the level after that.
*
* At the end, names are graded according to their score.
* If it is >= 1, they are completely valid. If it is >= 1/2,
* they are partially valid. If < 1/2, they are not valid at all.
* Partial versus lack of validity only affects the warning messages
* printed out. If a key does not have any comletely valid names,
* its introducer trust is set to 0.
*
* There are actually three kinds of zero introducer trust:
* - Undefined, meaning that it has not been set. If a key has
* a completely valid name and undefined introducer trust, the
* user is asked to set one.
* - Unknown
* - Untrusted
* These latter two are equivalent valid "set" values, with the
* only difference being for user-interface purposes.
*
*
* -- The new trust model (PGPTRUSTMODEL == 1 or 2) --
*
* This is based on a much more rigorous treatment of trust by Ueli
* Maurer. See "Modelling a Public-Key Infrastructure", Ueli Maurer,
* Proceedings 1996 European Symposium on Research in Computer Security
* (ESORICS '96), E. Bertino (Ed.), Lecture Notes in Computer Science,
* Berlin: Springer-Verlag, Rome, Italy, Sept. 1996. Also available from
* <URL: http://www.inf.ethz.ch/personal/maurer/publications>.
*
* Consider the graph formed by taking each key as a node and
* each signature as an edge, and assign each signature an independent
* probability of being correct. (The probabilities don't have to be
* independent, but it gets messy fast if they aren't, so the possibility
* isn't considered any further.) For each possible combination of
* valid and invalid signatures, assign it a probability based on the
* probability of each signature being correct, and if there is a path
* of valid signatures from the root set to a given name, it is correct.
* Add up the probabilities of all the cases in which the name is correct,
* and you have the computed probability that the name is correct.
*
* For PGPTRUSTMODEL==1:
*
* Basically, you have to consider every possible path from the root
* set of axiomatically trusted keys to the name in question and correct
* for overlaps and loops. This can't be done in a reasonable amount of
* time, but a conservative approximation can be made. By leaving out
* some of the more complex paths, a lower probability can be computed.
* Since it is lower than the ideal computation, it can still be trusted.
* A good approximation is fast to compute and yet approaches the ideal
* approximation well.
*
* Since PGP supports multiple names on a key, the question arises
* which validity value is used. The solution here is to assign a trust
* value to each of the names, take the validity*trust product for each
* name, and take the maximum. A name which is meaningless to the user
* (e.g. a name in an unknown character set, such as cyrillic or kanji)
* can't be trusted, but that doesn't mean that a second name which *is*
* intelligible should be penalized.
*
* The approximation that PGP uses is as follows:
* - Clear all trust accumulators to 0.
* - Starting with a root set of keys (level 0), each of which have some
* specified confidence, take that confidence directly as the trust.
* This is currently assumed to be ultimate. For each signature made
* by such a key, add the confidence to the name's valididty score.
* - Using this provisional validity, multiply each names's confidence
* by its validity to get a provisional trust. Use this trust to
* increase the validity score of each name already processed.
* - Multiply the final validity by the confidence to get the final trust
* on the key, which is used
*
* Description of PGPTRUSTMODEL==2:
*
* This implements the basic Maurer trust model fairly literally. The
* Maurer model is often considered to be "intractably complex" (an
* out of context quote from his paper), but with certain
* simplifications it should be feasible:
*
* - Focus on on-the-fly validation so we only have to find the validity of
* a single key.
* - Use a ringset composed only of trusted introducers and the target key.
* - Search for paths from the target key back to an axiomatic key such that
* all keys on the path are trusted introducers (nonzero confidence).
* - Limit this search by a depth constraint, and possibly limit the total
* number of nodes we will look at if the depth constraint is inadequate.
* - Find the basic trust contribution of each of the resulting paths (not yet
* considering interactions) as the product of the trust of each segment on
* the path.
* - Sort the paths by this value and prune to keep just the top 10 or
* so paths.
* - Apply Maurer's algorithm, considering all subsets of paths (2**n of them,
* where n is the number of paths). For each subset, form the union of all
* segments on the paths in that subset, and take the product of the segment
* trusts. Subsets with an odd number of paths add confidence, those with an
* even number of paths subtract.
*
* By limiting ourselves to just the trusted introducers (plus one
* other) the total number of keys to be considered should not become
* too large. Given practical and customary limitations on the number
* of signatures a key will usually have, it should be possible to
* find paths back to the axiomatic keys in a reasonable amount of
* time, especially if the algorithm only needs to be run for a single
* key (or a small number of keys) when we encrypt or check a
* signature. Then when we prune the list of paths to just the top
* 10, we should still be able to get a reasonably accurate
* (under)estimate of the actual trust, unless the key gained trust by
* having a large number of paths each with only a small amount of
* trust. That case must be neglected for efficiency in this
* algorithm. Having pruned the paths to a small enough set we can
* then apply Maurer's algorithm efficiently.
*
* There is an additional complication specific to the PGP trust
* model. Maurer's algorithm is expressed in terms of paths where
* each segment has an associated trust level. These trusts are
* described in PGP as user-defined confidence levels specified for
* the name(s) on a key. This means that the output of the
* calculation above is a validity value for a particular name on a
* key, based on the signatures on that name. The inputs need to be
* based on the user defined confidence values for the names. The
* complication arises for keys which have more than one name (userid)
* with different user-defined confidence levels. It is not clear how
* to map this situation to Maurer's trust model.
*
* For keys with only one name, the confidence of that name is used.
* If there is more than one name, a conservative approach is to use
* the name with the smallest user-defined confidence value. (Names
* for which the user has not specified a confidence level do not
* enter into this calculation.) If all of the names (of those with
* non-zero confidence) have validity values (from earlier runs of the
* trust calculation) then we can choose the confidence of the name
* with the highest validity as in PGPTRUSTMODEL==1.
*
* Not all of the optimizations above are implemented in the current
* code, nor has it been thoroughly tested. In particular, the
* ringMnt function is still used and tries to run the algorithm on
* all keys in the set. Although a depth limitation is used there is
* no other way of limiting the construction of the paths. And the
* set of keys looked at is not pre-selected to be limited to the
* trusted introducers. That last change should be done before
* enabling this code.
*
* Some further ideas for improvement:
*
* - Paths don't need to include the last step to the trusted
* introducer, since that always has confidence of 1.0.
*
* - For creating the path subset unions, a linear search for overlap
* is used now. This may be OK for short paths but a more efficient
* algorithm would be better. Perhaps a hash table for path segments
* in the subset would be worthwhile.
*
* - When adding/subtracting the pathconf values, maybe it would be
* better to accumulate 1-confidence rather than confidence? I am
* worried about roundoff error for values very close to 1.0.
*
* - Could use a Gray code to go through path subsets in a sequence
* which would alternate even and odd parity subsets, so we would
* alternately add and subtract trust. This might help with
* accuracy. (A Gray code is a binary counter which has the property
* that only one bit changes each step. One example is:
* ++j, i ^= j & (j ^ (j-1)); which counts j as a regular counter and
* i as a Gray code counter.)
*/
/*
* Check to see if the signature is valid in time.
*
* To avoid overflow problems with 65535 days being larger than a 32-bit
* timestamp, compute how old the signature is in days, and compare
* that with the validity period. If its age, in days rounded down,
* is < the validity period, it is valid. To deal with a validity
* period of 0 being infinite, subtract one from the validity using
* unsigned arithmetic and change the < to <=. (n < 5 iff n <= 4, for
* integer n). 0 will wrap to UINT_MAX, and every unsigned number
* compared <= UINT_MAX, so the test will always be true.
*
* An intelligent 8086 or 68000 compiler would evaluate the division by
* 86400 = 675 * 128 as (x>>7)/675, which can use a 32/16 bit divide
* (which can't overflow), but I'd be surprised if any PC compilers
* actually did it that way.
*/
static int
mntSigIsValid(word32 timenow, word32 tstamp, unsigned validity)
{
return
(timenow <= tstamp || (unsigned)((timenow-tstamp)/86400) <= validity-1u);
}
#if PGPTRUSTMODEL==2
#define LOOKINGAT(key) ((key)->confidence & 1)
#define SETLOOKINGAT(key) ((key)->confidence |= 1)
#define CLEARLOOKINGAT(key) ((key)->confidence &= ~1)
/*
* Maximum number of paths we will keep.
* Must calculate trust for each subset.
*/
#define TOTALPATHMAX 10
/*
* The Path and PathList routines use a convention copied from the
* PgpPipeline package for tail pointers. The tail pointer points
* at the "next" pointer of the last packet, or if there are no packets
* it points at the "head" pointer for the whole list. This allows a
* uniform interface for adding packets whether they are the first or
* not.
*/
/* This is a path or a path segment. next->src may not equal dest. */
typedef struct Path {
struct Path *next;
RingObject *src,
*dest;
double confidence;
} Path, *pPath;
/* This is a list of paths. Some segments may be on multiple paths. */
typedef struct PathList {
struct PathList *next;
Path *path;
Path **ptail;
double confidence;
} PathList, *pPathList;
#undef DEBUGPATH
#ifdef DEBUGPATH
static char const hexchar[16] = {
'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'
};
static int
ringTtyPrintKeyID(FILE *f, RingObject *key)
{
byte *buf;
int i;
pgpAssert (OBJISKEY(key));
buf = key->k.keyID;
for (i = 4; i < 8; i++) {
putc(hexchar[buf[i] >> 4 & 15], f);
putc(hexchar[buf[i] & 15], f);
}
return 8;
}
static int
ringTtyPrintKeyName(FILE *f, RingObject *key, RingPool *pool)
{
RingSet *set = ringSetCreate(pool);
char const *sname;
char bname[256];
size_t len;
key = key->g.down;
while (!OBJISNAME(key)) {
key = key->g.next;
}
ringSetAddObject (set, key);
sname = ringNameName (set, key, &len);
strncpy (bname, sname, len);
bname[len] = '\0';
fputs (bname, f);
ringSetDestroy (set);
return 0;
}
static int
ringTtyPrintKey(FILE *f, RingObject *key, RingPool *pool)
{
ringTtyPrintKeyID (f, key);
fprintf (f, "(");
ringTtyPrintKeyName (f, key, pool);
fprintf (f, ")");
return 0;
}
static void
ringTtyPrintPath(FILE *f, Path *path, RingPool *pool)
{
fprintf (f, " Path ");
while (path) {
fprintf (f, "from ");
ringTtyPrintKey (f, path->src, pool);
fprintf (f, " to ");
ringTtyPrintKey (f, path->dest, pool);
fprintf (f, ", conf: %g\n", path->confidence);
path = path->next;
if (path)
fprintf (f, " ...segment ");
}
}
static void
ringTtyPrintPathList(FILE *f, PathList *pl, RingPool *pool)
{
fprintf (f, "Begin PathList\n");
while (pl) {
fprintf (f, " (conf=%g) ", pl->confidence);
ringTtyPrintPath (f, pl->path, pool);
pl = pl->next;
}
fprintf (f, "End PathList\n");
}
#endif /* DEBUGPATH */
/* Allocate a Path segment */
static Path *
pathAlloc (RingPool *pool)
{
Path *path = pool->paths;
if (path) {
pool->paths = path->next;
} else {
path = memPoolAlloc (&pool->pathpool, sizeof(struct Path),
alignof(struct Path));
}
return path;
}
/* Free a Path segment */
static void
pathFree (Path *path, RingPool *pool)
{
path->next = pool->paths;
pool->paths = path;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?