📄 pgpkeysig.c
字号:
&handled, &changed,
&verified);
}
if( IsPGPError( err ) )
goto error;
if( changed && IsntNull(changeSet) )
PGPAddKey( key, changeSet );
if( handled && IsntNull(func) )
{
err = func( arg, gchild, verified );
if( IsPGPError( err ) )
goto error;
}
}
}
}
}
error:
return err;
}
/* Mark sig as unchecked */
static void
sUncheckSig( PGPKeyDBObj *sig, PGPKeyDB *keydb, PGPBoolean *changed )
{
PGPSigInfo * sinfo;
PGPByte strust;
*changed = FALSE;
sinfo = pgpSigToSigInfo( sig );
strust = sinfo->trust;
strust &= ~PGP_SIGTRUSTF_CHECKED_TRIED;
if( strust != sinfo->trust )
{
sinfo->trust = strust;
pgpKeyDBIsDirty( keydb );
*changed = TRUE;
}
}
PGPError
pgpCheckSig_internal( PGPKeyDBRef keydb, PGPKeyDBObj *sig,
PGPKeyDBRef otherdb, PGPBoolean checkAll, PGPBoolean revocationonly,
PGPBoolean *handled, PGPBoolean *changed, PGPBoolean *verified )
{
PGPKeyDBObj * signkey;
PGPError err = kPGPError_NoErr;
*handled = FALSE;
*changed = FALSE;
*verified = FALSE;
if( sCheckableSig( sig, otherdb, checkAll, revocationonly, &signkey ) )
{
if( IsNull( signkey ) )
{
sUncheckSig( sig, keydb, changed );
} else {
*handled = TRUE;
err = sCheckSig( sig, signkey, changed, verified );
}
}
return err;
}
/* Check sig */
static PGPError
sCheckSig( PGPKeyDBObj *sig, PGPKeyDBObj *key, PGPBoolean *changed,
PGPBoolean *verified )
{
PGPKeyInfo * kinfo;
PGPSigInfo * sinfo;
PGPKeyDB * keydb;
PGPKeyDB * sigdb;
PGPKeyDBObj * parent;
PGPKeyDBObj * topkey;
PGPKeyDBObj * parentkey;
PGPUserIDInfo * uinfo;
PGPContextRef context;
PGPByte strust;
PGPBoolean sighashnew;
PGPHashVTBL const * hash;
PGPHashContextRef hashctx = NULL;
PGPByte const * buf;
PGPByte const * extrabuf;
PGPSize len;
PGPSize hlen;
PGPSize extralen;
PGPError err = kPGPError_NoErr;
MemPool cut;
PGPMemoryMgrRef mgr = NULL;
pgpAssert( OBJISSIG( sig ) );
pgpAssert( OBJISKEY( key ) );
*changed = FALSE;
*verified = FALSE;
keydb = PGPPeekKeyDBObjKeyDB( key );
sigdb = PGPPeekKeyDBObjKeyDB( sig );
context = PGPPeekKeyDBContext( keydb );
cut = *pgpPeekContextMemPool( context );
mgr = pgpPeekContextMemPoolMgr( context );
sinfo = pgpSigToSigInfo( sig );
strust = sinfo->trust;
strust &= ~PGP_SIGTRUSTF_CHECKED_TRIED;
/* If the superior object is a userid, reset the WARNONLY flag. */
parent = sig->up;
if( OBJISUSERID(parent) )
{
uinfo = pgpUserIDToUserIDInfo( parent );
if( uinfo->oldvalidity & PGP_USERIDTRUSTF_WARNONLY)
{
uinfo->oldvalidity &= ~PGP_USERIDTRUSTF_WARNONLY;
pgpKeyDBIsDirty( sigdb );
}
}
/* The new hash style includes the userid header and count */
sighashnew = sinfo->version > PGPVERSION_3;
/* Special verification rules for X.509 signatures */
if( SIGISX509(sinfo) ) {
strust |= PGP_SIGTRUSTF_TRIED;
if( pgpSigX509Valid (sig, key) )
strust |= PGP_SIGTRUSTF_CHECKED;
goto done;
}
hash = pgpHashByNumber( (PGPHashAlgorithm)sinfo->hashalg );
if( IsNull( hash ) )
goto done;
/* Try to create our hash context */
hashctx = pgpHashCreate( mgr, hash );
if( IsNull( hashctx ) )
{
err = kPGPError_OutOfMemory;
goto done;
}
if( IsPGPError( err = pgpHashObjParents( hashctx, sig->up, sighashnew ) ) )
goto done;
buf = pgpFetchObject(sig, &len);
extrabuf = pgpSigParseExtra(buf, len, &extralen);
strust |= PGP_SIGTRUSTF_TRIED;
/*
* extralen != 5 legal only if sig is on key directly,
* or it is a new-format EXTENDED type.
*/
if( !OBJISTOPKEY(sig->up) /* Sig directly on key */
&& extralen != 5 /* Traditional case */
&& sinfo->version <= PGPVERSION_3) /* New format packets */
{
/* Not legal, set as failed */
goto done;
}
/* Hash in extra bytes for signature */
PGPContinueHash(hashctx, extrabuf, extralen);
if( sighashnew )
{
/* Make sure hash can't match any old or doc hashes */
PGPByte postscript[6];
postscript[0] = PGPVERSION_4; /* actually a 4! */
postscript[1] = 0xff; /* different from sig type */
postscript[2] = (PGPByte)(extralen>>24);
postscript[3] = (PGPByte)(extralen>>16);
postscript[4] = (PGPByte)(extralen>> 8);
postscript[5] = (PGPByte)(extralen>> 0);
PGPContinueHash (hashctx, postscript, sizeof(postscript));
}
/* Ready for the actual sig check */
hlen = pgpPktBufferHeaderLen( buf );
err = pgpSigDataCheckBuf(buf+hlen, len-hlen, key, pgpHashFinal(hashctx));
PGPFreeHashContext( hashctx );
hashctx = NULL;
if( err != 1 && err != 0 )
{
/* ViaCrypt self-signs encryption-only keys (as they're RSA,
it's possible), but we don't handle these. Ignore this
type of signature. */
if( pgpIsKeyRelatedError( err ) )
{
err = kPGPError_NoErr;
}
goto done;
}
if( err == 1 )
{
/* Success */
strust |= PGP_SIGTRUSTF_CHECKED;
/*
* On finding a valid self signature, apply any info in sig
* to parent key which goes there. "key" is signer, topkey,
* is top level key above this sig, parentkey is closest
* key above this sig (may be a subkey).
*/
/* Find the top level key and the key which is above us */
if( OBJISTOPKEY( sig->up ) )
{
topkey = sig->up;
parentkey = topkey;
} else {
topkey = sig->up->up;
if( OBJISSUBKEY( sig->up ) )
parentkey = sig->up;
else
parentkey = topkey;
}
pgpAssert( OBJISTOPKEY( topkey ) );
pgpAssert( OBJISKEY( parentkey ) );
if( key == topkey ) {
/* Self signature */
PGPByte const *pk;
/* Look for expiration date, apply to key above us */
/* Note that this may alter the contents of buf */
pk = pgpSigFindSubpacket (buf, SIGSUB_KEY_EXPIRATION,
0, NULL, NULL, NULL, NULL, NULL);
if( IsntNull(pk) ) {
PGPUInt32 keyexp;
keyexp = (PGPUInt32)
((unsigned)pk[0]<<8|pk[1]) << 16 |
((unsigned)pk[2]<<8|pk[3]);
kinfo = pgpKeyToKeyInfo( parentkey );
kinfo->validityperiod =
(PGPUInt16)(keyexp/(24*3600));
}
/* Also check for primary userid sigs here */
if( OBJISUSERID( parent ) )
{
if( !(sinfo->trust & PGP_SIGTRUSTF_CHECKED) &&
(strust & PGP_SIGTRUSTF_CHECKED) )
{
/* A freshly validated self-sig */
if( SIGISPRIMARYUID (sinfo) )
{
/* Clear any manual primary uid settings we had */
pgpKeyClearPrimaryUserIDs( key, (PGPUInt32)-1 );
}
}
}
}
}
err = kPGPError_NoErr;
done:
*verified = (strust & PGP_SIGTRUSTF_CHECKED) != 0;
if( sinfo->trust != strust )
{
pgpKeyDBIsDirty( sigdb );
sinfo->trust = strust;
*changed = TRUE;
}
if( IsntNull( hashctx ) )
PGPFreeHashContext( hashctx );
if( IsntNull( mgr ) )
pgpContextMemPoolCutBack( context, &cut );
return err;
}
/*
* Update the hash context with the data in the object.
*
* hc HashContext to update
* obj Object to update it with
* hashuseridlength If true, include the length of a userid packet in hash
* (this is the post-V3 sig convention)
*
* Return 0 on success, or an error code
*/
PGPError
pgpHashObj (PGPHashContext *hc, PGPKeyDBObj *obj, PGPBoolean hashuseridlength)
{
PGPKeyDB *keydb;
PGPUserIDInfo *uinfo;
PGPSize objlen;
PGPSize hlen;
PGPByte const *objbuf;
PGPByte tmpbuf[5];
PGPBoolean mustfree = FALSE;
if( IsNull(hc) || IsNull(obj) )
return kPGPError_BadParams;
if( OBJISKEY(obj) && pgpKeyIsSec(obj) )
{
objbuf = pgpKeyDBObjToPubData( obj, &objlen );
mustfree = TRUE;
} else {
objbuf = pgpFetchObject(obj, &objlen);
}
if( IsNull(objbuf) )
{
keydb = PGPPeekKeyDBObjKeyDB( obj );
return pgpKeyDBError(keydb);
}
hlen = pgpPktBufferHeaderLen( objbuf );
objbuf += hlen;
objlen -= hlen;
/* We use this format even for subkeys */
if( OBJISKEY(obj) )
{
pgpAssert(objlen <= 65535);
tmpbuf[0] = PKTBYTE_BUILD(PKTBYTE_PUBKEY, 1);
tmpbuf[1] = (PGPByte)(objlen>>8);
tmpbuf[2] = (PGPByte)objlen;
PGPContinueHash(hc, tmpbuf, 3);
} else if( OBJISUSERID(obj) && hashuseridlength ) {
/*
* Use four bytes for userid length for future expansion. Can't do
* it for keys due to backwards compatibility.
*/
uinfo = pgpUserIDToUserIDInfo( obj );
tmpbuf[0] = NAMEISATTR(uinfo)
? PKTBYTE_BUILD(PKTBYTE_ATTRIBUTE, 0)
: PKTBYTE_BUILD(PKTBYTE_USERID, 0);
tmpbuf[1] = (PGPByte)(objlen>>24);
tmpbuf[2] = (PGPByte)(objlen>>16);
tmpbuf[3] = (PGPByte)(objlen>> 8);
tmpbuf[4] = (PGPByte)(objlen>> 0);
PGPContinueHash(hc, tmpbuf, 5);
}
PGPContinueHash(hc, objbuf, objlen);
if( mustfree )
PGPFreeData( (PGPByte *)objbuf-hlen );
return kPGPError_NoErr;
}
/*
* Update the hash context with the data from the object and its parents
*/
PGPError
pgpHashObjParents (PGPHashContext *hc, PGPKeyDBObj *obj,
PGPBoolean hashuseridlength)
{
PGPKeyDBObj * parents[3];
PGPError err;
int level;
/* Trace object's parents up to top level */
level = 0;
for ( ; ; ) {
pgpAssert ((unsigned)level < sizeof(parents)/sizeof(parents[0]));
parents[level++] = obj;
if( OBJISTOPKEY(obj) )
break;
obj = obj->up;
}
/* Hash downwards from top to object */
while (--level >= 0) {
err = pgpHashObj( hc, parents[level], hashuseridlength );
if( IsPGPError( err ) ) {
return err;
}
}
return kPGPError_NoErr;
}
/*
* Sign the specified object obj, along with its parents.
* Place signature into newly allocated sig buffer.
* sig buffer should be at least pgpMakeSigMaxSize(spec) bytes lon
* Returns size of sig in bytes on success, negative on error.
*/
PGPInt32
pgpSignObj(PGPKeyDBObj *obj, PGPSigSpec *spec, PGPByte **sig)
{
PGPContextRef context;
PGPHashContext *hc;
PGPInt32 len;
PGPError retval;
PGPBoolean sighashnew;
MemPool cut;
PGPMemoryMgrRef mgr = NULL;
/* Initialize hash */
context = PGPPeekKeyDBContext(PGPPeekKeyDBObjKeyDB( obj ) );
cut = *pgpPeekContextMemPool( context );
mgr = pgpPeekContextMemPoolMgr( context );
hc = pgpHashCreate( mgr, pgpSigSpecHash(spec));
if( IsNull(hc) )
{
pgpContextMemPoolCutBack( context, &cut );
return kPGPError_BadHashNumber;
}
/* Use new hashing convention (include userid length) on new formats */
sighashnew = pgpSigSpecVersion(spec) > PGPVERSION_3;
retval = pgpHashObjParents( hc, obj, sighashnew );
if( IsPGPError( retval ) )
{
PGPFreeHashContext(hc);
pgpContextMemPoolCutBack( context, &cut );
return (PGPInt32) retval;
}
/* Make the new sig */
len = pgpMakeSig (spec, hc, sig);
PGPFreeHashContext(hc);
pgpContextMemPoolCutBack( context, &cut );
return len;
}
/*
* Local Variables:
* tab-width: 4
* End:
* vi: ts=4 sw=4
* vim: si
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -