📄 key_rd.c
字号:
/****************************************************************************
* *
* Public/Private Key Read Routines *
* Copyright Peter Gutmann 1992-2007 *
* *
****************************************************************************/
#include <stdio.h>
#define PKC_CONTEXT /* Indicate that we're working with PKC contexts */
#if defined( INC_ALL )
#include "context.h"
#include "asn1.h"
#include "asn1_ext.h"
#include "misc_rw.h"
#include "pgp.h"
#else
#include "context/context.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#include "misc/misc_rw.h"
#include "misc/pgp.h"
#endif /* Compiler-specific includes */
/* Although there is a fair amount of commonality between public and private-
key functions, we keep them distinct to enforce red/black separation.
The DLP algorithms split the key components over the information in the
AlgorithmIdentifier and the actual public/private key components, with the
(p, q, g) set classed as domain parameters and included in the
AlgorithmIdentifier and y being the actual key.
params = SEQ {
p INTEGER,
q INTEGER, -- q for DSA
g INTEGER, -- g for DSA
j INTEGER OPTIONAL, -- X9.42 only
validationParams [...] -- X9.42 only
}
key = y INTEGER -- g^x mod p
For peculiar historical reasons (copying errors and the use of obsolete
drafts as reference material) the X9.42 interpretation used in PKIX
reverses the second two parameters from FIPS 186 (so it uses p, g, q
instead of p, q, g), so when we read/write the parameter information we
have to switch the order in which we read the values if the algorithm
isn't DSA */
#define hasReversedParams( cryptAlgo ) \
( ( cryptAlgo ) == CRYPT_ALGO_DH || \
( cryptAlgo ) == CRYPT_ALGO_ELGAMAL )
#ifdef USE_PKC
/****************************************************************************
* *
* KeyID Routines *
* *
****************************************************************************/
/* Generate a key ID, which is the SHA-1 hash of the SubjectPublicKeyInfo.
There are about half a dozen incompatible ways of generating X.509
keyIdentifiers, the following is conformant with the PKIX specification
("use whatever you like as long as it's unique") but differs slightly
from one common method that hashes the SubjectPublicKey without the
BIT STRING encapsulation. The problem with that method is that some
DLP-based algorithms use a single integer as the SubjectPublicKey,
leading to potential key ID clashes */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int calculateFlatKeyID( IN_BUFFER( keyInfoSize ) const void *keyInfo,
IN_LENGTH_SHORT_MIN( 16 ) const int keyInfoSize,
OUT_BUFFER_FIXED( keyIdMaxLen ) BYTE *keyID,
IN_LENGTH_FIXED( KEYID_SIZE ) const int keyIdMaxLen )
{
HASHFUNCTION_ATOMIC hashFunctionAtomic;
assert( isReadPtr( keyInfo, keyInfoSize ) );
assert( isWritePtr( keyID, keyIdMaxLen ) );
REQUIRES( keyInfoSize >= 16 && keyInfoSize < MAX_INTLENGTH_SHORT );
REQUIRES( keyIdMaxLen == KEYID_SIZE );
/* Hash the key info to get the key ID */
getHashAtomicParameters( CRYPT_ALGO_SHA1, &hashFunctionAtomic, NULL );
hashFunctionAtomic( keyID, keyIdMaxLen, keyInfo, keyInfoSize );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int calculateKeyIDFromEncoded( INOUT CONTEXT_INFO *contextInfoPtr,
IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo )
{
PKC_INFO *publicKey = contextInfoPtr->ctxPKC;
STREAM stream;
BYTE buffer[ ( CRYPT_MAX_PKCSIZE * 4 ) + 50 + 8 ];
int length, status;
assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
REQUIRES( cryptAlgo >= CRYPT_ALGO_FIRST_PKC && \
cryptAlgo <= CRYPT_ALGO_LAST_PKC );
status = calculateFlatKeyID( publicKey->publicKeyInfo,
publicKey->publicKeyInfoSize,
publicKey->keyID, KEYID_SIZE );
if( cryptStatusError( status ) )
retIntError();
if( cryptAlgo != CRYPT_ALGO_KEA && cryptAlgo != CRYPT_ALGO_RSA )
return( CRYPT_OK );
/* If it's an RSA context we also need to remember the PGP 2 key ID
alongside the cryptlib one */
if( cryptAlgo == CRYPT_ALGO_RSA )
{
sMemConnect( &stream, publicKey->publicKeyInfo,
publicKey->publicKeyInfoSize );
readSequence( &stream, NULL );
readUniversal( &stream );
readBitStringHole( &stream, &length, MIN_PKCSIZE, DEFAULT_TAG );
readSequence( &stream, NULL );
status = readInteger( &stream, buffer, CRYPT_MAX_PKCSIZE, &length );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
retIntError();
if( length > PGP_KEYID_SIZE )
{
memcpy( publicKey->pgp2KeyID, buffer + length - PGP_KEYID_SIZE,
PGP_KEYID_SIZE );
}
return( CRYPT_OK );
}
#ifdef USE_KEA
/* If it's a KEA context we also need to remember the start and length
of the domain parameters and key agreement public value in the
encoded key data */
sMemConnect( &stream, publicKey->publicKeyInfo,
publicKey->publicKeyInfoSize );
readSequence( &stream, NULL );
readSequence( &stream, NULL );
readUniversal( &stream );
readOctetStringHole( &stream, &length, MIN_PKCSIZE, DEFAULT_TAG );
publicKey->domainParamPtr = sMemBufPtr( &stream );
publicKey->domainParamSize = ( int ) length;
sSkip( &stream, length );
readBitStringHole( &stream, &length, MIN_PKCSIZE, DEFAULT_TAG );
publicKey->publicValuePtr = sMemBufPtr( &stream );
publicKey->publicValueSize = ( int ) length - 1;
assert( sSkip( &stream, length ) == CRYPT_OK );
sMemDisconnect( &stream );
#endif /* USE_KEA */
return( CRYPT_OK );
}
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int calculateOpenPGPKeyID( INOUT CONTEXT_INFO *contextInfoPtr,
IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo )
{
PKC_INFO *publicKey = contextInfoPtr->ctxPKC;
HASHFUNCTION hashFunction;
HASHINFO hashInfo;
STREAM stream;
BYTE buffer[ ( CRYPT_MAX_PKCSIZE * 4 ) + 50 + 8 ];
BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ], packetHeader[ 64 + 8 ];
int hashSize, length, status;
assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
REQUIRES( cryptAlgo >= CRYPT_ALGO_FIRST_PKC && \
cryptAlgo <= CRYPT_ALGO_LAST_PKC );
/* Since calculation of the OpenPGP ID requires the presence of data
that isn't usually present in a non-PGP key we can't calculate a
real OpenPGP ID for some keys but have to use the next-best thing,
the first 64 bits of the key ID. This shouldn't be a major problem
because it's really only going to be used with private keys, public
keys will be in PGP format and selected by user ID (for encryption)
or PGP 2 ID/genuine OpenPGP ID (signing) */
if( ( cryptAlgo != CRYPT_ALGO_RSA && cryptAlgo != CRYPT_ALGO_DSA && \
cryptAlgo != CRYPT_ALGO_ELGAMAL ) || \
publicKey->pgpCreationTime <= MIN_TIME_VALUE )
{
/* No creation time or non-PGP algorithm, fake it */
memcpy( publicKey->openPgpKeyID, publicKey->keyID,
PGP_KEYID_SIZE );
publicKey->openPgpKeyIDSet = TRUE;
return( CRYPT_OK );
}
/* There's a creation time present, generate a real OpenPGP key ID:
byte ctb = 0x99
byte[2] length
-- Key data --
byte version = 4
byte[4] key generation time
byte algorithm
byte[] key data
We do this by writing the public key fields to a buffer and creating a
separate PGP public key header, then hashing the two */
sMemOpen( &stream, buffer, ( CRYPT_MAX_PKCSIZE * 4 ) + 50 );
status = publicKey->writePublicKeyFunction( &stream, contextInfoPtr,
KEYFORMAT_PGP,
"public_key", 10 );
if( cryptStatusError( status ) )
{
sMemClose( &stream );
return( status );
}
length = stell( &stream );
packetHeader[ 0 ] = 0x99;
packetHeader[ 1 ] = ( length >> 8 ) & 0xFF;
packetHeader[ 2 ] = length & 0xFF;
/* Hash the data needed to generate the OpenPGP keyID */
getHashParameters( CRYPT_ALGO_SHA1, &hashFunction, &hashSize );
hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2,
HASH_STATE_START );
hashFunction( hashInfo, hash, CRYPT_MAX_HASHSIZE, buffer, length,
HASH_STATE_END );
memcpy( publicKey->openPgpKeyID, hash + hashSize - PGP_KEYID_SIZE,
PGP_KEYID_SIZE );
sMemClose( &stream );
publicKey->openPgpKeyIDSet = TRUE;
return( CRYPT_OK );
}
#endif /* USE_PGP || USE_PGPKEYS */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int writePKCS3Key( INOUT STREAM *stream,
const PKC_INFO *dlpKey,
IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo )
{
const int parameterSize = ( int ) sizeofObject( \
sizeofBignum( &dlpKey->dlpParam_p ) + \
3 + /* INTEGER value 0 */
sizeofBignum( &dlpKey->dlpParam_g ) );
const int componentSize = sizeofBignum( &dlpKey->dlpParam_y );
int totalSize;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( dlpKey, sizeof( PKC_INFO ) ) );
REQUIRES( isDlpAlgo( cryptAlgo ) );
/* Implement a cut-down version of writeDlpSubjectPublicKey(), writing a
zero value for q */
totalSize = sizeofAlgoIDex( cryptAlgo, CRYPT_ALGO_NONE, parameterSize ) + \
( int ) sizeofObject( componentSize + 1 );
writeSequence( stream, totalSize );
writeAlgoIDex( stream, cryptAlgo, CRYPT_ALGO_NONE, parameterSize );
writeBignum( stream, &dlpKey->dlpParam_p );
swrite( stream, "\x02\x01\x00", 3 ); /* Integer value 0 */
writeBignum( stream, &dlpKey->dlpParam_g );
writeBitStringHole( stream, componentSize, DEFAULT_TAG );
return( writeBignum( stream, &dlpKey->dlpParam_y ) );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int calculateKeyID( INOUT CONTEXT_INFO *contextInfoPtr )
{
PKC_INFO *publicKey = contextInfoPtr->ctxPKC;
STREAM stream;
BYTE buffer[ ( CRYPT_MAX_PKCSIZE * 4 ) + 50 + 8 ];
const CRYPT_ALGO_TYPE cryptAlgo = contextInfoPtr->capabilityInfo->cryptAlgo;
int status;
assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
REQUIRES( contextInfoPtr->type == CONTEXT_PKC );
/* If the public key info is present in pre-encoded form, calculate the
key ID directly from that */
if( publicKey->publicKeyInfo != NULL )
return( calculateKeyIDFromEncoded( contextInfoPtr, cryptAlgo ) );
/* Write the public key fields to a buffer and hash them to get the key
ID */
sMemOpen( &stream, buffer, ( CRYPT_MAX_PKCSIZE * 4 ) + 50 );
if( isDlpAlgo( cryptAlgo ) && BN_is_zero( &publicKey->dlpParam_q ) )
{
/* OpenPGP Elgamal keys and SSL/SSH DH keys don't have a q
parameter, which makes it impossible to write them in the X.509
format. If this situation occurs we write them in a cut-down
version of the format, which is OK because the X.509 keyIDs are
explicit and not implicitly generated from the key data like
OpenPGP one */
status = writePKCS3Key( &stream, publicKey, cryptAlgo );
}
else
{
status = publicKey->writePublicKeyFunction( &stream, contextInfoPtr,
KEYFORMAT_CERT,
"public_key", 10 );
}
if( cryptStatusOK( status ) )
status = calculateFlatKeyID( buffer, stell( &stream ),
publicKey->keyID, KEYID_SIZE );
sMemClose( &stream );
if( cryptStatusError( status ) )
return( status );
/* If it's an RSA key, we need to calculate the PGP 2 key ID alongside
the cryptlib one */
if( cryptAlgo == CRYPT_ALGO_RSA )
{
const PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
int length;
status = getBignumData( &pkcInfo->rsaParam_n, buffer,
CRYPT_MAX_PKCSIZE, &length );
if( cryptStatusError( status ) )
return( status );
if( length > PGP_KEYID_SIZE )
{
memcpy( publicKey->pgp2KeyID,
buffer + length - PGP_KEYID_SIZE, PGP_KEYID_SIZE );
}
}
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
/* If the OpenPGP ID is already set by having the key loaded from a PGP
keyset, we're done */
if( publicKey->openPgpKeyIDSet )
return( CRYPT_OK );
/* Finally, set the OpenPGP key ID */
status = calculateOpenPGPKeyID( contextInfoPtr, cryptAlgo );
if( cryptStatusError( status ) )
return( status );
#endif /* USE_PGP || USE_PGPKEYS */
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read Public Keys *
* *
****************************************************************************/
/* Read X.509 SubjectPublicKeyInfo public keys */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int readRsaSubjectPublicKey( INOUT STREAM *stream,
INOUT CONTEXT_INFO *contextInfoPtr,
OUT_FLAGS_Z( ACTION_PERM ) int *actionFlags )
{
CRYPT_ALGO_TYPE cryptAlgo;
PKC_INFO *rsaKey = contextInfoPtr->ctxPKC;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
assert( isWritePtr( actionFlags, sizeof( int ) ) );
REQUIRES( contextInfoPtr->type == CONTEXT_PKC && \
contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA );
/* Clear return value */
*actionFlags = ACTION_PERM_NONE;
/* Read the SubjectPublicKeyInfo header field and parameter data if
there's any present. We read the outer wrapper in generic form since
it may be context-specific-tagged if it's coming from a keyset (RSA
public keys is the one place where PKCS #15 keys differ from X.509
ones) or something odd from CRMF */
readGenericHole( stream, NULL, 8 + RSAPARAM_MIN_N + RSAPARAM_MIN_E,
DEFAULT_TAG );
status = readAlgoID( stream, &cryptAlgo );
if( cryptStatusError( status ) )
return( status );
if( cryptAlgo != CRYPT_ALGO_RSA )
return( CRYPT_ERROR_BADDATA );
/* Set the maximum permitted actions. More restrictive permissions may
be set by higher-level code if required and in particular if the key
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -