sources/TPGPkey.cp

//  TPGPkey.cp - PGP Key Object  
// 
// Apple Macintosh Developer Technical Support
// Written by:  Vinnie Moscaritolo
//
//  Copyright (work in progress)  Apple Computer, Inc All rights reserved.
//
// You may incorporate this sample code into your applications without
// restriction, though the sample code has been provided "AS IS" and the
// responsibility for its operation is 100% yours.  However, what you are
// not permitted to do is to redistribute the source as "DSC Sample Code"
// after having made changes. If you're going to re-distribute the source,
// we require that you make it clear in the source that the code was
// descended from Apple Sample Code, but that you've made changes.
// 
 
 
#include <string.h>
#include <TextUtils.h>
#include <PLStringFuncs.h>
#include "TPGPkey.h"
#include "TPGPException.h"
 
#include "pgpRandomPool.h"
#include "pgpUserInterface.h"
#include "pgpPublicKey.h"
 
 
 
// ===========================================================================
//  Static member variables
// ===========================================================================
 
PGPContextRef       TPGPkey::fgContext      = kInvalidPGPContextRef;    //  PGP context
PGPKeySetRef        TPGPkey::fgPGPKeySetRef = kInvalidPGPKeySetRef;         //  this keyset
 
// ---------------------------------------------------------------------------
 void TPGPkey::Initialize() //  (static public)
// ---------------------------------------------------------------------------
//  Create a new PGP context
{
    if(! PGPContextRefIsValid(fgContext) ) 
        ThrowIfPGPErr( PGPNewContext( kPGPsdkAPIVersion, &fgContext));
 
}
 
// ---------------------------------------------------------------------------
 void TPGPkey::Initialize(PGPMemoryMgrRef memMgr) //  (static public)
// ---------------------------------------------------------------------------
//  Create a ustome PGP context
{
 
    PGPNewContextStruct contextInfo;
 
// Create a custom PGP context
    contextInfo.sizeofStruct =  sizeof( PGPNewContextStruct );
    contextInfo.memoryMgr    =  memMgr;
 
    if(! PGPContextRefIsValid(fgContext) ) 
        ThrowIfPGPErr(  PGPNewContextCustom( kPGPsdkAPIVersion, &contextInfo ,&fgContext));
 
}
 
 
 
// ---------------------------------------------------------------------------
 void TPGPkey::Finalize() //  (static public)
// ---------------------------------------------------------------------------
//  shutdown a new PGP context
{
    if( PGPContextRefIsValid( fgContext)) PGPFreeContext(fgContext );
    fgContext = kInvalidPGPContextRef;
 
}
 
// ---------------------------------------------------------------------------
 void TPGPkey::OpenKeyDefaultRing() //  (static public)
// ---------------------------------------------------------------------------
//  Create a new PGP context
{
    if( PGPContextRefIsValid(fgContext)  
        && ( fgPGPKeySetRef == kInvalidPGPKeySetRef) )
            ThrowIfPGPErr( PGPOpenDefaultKeyRings(fgContext,  0, &fgPGPKeySetRef)) ; 
 }
 
 
// ---------------------------------------------------------------------------
 void TPGPkey::CloseKeyRing() //  (static public)
// ---------------------------------------------------------------------------
//  Create a new PGP context
{
    if( PGPContextRefIsValid(fgContext) && PGPKeySetRefIsValid ( fgPGPKeySetRef ))
        PGPFreeKeySet (fgPGPKeySetRef );
 
    fgPGPKeySetRef = kInvalidPGPKeySetRef;
 }
 
 
 
// ---------------------------------------------------------------------------
void    TPGPkey::Initialize(PGPKeyRef theKey)
// ---------------------------------------------------------------------------
//  
{
    
    if( PGPContextRefIsValid(fgContext)
    /*      && PGPKeySetRefIsValid(fgPGPKeySetRef) */
        && PGPKeyRefIsValid(theKey))
    {
        PGPHashContextRef   theHashRef      = kInvalidPGPHashContextRef;
        PGPInt32            level;
        PGPTime             theTime;
        
        ThrowIfPGPErr( PGPGetKeyNumber (theKey,  kPGPKeyPropAlgID,  (PGPInt32*) &fPublicKeyAlgorithm));
        ThrowIfPGPErr( PGPGetHashAlgUsed (theKey, &fHashAlgorithm) );
        ThrowIfPGPErr( PGPGetKeyBoolean (theKey, kPGPKeyPropIsExpired,  &fExpired));
        ThrowIfPGPErr( PGPGetKeyBoolean (theKey, kPGPKeyPropIsRevoked,  &fRevoked));
        ThrowIfPGPErr( PGPGetKeyBoolean (theKey, kPGPKeyPropIsDisabled, &fDisabled));
        ThrowIfPGPErr( PGPGetKeyBoolean (theKey, kPGPKeyPropIsAxiomatic,&fAxiomatic)) ;
        ThrowIfPGPErr( PGPGetKeyNumber(theKey, kPGPKeyPropBits, (PGPInt32*) &fKeySize));
        ThrowIfPGPErr( PGPGetKeyTime(theKey, kPGPKeyPropCreation, &theTime));
        fCreatedTime = PGPTimeToMacTime(theTime);
        ThrowIfPGPErr( PGPGetKeyTime(theKey, kPGPKeyPropExpiration, &theTime));
        fExpireTime = PGPTimeToMacTime(theTime);
    
        ThrowIfPGPErr (PGPNewHashContext( PGPGetContextMemoryMgr(fgContext), fHashAlgorithm, &theHashRef));
        ThrowIfPGPErr (PGPGetHashSize( theHashRef,&fHashSize));
        PGPFreeHashContext(theHashRef);
 
    
        ThrowIfPGPErr( PGPGetPrimaryUserIDValidity(theKey, &fValidity));
 
        ThrowIfPGPErr( PGPGetKeyNumber(theKey, kPGPKeyPropTrust, &level));
        switch(level)
            {
                default:
                case kPGPKeyTrust_Undefined:
                case kPGPKeyTrust_Unknown:
                case kPGPKeyTrust_Never:
                    fTrustLevel = 0;
                    break;
                case kPGPKeyTrust_Marginal:
                    fTrustLevel = 1;
                    break;
                case kPGPKeyTrust_Complete:
                    fTrustLevel = 2;
                    break;
                case kPGPKeyTrust_Ultimate:
                    fTrustLevel = 3;
                    break;
            }
        fKeyRef = theKey;
        fInitialized = true;
    }
    else
    {
        fKeyRef      = kInvalidPGPKeyRef;
        fInitialized = false;
    }
}
 
// ---------------------------------------------------------------------------
Boolean TPGPkey::CanSign()
// ---------------------------------------------------------------------------
// key can be used to sign
{
    Boolean result = false;
    
    if( this->IsOperational() )
        ThrowIfPGPErr( PGPGetKeyBoolean (fKeyRef, kPGPKeyPropCanSign,  &result));
    
    return result;
}
 
// ---------------------------------------------------------------------------
Boolean TPGPkey::CanVerify()
// ---------------------------------------------------------------------------
// key can be used to sign
{
    Boolean result = false;
    
    if( this->IsOperational() )
        ThrowIfPGPErr( PGPGetKeyBoolean (fKeyRef, kPGPKeyPropCanVerify,  &result));
    
    return result;
}
 
 
// ---------------------------------------------------------------------------
short   TPGPkey::GetIconID()
// ---------------------------------------------------------------------------
// determine which icon to draw
{
 
    short   iconID = kAlertIcon_ID;
    
    if( fInitialized )
    {
        if(fPublicKeyAlgorithm == kPGPPublicKeyAlgorithm_RSA)
        {
            if( fRevoked) iconID = kIconRSA_Revoked_ID;
            else if(fExpired) iconID = kIconRSA_Expired_ID;
            else if(fDisabled) iconID = kIconRSA_Disabled_ID;
            else iconID = kIconRSA_ID;
        } 
        else if(fPublicKeyAlgorithm == kPGPPublicKeyAlgorithm_DSA)
        {
            if(fRevoked) iconID = kIconDH_RevokedID;
            else if(fExpired) iconID = kIconDH_Expired_ID;
            else if(fDisabled) iconID = kIconDH_Disabled_ID;
            else iconID = kIconDH_ID;
        }
    }
 
    return iconID;
}
 
// ---------------------------------------------------------------------------
void    TPGPkey::GetPrimaryUserNamePString(StringPtr outbuf)
// ---------------------------------------------------------------------------
// 
{
    PGPSize     bufLen;
    char        buffer[256];
 
    ThrowIfPGPErr(PGPGetPrimaryUserIDNameBuffer(fKeyRef, sizeof( buffer) -1 , (char*) &buffer[1], &bufLen ));
 
  // truncate at email address
     buffer[0] =  (strchr(&buffer[1], '<')  - &buffer[1] - 1);
    PLstrcpy(outbuf,(StringPtr) buffer);
    
}
 
 
// ---------------------------------------------------------------------------
void    TPGPkey::GetFingerprintBinaryPString(void* outBuf)
// ---------------------------------------------------------------------------
// 
{ 
    PGPSize fpSize;
    unsigned char* p = (unsigned char*) outBuf;
    
    ThrowIfPGPErr( PGPGetKeyPropertyBuffer( fKeyRef, kPGPKeyPropFingerprint, MAX_PGPSize, NULL, &fpSize));
 
    ThrowIfPGPErr(PGPGetKeyPropertyBuffer( fKeyRef, kPGPKeyPropFingerprint, fpSize, &p[1], &fpSize));
    
    p[0] = (fpSize & 0xFF);
 
}
 
 
// ---------------------------------------------------------------------------
void    TPGPkey::GetFingerprintPString(StringPtr outBuf)
// ---------------------------------------------------------------------------
// 
{
    const    char   hexDigit[] = "0123456789ABCDEF";
    PGPSize         bufLen;
    unsigned char   buffer[255];
    int             strIndex;
 
    ThrowIfPGPErr(PGPGetKeyPropertyBuffer( fKeyRef, kPGPKeyPropFingerprint, sizeof( buffer ), buffer, &bufLen));
 
    char *p = (char *) outBuf;
    *p++ = '\0';
    
    if(bufLen == 20)
        {
            for(strIndex = 0 ; strIndex < 20 ; strIndex++)
            {
                *p++ = hexDigit[buffer[strIndex]>>4];
                *p++ = hexDigit[buffer[strIndex]&0xF];
                if((strIndex == 1) || (strIndex == 3) || (strIndex == 5)
                        || (strIndex == 7) || (strIndex == 11) ||
                        (strIndex == 13)
                        || (strIndex == 13) || (strIndex == 15) ||
                        (strIndex == 17))
                    *p++ = ' ';
                else if(strIndex == 9)
                {
                    *p++ = ' ';
                    *p++ = ' ';
//                  *p++ = '\r';
                }
            }
        }
    else
        {
            for(strIndex = 0 ; strIndex < 16 ; strIndex++)
            {
                *p++ = hexDigit[buffer[strIndex]>>4];
                *p++ = hexDigit[buffer[strIndex]&0xF];
                if((strIndex == 1) || (strIndex == 3) || (strIndex == 5)
                    || (strIndex == 9) || (strIndex == 11) || (strIndex == 13))
                    *p++ = ' ';
                else if(strIndex == 7)
                {
                    *p++ = ' ';
                    *p++ = ' ';
                }
            }
        }
    outBuf[0] = (p - (char*)outBuf - 1) & 0xFF;
 
}
 
 
// ---------------------------------------------------------------------------
void    TPGPkey::GetPublicKeyAlgorithmPstring(StringPtr outBuf)
// ---------------------------------------------------------------------------
// 
{
    StringPtr   algStr = "\pUnknown";
        
    if(outBuf)
    {
        if( fInitialized )
            switch(fPublicKeyAlgorithm)
            {
                case kPGPPublicKeyAlgorithm_Invalid:        algStr = "\pInvalid";  break;
                case kPGPPublicKeyAlgorithm_RSA:            algStr = "\pRSA";   break;
                case kPGPPublicKeyAlgorithm_RSAEncryptOnly: algStr = "\pRSA (Encrypt Only)"; break;
                case kPGPPublicKeyAlgorithm_RSASignOnly:    algStr = "\pRSA (Sign Only)";   break;
                case kPGPPublicKeyAlgorithm_ElGamal:        algStr = "\pDiffie-Hellman";        break;
                case kPGPPublicKeyAlgorithm_DSA:            algStr = "\pDSS";   break;
            }
        PLstrcpy(outBuf,algStr);
    }
}
 
 
 
 
 
// ---------------------------------------------------------------------------
void    TPGPkey::Export(void** buf, PGPSize *bufSize )
// ---------------------------------------------------------------------------
// 
{
    if( this->IsOperational() )
    {
        PGPKeySetRef theKeyset;
        Str255          keyName;
    
        this->GetPrimaryUserNamePString(keyName);
        p2cstr(keyName);
        
        PGPNewSingletonKeySet( fKeyRef,&theKeyset );
        PGPExportKeySet( theKeyset,
                            PGPOAllocatedOutputBuffer   ( fgContext, buf, MAX_PGPUInt32, bufSize ),
                            PGPOArmorOutput (fgContext, true),
//                          PGPOVersionString   (fgContext, "PGPUAM Version 1.0" ),
                            PGPOCommentString   (fgContext, (char*) keyName),
                            PGPOLastOption( fgContext ));
                            
        PGPFreeKeySet(theKeyset);
    }
 
 
}
 
// the behaviour of allowing disable keys to verify is questionable.
// PGPTools allows a user to verify and decrypt with keys that have
// been flagged by the user as disabled...BUT It could be argued that
// this is the wroong thing to do in a server authentication environment
// if disagree wit hthis feel free to disable the following define..
 
#define DISABLED_KEYS_CANT_VERIFY 1
 
 
// ---------------------------------------------------------------------------
Boolean TPGPkey::Verify( void* inbuf, PGPSize inBufSize, void* sigbuf,  PGPSize sigBufSize )
// ---------------------------------------------------------------------------
// 
{
    PGPError err  = kPGPError_BadParams;
    
    if( this->IsOperational()  
#if DISABLED_KEYS_CANT_VERIFY
                && ! this->IsDisabled()
#endif
        )
    {
        PGPHashContextRef       theHashRef      = kInvalidPGPHashContextRef;
        PGPPublicKeyContextRef  thePublicKeyRef = kInvalidPGPPublicKeyContextRef;
 
        ThrowIfPGPErr( PGPNewHashContext( PGPGetContextMemoryMgr(fgContext), fHashAlgorithm, &theHashRef));
        ThrowIfPGPErr( PGPContinueHash( theHashRef,inbuf, inBufSize ));
 
        ThrowIfPGPErr( PGPNewPublicKeyContext(  fKeyRef,
                                                kPGPPublicKeyMessageFormat_PGP,
                                                &thePublicKeyRef));
        err = PGPPublicKeyVerifySignature( thePublicKeyRef, theHashRef, sigbuf, sigBufSize) ;
 
        PGPFreePublicKeyContext(thePublicKeyRef);
    }
    
   
    return (err == kPGPError_NoErr) ;
 
}
 
 
 
// ---------------------------------------------------------------------------
Boolean TPGPkey::Sign( void* inbuf, PGPSize inBufSize, void* sigbuf,  PGPSize *sigBufSize,  const char *passPhrase)
// ---------------------------------------------------------------------------
// 
{
    PGPError err  = kPGPError_BadParams;
 
    if( this->IsOperational() )
    {
 
        PGPHashContextRef       theHashRef          = kInvalidPGPHashContextRef;
        PGPPrivateKeyContextRef thePrivateKeyRef    = kInvalidPGPPrivateKeyContextRef;
 
        ThrowIfPGPErr( PGPNewHashContext( PGPGetContextMemoryMgr(fgContext), fHashAlgorithm, &theHashRef));
        ThrowIfPGPErr( PGPContinueHash( theHashRef,inbuf, inBufSize ));
 
        err =  PGPNewPrivateKeyContext( fKeyRef, kPGPPublicKeyMessageFormat_PGP, &thePrivateKeyRef,
                                            PGPOPassphrase( fgContext, passPhrase),
                                            PGPOLastOption( fgContext ));
        if(err == kPGPError_NoErr)
        {   
        ThrowIfPGPErr( PGPPrivateKeySign( thePrivateKeyRef, theHashRef, sigbuf, sigBufSize ));
        PGPFreePrivateKeyContext(thePrivateKeyRef);
        }
    }
 
    return (err == kPGPError_NoErr) ;
 }