Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex-kmip-6.cpp
#include <stdio.h>
#include <stdlib.h>
#include <memory> // nothrow
#include "p6skc.h"
#include "p6config.h"
#include "p6file.h"
#include "p6dir.h"
#include "p6gencerts.h"
#include "p6genkeys.h"
#include "p6kmipserver.h"
#include "p6lock.h"
#include "cconsolestream.h"
using namespace P6R;
P6DECLARE_CID( p6KMIPEncoder );
P6DECLARE_CID( p6KMIPClient );
P6DECLARE_CID( p6Time );
P6DECLARE_CID( p6IoBufferFactory );
P6DECLARE_CID( p6EntropySource );
P6DECLARE_CID( p6Random );
P6DECLARE_IID( p6IRunningIface );
P6DECLARE_CID( p6Cert );
P6DECLARE_CID( p6GenCerts );
P6DECLARE_CID( p6GenKeys );
P6DECLARE_CID( p6SymmetricCrypto );
P6DECLARE_CID( p6CryptoKey );
P6DECLARE_CID( p6Dir );
P6DECLARE_CID( p6UnbufferedFile );
P6DECLARE_CID( p6Keystore );
P6DECLARE_CID( p6KMIPServer );
P6DECLARE_CID( p6TcpSocket );
P6DECLARE_CID( p6IntervalTime );
P6DECLARE_CID( p6Lock );
P6DECLARE_CID( p6Condvar );
namespace {
// -> synch between different parts of the test code
typedef struct {
P6R::p6ILock* pLock;
P6R::P6BOOL bSignaled;
P6R::P6BOOL bDone;
P6R::P6BOOL bStart;
} KMIP_CTX;
class CKmipExample6
{
public:
P6CLASSMETHOD initialize();
P6CLASSMETHOD run( p6IDataStream *pStreamDebug );
CKmipExample6()
: m_port(0), m_compatMask(0), m_pHostName(NULL)
{ }
~CKmipExample6()
{
if (NULL != m_pHostName) m_cpStr->wstrfree( m_pHostName );
if (NULL != m_cpStoreInit) m_cpStoreInit->close();
}
// -> static entry point where p6IKMIPServer enters with an incoming KMIP Notify or Put request
static P6R::P6ERR processNotifyPutCallBack12( P6R::P6VOID* pContext, P6R::P6KMIP_INCOMING_REQUEST* pRequest );
KMIP_CTX m_ctx; // -> share the condvar across all the code
protected:
// -> connection related
P6CLASSMETHOD createSession( p6IKMIPClient* pClient, P6BOOL bWithCredentials );
P6CLASSMETHOD setPreferences( P6KMIP_PREF* pPrefs, P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy );
P6CLASSMETHOD destroyObject( p6IKMIPClient* pClient, P6NCSTR objectId, P6BOOL bFree );
// -> functions used often to extract a unique id returned by the server
P6CLASSMETHOD extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId );
P6R::P6BOOL isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id );
// -> supports crypto
P6CLASSMETHOD getRNG(P6R::p6IRandom **ppRandom);
P6CLASSMETHOD getIGenKeys( p6IGenKeys** ppGenKeys );
// -> used to initialize the keystore for SSL connections
P6CLASSMETHOD savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize );
P6CLASSMETHOD saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath );
P6CLASSMETHOD saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath );
P6CLASSMETHOD createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** pInit, p6IKeystore** pKeystore );
P6CLASSMETHOD loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey );
P6CLASSMETHOD loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert );
P6CLASSMETHOD createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey );
// -> process Notify & Put requests
P6CLASSMETHOD verifyPutCallBack12( P6R::P6KMIP_INCOMING_REQUEST* pRequest );
P6CLASSMETHOD verifyNotifyCallBack12( P6R::P6KMIP_INCOMING_REQUEST* pRequest );
// -> for self test only
P6CLASSMETHOD fakeClientPut( P6NCSTR* pKeyId );
P6CLASSMETHOD fakeClientNotify( P6NCSTR* pKeyId, const P6CHAR* pAttribName, P6UINT32 attribLength );
P6CLASSMETHOD sendMessage( p6IIoBuffer* pRequest, p6ITcpSocket* pSocket, P6INTERVAL tTimeout, P6UINT32& cBytesSent );
P6CLASSMETHOD createClientSocket( p6ITcpSocket** ppSocket );
P6CLASSMETHOD connectClientSocket( p6ITcpSocket* pSocket );
P6CLASSMETHOD createFakePutKeyTTLV( P6NCSTR* pKeyId, p6ICryptoKey* pKey, p6IIoBuffer*& pReqBuf );
P6CLASSMETHOD createFakeNotifyTTLV( P6NCSTR* pKeyId, const P6CHAR* pAtribValue, P6UINT32 attribLength, p6IIoBuffer*& pReqBuf );
p6ComPtr<p6ISafeString> m_cpStr; // -> generic string library
p6ComPtr<p6ICryptoKey> m_cpSignKey; // -> use the same keystore across all the unit tests
p6ComPtr<p6ISymmetricCrypto> m_cpCrypto; // -> used to encrypt the contents of the keystore
p6ComPtr<p6IRandom> m_cpRandom; // -> supports key generation
p6ComPtr<p6IKeystore> m_cpKeystore; // -> the KMIP client requires that a properly initialzed keystore is setup
p6ComPtr<p6IKeystoreInit> m_cpStoreInit; // -> keystore for client SSL connection
p6ComPtr<p6IIntervalTime> m_cpIT; // -> for protocol time outs
p6ComPtr<p6INetHelpers> m_cpHelper; // -> network utility functions
p6ComPtr<p6IIoBufferFactory> m_cpPool; // -> we must avoid using the system heap directly
p6ComPtr<p6ITime> m_cpTime; // -> for message timestamps
P6UINT32 m_port; // -> KMIP server port to connect to
P6UINT32 m_compatMask; // -> compatibility mask: 0 use TTLV message encoding, 1 TTLV over HTTP, 2 use XML message encoding, 4 use JSON message encoding
P6WCHAR* m_pHostName; // IP address or FQDN of KMIP server to connect to
P6NCSTR m_keyId; // -> unique Id arrives from a Put should match what the client receives from the server
};
// Create the key file (supplied by the server vendor) into a p6ICryptoKey object for SSL
//
P6CLASSMETHODIMPL CKmipExample6::createPKCS8Key( P6BSTR keyBuffer, P6SIZE keySize, p6ICryptoKey** ppNewKey )
{
P6ERR err = eOk;
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6CryptoKey, VALIDATECOMPTR( p6ICryptoKeyInit, cpKeyInit ))))
{
if (P6SUCCEEDED( err = cpKeyInit->initialize( P6CKF_NONE, m_cpRandom )))
{
if (P6SUCCEEDED( err = cpKeyInit->loadPKCS8Key( keyBuffer.pString, (P6UINT32) keyBuffer.length, (P6UINT32) keySize )))
{
err = cpKeyInit->queryInterface( VALIDATEIF( p6ICryptoKey, ppNewKey ));
}
}
}
return err;
}
// Load the KMIP server vendor supplied certificate file for SSL into a p6ICert object
//
P6CLASSMETHODIMPL CKmipExample6::loadCertificateFromFile( const P6WCHAR* pPath, p6ICert** ppNewCert )
{
P6ERR err = eOk;
if (P6FAILED( err = p6CreateCryptoInstance( CID_p6Cert, VALIDATECOMPTR( p6ICertInit, cpInit )))) return err;
if (P6FAILED( err = cpInit->initialize( P6CERT_NOFLAGS ))) return err;
if (P6SUCCEEDED( err = cpInit->loadCert( pPath ))) {
err = cpInit->queryInterface( VALIDATEIF( p6ICert, ppNewCert ));
}
return err;
}
// Load the PEM private key file (supplied by the server vendor) for SSL into a p6ICryptoKey object
//
P6CLASSMETHODIMPL CKmipExample6::loadKeyFromFile( const P6WCHAR* pwszFilename, P6UINT32 keySize, p6ICryptoKey** ppNewKey )
{
P6BSTR keyBuffer = {NULL, 0};
P6SIZE cBytesRead = 0;
P6ERR err = eNotFound;
if (NULL == pwszFilename) return eInvalidArg;
if (P6SUCCEEDED( err = p6CreateInstance( NULL, CID_p6UnbufferedFile, VALIDATECOMPTR( p6IUnbufferedFile, cpFile ))))
{
if (P6SUCCEEDED( err = cpFile->initialize()))
{
if (P6SUCCEEDED( err = cpFile->open( pwszFilename, P6FILE_READ|P6FILE_OPENEXISTING,P6FP_DEFAULT )))
{
P6FILEINFO info;
if ( P6SUCCEEDED( err = cpFile->stat( &info )))
{
if (info.m_cSize)
{
P6CHAR* pBuffer = new (std::nothrow) P6CHAR[(P6UINT32)info.m_cSize];
if ( pBuffer )
{
if (P6SUCCEEDED( err = cpFile->read( pBuffer,(P6UINT32)info.m_cSize, &cBytesRead )))
{
if (cBytesRead == info.m_cSize)
{
keyBuffer.length = cBytesRead;
keyBuffer.pString = (P6UCHAR*)pBuffer;
err = createPKCS8Key( keyBuffer, keySize, ppNewKey ); // or PKCS1?
}
}
}
else err = eIoError;
delete [] pBuffer;
}
else err = eNoMemory;
}
else {
err = eTooSmall;
}
cpFile->close();
}
}
}
return err;
}
// The contents of the keystore is protected by a key
P6CLASSMETHODIMPL CKmipExample6::getIGenKeys( p6IGenKeys** ppGenKeys )
{
P6ERR err = eOk;
*ppGenKeys = NULL;
err = p6CreateCryptoInstance( CID_p6GenKeys, VALIDATEIF( p6IGenKeys, ppGenKeys ));
if (P6SUCCEEDED(err))
{
err = (*ppGenKeys)->initialize( P6GENKEY_NOFLAGS, m_cpRandom );
if (P6FAILED( err ))
{
(*ppGenKeys)->release();
*ppGenKeys = NULL;
}
}
return err;
}
// Load in the certs we need to connect to the KMIP server into our new store
P6CLASSMETHODIMPL CKmipExample6::saveRootCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pFilePath )
{
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6WCHAR szHash[20] = {0};
P6SIZE nHash = 0;
P6ERR err = eOk;
p6ComPtr<p6ICert> cpNewCert;
P6ARG args[2];
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if ( P6SUCCEEDED( err = loadCertificateFromFile( confPath, cpNewCert.addressof() )))
{
// SSL library expects a specific namespace depending on how the cert is used
// We store the root cert by the has of its subject which during the SSL protocol is what OpenSSL looks for
if (P6SUCCEEDED( err = cpNewCert->getSubjectHash( &nHash )))
{
P6AI_SIZE_HEXL( args, nHash );
if (P6SUCCEEDED( err = m_cpStr->formatStringW( szHash, P6CNTOF(szHash), NULL, P6TEXT("%1$"), args, 1 )))
err = pKeystore->setCertificate( pNamespace, szHash, cpNewCert, NULL );
}
}
return err;
}
// KMIP requires both server and client certificates, we store the certificate under the host name of the KMIP server
P6CLASSMETHODIMPL CKmipExample6::saveClientCertInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath )
{
p6ComPtr<p6ICert> cpNewCert;
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if (P6SUCCEEDED( err = loadCertificateFromFile( confPath, cpNewCert.addressof() )))
{
err = pKeystore->setCertificate( pNamespace, pName, cpNewCert, NULL );
}
return err;
}
// Load in the keys we need to connect to the KMIP server into our new store, we store the private key under the host name of the KMIP server
P6CLASSMETHODIMPL CKmipExample6::savePrivKeyInStore( p6IKeystore* pKeystore, const P6WCHAR* pNamespace, const P6WCHAR* pName, const P6WCHAR* pFilePath, P6UINT32 keySize )
{
P6WCHAR confPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, confPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( confPath, P6CNTOF(confPath), pFilePath, NULL ))) return err;
if (P6SUCCEEDED( err = loadKeyFromFile( confPath, keySize, cpNewKey.addressof() )))
{
err = pKeystore->setKey( pNamespace, pName, cpNewKey, NULL, 0, NULL );
}
return err;
}
// Create a keystore and then load it with the information we need to connect to the KMIP server.
// P6R's SSL looks into the keystore for certificates and the private key when it starts a connection to the KMIP server.
//
P6CLASSMETHODIMPL CKmipExample6::createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, P6UINT32 privSize, p6IKeystoreInit** pInit, p6IKeystore** pKeystore )
{
P6ERR err = eOk;
*pInit = NULL;
*pKeystore = NULL;
// Create the keystore and fill it with vendor provided SSL certificates
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Keystore, VALIDATEIF( p6IKeystoreInit, pInit )))) return err;
err = (*pInit)->queryInterface( VALIDATEIF( p6IKeystore, pKeystore ));
if (P6FAILED( err = (*pInit)->initialize( P6KEYSTORE_NOFLAGS, m_cpCrypto, SH_SHA256, m_cpSignKey )))
{
if (NULL != (*pKeystore)) (*pKeystore)->release();
(*pKeystore) = NULL;
(*pInit)->release();
(*pInit) = NULL;
return err;
}
if (P6FAILED( err = (*pInit)->openSigned( NULL, pKeystoreName )))
{
if (NULL != (*pKeystore)) (*pKeystore)->release();
(*pKeystore) = NULL;
(*pInit)->release();
(*pInit) = NULL;
return err;
}
if (P6FAILED( err = saveRootCertInStore( (*pKeystore), P6TEXT("p6r.trustedroot"), rootPEM ))) return err;
if (P6FAILED( err = saveClientCertInStore( (*pKeystore), P6TEXT("p6r.clientauth"), m_pHostName, certPEM ))) return err;
if (P6FAILED( err = savePrivKeyInStore( (*pKeystore), P6TEXT("p6r.clientauth"), m_pHostName, privPEM, privSize ))) return err;
return eOk;
}
// Key generation requires an entropy source
P6CLASSMETHODIMPL CKmipExample6::getRNG( p6IRandom** ppRandom )
{
P6ERR err = eOk;
p6ComPtr<p6IRunningIface> cpRIT( IID_p6IRunningIface, &err );
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6EntropySource, VALIDATECOMPTR( p6IEntropySource, cpSource ))))
{
if (P6SUCCEEDED( err = cpSource->initialize( P6ENTROPY_HIGH )))
{
if (P6SUCCEEDED( err = p6CreateCryptoInstance( CID_p6Random, VALIDATECOMPTR( p6IRandomInit, cpInit ))))
{
if (P6SUCCEEDED( err = cpInit->initialize( P6RAND_NOFLAGS, cpSource ))) {
err = cpInit.queryInterface( VALIDATEIF( p6IRandom, ppRandom ));
}
}
}
}
return err;
}
// Replace the host name of "fqdn.com" to the fully qualified domain name of the server you wish to connect to.
P6R::P6ERR CKmipExample6::initialize()
{
P6ERR err = eOk;
P6WCHAR dbPath[P6MAXPATH+20] = {0};
// [1] Create a set of utility components
if (P6FAILED( err = p6GetRuntimeIface (VALIDATECOMPTR( p6ISafeString, m_cpStr )))) return err;
if (P6FAILED( err = p6GetRuntimeIface( VALIDATECOMPTR( p6INetHelpers, m_cpHelper )))) return err;
if (P6FAILED( err = p6CreateInstance(NULL, CID_p6Dir, VALIDATECOMPTR( p6IDir, cpDir )))) return err;
if (P6FAILED( err = cpDir->initialize())) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Time, VALIDATECOMPTR( p6ITime, m_cpTime )))) return err;
if (P6FAILED( err = m_cpTime->initialize())) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IntervalTime, VALIDATECOMPTR( p6IIntervalTime, m_cpIT )))) return err;
if (P6FAILED( err = m_cpIT->initialize())) return err;
if (P6FAILED(err = getRNG( m_cpRandom.addressof()))) return err;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6IoBufferFactory, VALIDATECOMPTR( p6IIoBufferFactory, m_cpPool )))) return err;
if (P6FAILED( err = m_cpPool->initialize( P6CTEXT("KMIP Notify Test"), 5000, 2, 5, P6IOBF_NOFLAGS ))) return err;
// [2] Right now configuration is hard coded, but could read out of a config file later
// -> m_compatMask is set to zero for TTLV message encoding , set to 2 for XML message encoding, and set to 4 for JSON message encoding
//
m_port = 5696;
m_compatMask = 0;
if (P6FAILED( err = m_cpStr->wstrdup( P6TEXT("fqdn.com"), &m_pHostName ))) return err;
// [3] Keystore needs a crypto key since it encrypts everything stored in it
if (P6SUCCEEDED( err = getIGenKeys( cpGenKey.addressof() )))
{
err = cpGenKey->genSymmetricKey( m_cpSignKey.addressof(), 256, P6FALSE );
if (P6SUCCEEDED( err )) {
err = cpGenKey->genSymmetricKey( cpKey.addressof(), 256, P6FALSE );
}
}
if (P6SUCCEEDED( err )) err = p6CreateCryptoInstance( CID_p6SymmetricCrypto, VALIDATECOMPTR( p6ISymmetricCrypto, m_cpCrypto ));
if (P6SUCCEEDED( err )) err = m_cpCrypto->initialize( P6SYM_NOPADDING, CIPHER_AES_CFB );
if (P6SUCCEEDED( err )) err = m_cpCrypto->setKey( cpKey );
// -> initialize the keystore with keys provided to us by the server vendor
dbPath[0] = P6CHAR('\0');
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( dbPath, P6CNTOF(dbPath), P6TEXT("/db/KMIP12_keystore"), NULL ))) return err;
cpDir->unlink( dbPath );
dbPath[0] = P6CHAR('\0');
if (P6FAILED( err = p6GetDirectory( P6D_DATA, dbPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( dbPath, P6CNTOF(dbPath), P6TEXT("/db/KMIP12_keystore.sig"), NULL ))) return err;
cpDir->unlink( dbPath );
err = createKeystore( P6TEXT("KMIP12_keystore"), P6TEXT("RootCert.pem"), P6TEXT("ClientCert.pem"), P6TEXT("ClientPrivate.pem"), 2048, m_cpStoreInit.addressof(), m_cpKeystore.addressof() );
return err;
}
// The client creates an SSL connection to the KMIP server and then makes its requests
// Configure the client on how it is supposed to behave (e.g., protocol version, use TTLV or XML or JSON, how to encode keys PKCSXXX).
//
P6CLASSMETHODIMPL CKmipExample6::setPreferences( P6KMIP_PREF* pPrefs, P6WCHAR* pLogDir, P6UINT16 asynchronous, P6UINT32 privateKeyEncoding, P6UINT32 publicKeyEncoding,
P6UINT32 symmetricKeyEncoding, P6UINT32 maxBufferSize, P6UINT32 connectTimeout, P6UINT32 sendTimeout, P6UINT32 receiveTimeout,
P6UINT32 initialBufCount, P6UINT32 growBufsBy )
{
m_cpStr->setMem( pPrefs, 0, sizeof( P6KMIP_PREF ));
pPrefs->pVersion = "1.2"; pPrefs->pSubdirectory = pLogDir;
pPrefs->asynchronous = asynchronous; pPrefs->maxBufferSize = maxBufferSize;
pPrefs->connectTimeout = connectTimeout; pPrefs->sendTimeout = sendTimeout;
pPrefs->receiveTimeout = receiveTimeout; pPrefs->initialBufCount = initialBufCount;
pPrefs->growBufsBy = growBufsBy; pPrefs->privateKeyEncoding = privateKeyEncoding;
pPrefs->publicKeyEncoding = publicKeyEncoding; pPrefs->symmetricKeyEncoding = symmetricKeyEncoding;
pPrefs->compatibility1 = m_compatMask;
return eOk;
}
// We just hard code the credentials (i.e., name and password) for a test if needed
//
P6CLASSMETHODIMPL CKmipExample6::createSession( p6IKMIPClient* pClient, P6BOOL bWithCredentials )
{
P6KMIP_CREDENTIAL credential;
P6ERR err = eOk;
// -> integration testing setups are not production so QuintessenceLabs has a non-secure setup
if (P6FAILED( err = pClient->setSSLOptions( NULL, (P6SSF_METHOD_TLS1 | P6SSF_SECURE_CLIENT | P6SSF_SECURE_CLIENT_AUTH | P6SSF_LOG_X509SUBJECTLOOKUPS | P6SSF_VRFY_DISABLEHOSTMATCH)))) return err;
m_cpStr->setMem( &credential, 0, sizeof( P6KMIP_CREDENTIAL ));
credential.type = 1;
credential.value.password.userName.pString = "Fred";
credential.value.password.userName.length = 4;
credential.value.password.password.pString = "password1";
credential.value.password.password.length = 9;
return pClient->open( m_pHostName, m_port, (bWithCredentials ? &credential : NULL));
}
// Just return the very first unique identifier from the enumerator
P6CLASSMETHODIMPL CKmipExample6::extractUniqueId( p6IKMIPStr* pEnum, P6NCSTR* pUniqueId )
{
P6CHAR* pGUID = NULL;
P6NCSTR buffer = { NULL, 0 };
P6ERR err = eOk;
pUniqueId->pString = NULL;
pUniqueId->length = 0;
err = pEnum->reset();
err = pEnum->next( &buffer );
if ( 0 < buffer.length )
{
if (NULL == (pGUID = new(std::nothrow) P6CHAR[buffer.length + 2])) return eNoMemory;
pGUID[0] = 0;
buffer.pString = pGUID;
buffer.length += 2;
if (P6FAILED( err = pEnum->next( &buffer ))) return err;
pUniqueId->pString = buffer.pString;
pUniqueId->length = buffer.length;
}
else err = eFail;
return err;
}
// Verify the string returned in pUniqueId matches that in the id parameter
P6R::P6BOOL CKmipExample6::isEqualId( p6IKMIPStr* pUniqueId, P6NCSTR id )
{
P6NCSTR enumStr = {NULL, 0};
P6INT32 match = -1;
P6ERR err = eOk;
if (NULL != pUniqueId)
{
if (P6FAILED( err = extractUniqueId( pUniqueId, &enumStr ))) return err;
if (enumStr.length == id.length)
{
err = m_cpStr->strcmp( enumStr.pString, id.pString, id.length, &match );
delete [] enumStr.pString;
}
}
return (0 == match);
}
// Used for self test only
P6CLASSMETHODIMPL CKmipExample6::sendMessage( p6IIoBuffer* pRequest, p6ITcpSocket* pSocket, P6INTERVAL tTimeout, P6UINT32& cBytesSent )
{
P6UINT8* pRawBuffer = NULL;
P6UINT32 dontCare = 0;
P6UINT32 bytesLeft = 0;
P6UINT32 bytesSent = 0;
P6UINT32 offset = 0;
P6ERR err = eOk;
if (P6FAILED( err = pRequest->getBufPtr( &pRawBuffer, &dontCare, &bytesLeft ))) return err;
while( P6SUCCEEDED(err) && 0 < bytesLeft )
{
err = pSocket->send( &pRawBuffer[offset], bytesLeft, &bytesSent, tTimeout );
bytesLeft -= bytesSent;
offset += bytesSent;
bytesSent = 0;
}
cBytesSent = offset;
return err;
}
// Used for self test only
P6CLASSMETHODIMPL CKmipExample6::createClientSocket( p6ITcpSocket** ppSocket )
{
P6ARG args;
P6NETADDR addr;
P6WCHAR szErr[128];
p6ITcpSocket* pSocket = NULL;
P6UINT32 cErr = P6CHARCNT(szErr);
P6ERR err = eOk;
err = p6CreateInstance( NULL, CID_p6TcpSocket, VALIDATEIF( p6ITcpSocket, &pSocket ));
if (P6SUCCEEDED( err ))
{
err = pSocket->initialize(P6AF_INET,P6SF_NONE);
if (P6SUCCEEDED( err ))
{
// Bind it to the local address INADDR_ANY and any port
m_cpHelper->strToNetAddr( "0.0.0.0:0", &addr );
err = pSocket->bind( &addr );
if (P6SUCCEEDED( err ))
{
*ppSocket = pSocket;
(*ppSocket)->addref();
}
}
pSocket->release();
}
return err;
}
// Used for self test only
P6CLASSMETHODIMPL CKmipExample6::connectClientSocket( p6ITcpSocket* pSocket )
{
P6NETADDR addr;
P6INTERVAL tTimeout = 0;
P6ERR err;
// -> TBD make the server port configurable
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
m_cpHelper->strToNetAddr( "127.0.0.1:65524", &addr );
return pSocket->connect( &addr, tTimeout );
}
// Used for self test only
P6CLASSMETHODIMPL CKmipExample6::fakeClientPut( P6NCSTR* pKeyId )
{
P6UINT8 szRecvBuf[5000];
p6ITcpSocket* pSocket = NULL;
p6IIoBuffer* pReqBuf = NULL;
p6ICryptoKey* pKey = NULL;
p6ICryptoKeySetMeta* pMeta = NULL;
p6IGenKeys* pGenKey = NULL;
P6INTERVAL tTimeout = 0;
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6ERR err = eOk;
// -> Generate a new key to put on the client - make it look exactly like the key generated in the run() function
// to simulate what would happen with a real KMIP server
if (P6SUCCEEDED( err = getIGenKeys( &pGenKey )))
{
err = pGenKey->genSymmetricKey( &pKey, 128, P6FALSE );
pGenKey->release();
if (P6FAILED(err)) return err;
}
if (P6SUCCEEDED( err = pKey->queryInterface( VALIDATEIF( p6ICryptoKeySetMeta, &pMeta ))))
{
pMeta->release();
}
if (P6SUCCEEDED( err = createClientSocket( &pSocket )))
{
if( P6SUCCEEDED( err = connectClientSocket( pSocket )))
{
if (P6SUCCEEDED( err = createFakePutKeyTTLV( pKeyId, pKey, pReqBuf )))
{
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
szRecvBuf[0] = 0;
err = sendMessage( pReqBuf, pSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err )) {
// TBD - verify the type of server response message received
err = pSocket->recv( &szRecvBuf[0], sizeof(szRecvBuf), &cBytesRead, tTimeout );
}
}
}
pSocket->shutdown( SDF_BOTH );
}
pSocket->release();
if (NULL != pKey) pKey->release();
return err;
}
// Used for self test only
P6CLASSMETHODIMPL CKmipExample6::fakeClientNotify( P6NCSTR* pKeyId, const P6CHAR* pAttribName, P6UINT32 attribLength )
{
P6UINT8 szRecvBuf[5000];
p6ITcpSocket* pSocket = NULL;
p6IIoBuffer* pReqBuf = NULL;
P6INTERVAL tTimeout = 0;
P6UINT32 cBytesSent = 0;
P6UINT32 cBytesRead = 0;
P6ERR err = eOk;
if (P6SUCCEEDED( err = createClientSocket( &pSocket )))
{
if (P6SUCCEEDED( err = connectClientSocket( pSocket )))
{
if (P6SUCCEEDED( err = createFakeNotifyTTLV( pKeyId, pAttribName, attribLength, pReqBuf )))
{
m_cpIT->milliSecondsToInterval( 10000, &tTimeout );
szRecvBuf[0] = 0;
err = sendMessage( pReqBuf, pSocket, tTimeout, cBytesSent );
pReqBuf->release();
pReqBuf = NULL;
if (P6SUCCEEDED( err )) {
// TBD - verify the type of server response message received
err = pSocket->recv( &szRecvBuf[0], sizeof(szRecvBuf), &cBytesRead, tTimeout );
}
}
}
pSocket->shutdown( SDF_BOTH );
}
pSocket->release();
return err;
}
// Used for self test only
// This code uses a KMIP encoder to generate a notify message that would have come from the KMIP server, we send this to the p6IKMIPServer to
// simulate an incoming notify request. It generates the Put message defined in test case TC_NP_2_12 in the KMIP interop test document.
P6CLASSMETHODIMPL CKmipExample6::createFakePutKeyTTLV( P6NCSTR* pKeyId, p6ICryptoKey* pKey, p6IIoBuffer*& pReqBuf )
{
P6KMIP_ENCODER_PREF encodePrefs = {0, 0, 0};
P6KMIP_PUT putParams;
P6KMIP_ATTRIBUTE itemList[15];
p6IKMIPRequest* pMsg = NULL;
P6UINT32 keyFormatType = KMIP_KEYFORMAT_RAW;
P6TIME tStamp = 0;
P6ERR err = eOk;
const P6UCHAR digest[] = { 0x53,0x65,0x63,0x72,0x65,0x82,0x74,0x50,0x02,0x61,0x73,0x73,0x77,0x6F,0x72,0x64 };
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPEncoder, VALIDATECOMPTR( p6IKMIPEncoder, cpEncoder )))) return err;
if (P6FAILED( err = cpEncoder->initialize( P6KMIPENCODER_NOFLAGS, P6KMIP_VERSION_12, m_cpPool, &encodePrefs ))) return err;
if (P6FAILED( err = cpEncoder->queryInterface( VALIDATEIF( p6IKMIPRequest, &pMsg )))) return err;
if (P6FAILED( err = cpEncoder->queryInterface( VALIDATEIF( p6IKMIPRequest, &pMsg )))) return err;
err = m_cpTime->now( &tStamp );
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS ));
m_cpStr->setMem( &putParams, 0, sizeof( P6KMIP_PUT ));
itemList[0].type = KMIP_ATTRIB_EXTENSION;
itemList[0].index = 0;
itemList[0].value.extension.xName.pString = "x-ID";
itemList[0].value.extension.xName.length = 4;
itemList[0].value.extension.xValue.vText.pString = "TC-NP-2-12";
itemList[0].value.extension.xValue.vText.length = 10;
itemList[1].index = 0;
itemList[1].value.uniqueIdentifier.pString = pKeyId->pString;
itemList[1].value.uniqueIdentifier.length = pKeyId->length;
itemList[2].type = KMIP_ATTRIB_OBJECTTYPE;
itemList[2].index = 0;
itemList[3].index = 0;
itemList[3].value.cryptoAlgorithm = KMIP_AES;
itemList[4].index = 0;
itemList[4].value.cryptoLength = 128;
itemList[5].index = 0;
itemList[6].type = KMIP_ATTRIB_DIGEST;
itemList[6].index = 0;
itemList[6].value.digest.value.pString = digest;
itemList[6].value.digest.value.length = sizeof( digest );
itemList[7].type = KMIP_ATTRIB_FRESH;
itemList[7].index = 0;
itemList[7].value.fresh = P6TRUE;
itemList[8].type = KMIP_ATTRIB_INITIALDATE;
itemList[8].index = 0;
itemList[8].value.initialDate = tStamp; // <-- last change date = "NOW"
itemList[9].index = 0;
itemList[9].value.lastChangeDate = tStamp; // <-- last change date = "NOW"
itemList[10].type = KMIP_ATTRIB_LEASETIME;
itemList[10].index = 0;
itemList[10].value.leaseTime = 3600;
itemList[11].index = 0;
itemList[11].value.lastChangeDate = tStamp; // <-- last change date = "NOW"
itemList[12].type = KMIP_ATTRIB_STATE;
itemList[12].index = 0;
itemList[12].value.state = KMIP_STATE_PREACTIVE;
putParams.uniqueIdentifier.pString = pKeyId->pString;
putParams.uniqueIdentifier.length = pKeyId->length;
putParams.function = KMIP_PUT_NEW;
putParams.pAttributeList = itemList;
putParams.itemCount = 13;
// -> build the TTLV version of a KMIP message using the encoder
err = pMsg->startRequestMsg( params );
err = pMsg->addPutKeyRequest( putParams, pKey, &keyFormatType, NULL );
err = pMsg->endRequestMsg();
err = cpEncoder->getBufPtr( &pReqBuf );
if (NULL != pMsg) pMsg->release();
return err;
}
// Used for self test only
// This code uses a KMIP encoder to generate a notify message that would have come from the KMIP server, we send this to the p6IKMIPServer to
// simulate an incoming notify request. It generates the Notify messages defined in test case TC_NP_2_12 in the KMIP interop test document.
P6CLASSMETHODIMPL CKmipExample6::createFakeNotifyTTLV( P6NCSTR* pKeyId, const P6CHAR* pAtribValue, P6UINT32 attribLength, p6IIoBuffer*& pReqBuf )
{
P6KMIP_ENCODER_PREF encodePrefs = {0, 0, 0};
P6KMIP_ATTRIBUTE itemList[5];
P6BOOL ignore[5];
P6NCSTR uniqueId;
p6IKMIPRequest* pMsg = NULL;
P6TIME tStamp = 0;
P6UINT32 i = 0;
P6ERR err = eOk;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPEncoder, VALIDATECOMPTR( p6IKMIPEncoder, cpEncoder )))) return err;
if (P6FAILED( err = cpEncoder->initialize( P6KMIPENCODER_NOFLAGS, P6KMIP_VERSION_12, m_cpPool, &encodePrefs ))) return err;
if (P6FAILED( err = cpEncoder->queryInterface( VALIDATEIF( p6IKMIPRequest, &pMsg )))) return err;
err = m_cpTime->now( &tStamp );
m_cpStr->setMem( &params, 0, sizeof( P6KMIP_REQUESTPARAMS ));
uniqueId.pString = pKeyId->pString;
uniqueId.length = pKeyId->length;
if (NULL != pAtribValue)
{
itemList[i].type = KMIP_ATTRIB_EXTENSION;
itemList[i].index = 0;
itemList[i].value.extension.xName.pString = "x-provider";
itemList[i].value.extension.xName.length = 10;
itemList[i].value.extension.xValue.vText.pString = pAtribValue;
itemList[i].value.extension.xValue.vText.length = attribLength;
i++;
}
itemList[i].index = 0;
itemList[i].value.lastChangeDate = tStamp; // <-- last change date = "NOW"
i++;
if (NULL == pAtribValue)
{
itemList[i].type = KMIP_ATTRIB_STATE;
itemList[i].index = 0;
i++;
}
// -> used to indicate that x-owner is to be sent with no value
ignore[0] = P6FALSE; ignore[1] = P6FALSE;
// -> build the TTLV version of a KMIP message using the encoder
err = pMsg->startRequestMsg( params );
err = pMsg->addNotify( uniqueId, i, itemList, ignore, NULL );
err = pMsg->endRequestMsg();
err = cpEncoder->getBufPtr( &pReqBuf );
if (NULL != pMsg) pMsg->release();
return err;
}
// An action we seem to do a lot of during testing so we just place it once in a function
P6CLASSMETHODIMPL CKmipExample6::destroyObject( p6IKMIPClient* pClient, P6NCSTR objectId, P6BOOL bFree )
{
P6KMIP_RESULT resultCodes;
p6IKMIPStr* pUniqueId = NULL;
P6BOOL bMatch = P6FALSE;
P6ERR err = eOk;
if (NULL != objectId.pString)
{
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
err = pClient->destroyObject( objectId, NULL, &pUniqueId, &resultCodes );
if ( P6FAILED( err )) {
printf("\ncall to destroyObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error - destroy object\n");
}
bMatch = isEqualId( pUniqueId, objectId );
if (!bMatch) {
printf("\nKMIP server to destroy object with wrong object uniqueId\n");
}
if (bFree) delete [] objectId.pString;
// -> did we fail trying to destroy the object?
if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) return eFail;
}
return err;
}
// static entry point
// Notes:
// (1) We use the m_ctx structure here ONLY becuase we we are using our client to force the Put/Notify actions to occur.
// Thus we have to synchronize with the CKmipExample6::run code not to delete a created key before we get to process it here.
// This synchronization WOULD NOT be needed in a real application.
//
// (2) Upon entering this call back the code is running in a thread provided by the p6IKMIPServer component. Note that this
// callback must be able to handle multiple threads entering at the same time since the p6IKMIPServer component runs multiple threads
// can can be handing multiple Put/Notify requests at the same time.
//
// (3) Since the life time of passed in components (items in pRequest) are determined by the caller (i.e., p6IKMIPServer component) thus
// this code should not release any objects unless it does an addref first to keep the objects around for some purpose.
// See https://www.p6r.com/articles/2014/04/27/p6com-reference-counting-a-primer/ rule #4.
//
P6R::P6ERR CKmipExample6::processNotifyPutCallBack12( P6VOID* pContext, P6KMIP_INCOMING_REQUEST* pRequest )
{
P6ERR err = eFail;
CKmipExample6 *pThis = reinterpret_cast<CKmipExample6*>( pContext );
if (NULL != pThis)
{
if (KMIP_OP_PUT == pRequest->type) err = pThis->verifyPutCallBack12( pRequest );
else if (KMIP_OP_NOTIFY == pRequest->type) err = pThis->verifyNotifyCallBack12( pRequest );
}
// -> signal run() function that we are done processing the request
pThis->m_ctx.pLock->lock();
pThis->m_ctx.bDone = P6TRUE;
pThis->m_ctx.pCV->notify();
pThis->m_ctx.pLock->unlock();
return err;
}
// Notes:
// (1) Here the application gets the Notify message parsed into the P6KMIP_INCOMING_REQUEST structure.
// At this point it is application dependent what it does with that information. The code in this
// function just demonstrates how to get to the data in the Notify but not what to do with it since
// that is application dependent.
//
P6CLASSMETHODIMPL CKmipExample6::verifyNotifyCallBack12( P6KMIP_INCOMING_REQUEST* pRequest )
{
P6CHAR nameBuffer[500];
P6CHAR tempBuffer[500];
P6NCSTR oneId = {NULL, 0};
P6UINT32 attribType = 0;
P6INT32 match = -1;
P6ERR err = eOk;
if (NULL != pRequest->value.notify.pUniqueId)
{
// -> unique id of the object the notify is for
err = extractUniqueId( pRequest->value.notify.pUniqueId, &oneId );
if (NULL != oneId.pString) delete [] oneId.pString;
}
if ( NULL != pRequest->value.notify.pAttribute )
{
pRequest->value.notify.pAttribute->reset();
while (P6SUCCEEDED( pRequest->value.notify.pAttribute->next( &attribType )))
{
m_cpStr->setMem( &objAttribute, 0, sizeof( P6KMIP_OBJECT_ATTRIBUTE ));
switch( attribType ) {
nameBuffer[0] = 0; tempBuffer[0] = 0;
objAttribute.value.extension.xName.pString = nameBuffer;
objAttribute.value.extension.xName.length = 500;
objAttribute.value.extension.xValue.vText.pString = tempBuffer;
objAttribute.value.extension.xValue.vText.length = 500;
err = pRequest->value.notify.pAttribute->getValue( &objAttribute );
//printf( "\nextension [%s] [%s] %d %x\n", nameBuffer, tempBuffer, objAttribute.value.extension.xValue.vText.length, err );
// -> values we are expecting
match = -1;
if (10 == objAttribute.value.extension.xName.length) err = m_cpStr->strncmp( nameBuffer, "x-provider", 10, &match );
match = -1;
if (7 == objAttribute.value.extension.xValue.vText.length) err = m_cpStr->strncmp( tempBuffer, "unknown", 7, &match );
else if (11 == objAttribute.value.extension.xValue.vText.length) err = m_cpStr->strncmp( tempBuffer, "third party", 11, &match );
break;
err = pRequest->value.notify.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.lastChangeDate );
break;
err = pRequest->value.notify.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.state );
break;
default:
// -> unexpected attribute?
break;
}
}
}
// -> if we return anything other than eOk then an error condition is returned back to the KMIP server (e.g., eFormatError)
// -> see p6IKMIPServer.h for documentation on error codes to return
return err;
}
// Notes:
// (1) Here the application gets a Put message parsed into the P6KMIP_INCOMING_REQUEST structure.
// KMIP Put operations pass a copy of a managed object (e.g., a key, a certificate) that is newly
// created or modified to the application. The code in this function just demonstrates how to get
// to the data in the Put but not what to do with it since that is application dependent.
//
P6CLASSMETHODIMPL CKmipExample6::verifyPutCallBack12( P6KMIP_INCOMING_REQUEST* pRequest )
{
P6CHAR nameBuffer[500];
P6CHAR tempBuffer[500];
P6UCHAR numBuffer[500];
P6NCSTR oneId = {NULL, 0};
P6UINT32 attribType = 0;
P6UINT32 length = 0;
P6ERR err = eOk;
// -> the key's unique id is also passed as an attribute below
if ( NULL != pRequest->value.put.pUniqueId )
{
err = extractUniqueId( pRequest->value.put.pUniqueId, &oneId );
if (NULL != oneId.pString) delete [] oneId.pString;
}
// -> what type of put function is it?
// -> value is in pRequest->value.put.putFunction, can be KMIP_PUT_NEW
// -> check the type of the managed object that came in the put, here we expected a symmetric key but it can be a certificate, a public/private key etc.
// -> look at p6kmip.h for the constant KMIP_OBJECT_SYMMETRICKEY to see all the types of managed objects
{
P6UUID keyGuid = {0};
P6INT32 keySize = 0;
P6INT32 keyVersion = 0;
// -> note the uniqueId in the managed object structure is NULL for put operations
// -> verify how our object has been stamped with the key
err = pRequest->value.put.pushObject.value.symmetricKey.value.pKey->getInfo( &keyClass, &keyType, &keyGuid, &keySize, &keyVersion );
// -> key meta data is in: pRequest->value.put.pushObject.value.symmetricKey.cryptoAlgorithm
// -> key meta data in in: pRequest->value.put.pushObject.value.symmetricKey.cryptoLength
// -> to get the key material
err = pRequest->value.put.pushObject.value.symmetricKey.value.pKey->getSymetricKey( numBuffer, length, &length ); // -> 128 bits is 16 bytes
}
if ( NULL != pRequest->value.put.pAttribute )
{
pRequest->value.put.pAttribute->reset();
while (P6SUCCEEDED( pRequest->value.put.pAttribute->next( &attribType )))
{
m_cpStr->setMem( &objAttribute, 0, sizeof( P6KMIP_OBJECT_ATTRIBUTE ));
switch( attribType ) {
nameBuffer[0] = 0; tempBuffer[0] = 0;
objAttribute.value.extension.xName.pString = nameBuffer;
objAttribute.value.extension.xName.length = 500;
objAttribute.value.extension.xValue.vText.pString = tempBuffer;
objAttribute.value.extension.xValue.vText.length = 500;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
break;
nameBuffer[0] = 0;
objAttribute.value.uniqueIdentifier.pString = nameBuffer;
objAttribute.value.uniqueIdentifier.length = 500;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
break;
numBuffer[0] = 0;
objAttribute.value.digest.value.pString = numBuffer;
objAttribute.value.digest.value.length = 500;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.digest.value.length, objAttribute.value.digest.hashingAlgorithm, objAttribute.value.digest....
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.objectType
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.cryptoAlgorithm
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.cryptoLength
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.leaseTime
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.fresh
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.lastChangeDate, or objAttribute.value.initialDate, or objAttribute.value.origCreationDate
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.cryptoUsageMask
break;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.state
break;
default:
// -> unexpected attribute?
break;
}
}
}
// -> if we return anything other than eOk then an error condition is returned back to the KMIP server (e.g., eFormatError)
// -> see p6IKMIPServer.h for documentation on error codes to return
return err;
}
// Notes:
// (1) For testing purposes this code has a self-test switch (i.e., bSelfTest) where it sends itself the Put/notify
// messages. For the KMIP server to send us such messages it has to be properly configured. After doing so
// then re-run this test turning off the self-test switch.
//
// Main client function
//
P6CLASSMETHODIMPL CKmipExample6::run( p6IDataStream *pStreamDebug )
{
P6KMIP_PREF preferences;
P6KMIP_RESULT resultCodes;
P6KMIP_ATTRIBPARAMS attribParams;
P6KMIP_ATTRIBUTE attributeList[6];
p6ICryptoKey* pKey = NULL;
p6ICryptoKeySetMeta* pMeta = NULL;
p6IGenKeys* pGenKey = NULL;
P6BOOL bSelfTest = P6TRUE; // ** self-test switch
P6ERR err = eOk;
// [1] Initialize
m_keyId.pString = NULL;
m_keyId.length = 0;
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPServer, VALIDATECOMPTR( p6IKMIPServer, cpServer )))) return err;
if (P6FAILED( err = cpServer->initialize( (P6KMIPFLG_TRACE_MSGS | P6KMIPFLG_TRACE_FORMATKMIPXML), 0, 0, NULL, processNotifyPutCallBack12, this ))) return err;
if (P6FAILED( err = cpServer->start())) return err;
// -> note that here we will trigger the KMIP server to send us Put/Notify operations via actions made by the client
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6KMIPClient, VALIDATECOMPTR( p6IKMIPClient, cpClient )))) return err;
setPreferences( &preferences, P6TEXT("TC_NP_2_12"), 0, 0, 0, 0, 5000, 2000, 2000, 120000, 2, 2 );
err = cpClient->initialize( (P6KMIPFLG_TRACE_MSGS | P6KMIPFLG_TRACE_FORMATKMIPXML), m_cpKeystore, preferences );
// -> use a CondVar for the Put callback to notify this code when it has been called and is done running so this code can proceed
// -> if we don't use smart pointers then we have to make sure we free objects we have created
m_cpStr->setMem( &m_ctx, 0, sizeof( KMIP_CTX ));
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Lock, VALIDATEIF( p6ILock, &m_ctx.pLock )))) return err;
if (P6FAILED( err = m_ctx.pLock->initialize())) { m_ctx.pLock->release(); return err; }
if (P6FAILED( err = p6CreateInstance( NULL, CID_p6Condvar, VALIDATEIF( p6ICondvar, &m_ctx.pCV )))) { m_ctx.pLock->release(); return err; }
if (P6FAILED( err = m_ctx.pCV->initialize( m_ctx.pLock ))) {
m_ctx.pCV->release();
m_ctx.pLock->release();
return err;
}
// [2] Open a connection to the KMIP server via SSL
if (P6FAILED( err = createSession( cpClient, P6FALSE ))) return err;
// [3] Generate the key and set it's meta data
if (P6SUCCEEDED( err = getIGenKeys( &pGenKey )))
{
err = pGenKey->genSymmetricKey( &pKey, 128, P6FALSE );
pGenKey->release();
if (P6FAILED(err)) return err;
}
if (P6SUCCEEDED( err = pKey->queryInterface( VALIDATEIF( p6ICryptoKeySetMeta, &pMeta ))))
{
pMeta->release();
}
// [4] Register a key we generate to be saved inside the KMIP server which should force the KMIP server to send a Put to us
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &keyParams, 0, sizeof( P6KMIP_REGKEYPARAMS ));
m_cpStr->setMem( &newKey, 0, sizeof( P6KMIP_NEWOBJECT ));
attributeList[0].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
attributeList[0].index = 0;
attributeList[1].type = KMIP_ATTRIB_EXTENSION;
attributeList[1].index = 0;
attributeList[1].value.extension.xName.pString = "x-ID";
attributeList[1].value.extension.xName.length = 4;
attributeList[1].value.extension.xValue.vText.pString = "TC-NP-2-12";
attributeList[1].value.extension.xValue.vText.length = 10;
keyParams.attributes.attribCount = 2;
keyParams.attributes.pAttributeList = attributeList;
keyParams.type = 1;
keyParams.value.pKey = pKey;
err = cpClient->registerKeyObject( keyParams, &newKey, &resultCodes );
if ( P6FAILED( err )) {
printf("\ncall to registerKeyObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error - register object\n");
}
pKey->release();
if (NULL != newKey.pUniqueId) {
if (P6FAILED( err = extractUniqueId( newKey.pUniqueId, &m_keyId ))) return err;
}
// -> SELF-TEST send a fake PUT to our waiting server about the new key
if (bSelfTest) {
err = fakeClientPut( &m_keyId );
}
// -> wait for thread to let us know it's done
m_ctx.pLock->lock();
while( P6FALSE == m_ctx.bDone ) {
m_ctx.pCV->wait();
}
m_ctx.bDone = P6FALSE;
m_ctx.pLock->unlock();
// [5] Add an attribute on this new registered key which should force the KMIP server to send a Notify to us
m_cpStr->setMem( &resultCodes, 0, sizeof( P6KMIP_RESULT ));
m_cpStr->setMem( &attribParams, 0, sizeof( P6KMIP_ATTRIBPARAMS ));
m_cpStr->setMem( &getResult, 0, sizeof( P6KMIP_ATTRIBRESULT ));
attribParams.uniqueId = m_keyId;
attribParams.attribute.index = 0;
attribParams.attribute.value.extension.xName.pString = "x-provider";
attribParams.attribute.value.extension.xName.length = 10;
attribParams.attribute.value.extension.xValue.vText.pString = "unknown";
err = cpClient->addAttributeObject( attribParams, &getResult, &resultCodes );
if ( P6FAILED( err )) {
printf("\ncall to addAttributeObject has failed %x\n", err );
}
else if (KMIP_RESULT_SUCCESS != resultCodes.resultStatus) {
printf("\nKMIP server returned an error - add attribute\n");
}
// -> the KMIP server returns the same data we sent us, the SDK parses and returns it to the client which can verify it or just delete it
if (NULL != getResult.pAttribute) getResult.pAttribute->release();
// -> SELF-TEST send a fake notify to our waiting server with info about the modified attribute
if (bSelfTest) {
err = fakeClientNotify( &m_keyId, "unknown", 7 );
}
// -> wait for thread to let us know it's done
m_ctx.pLock->lock();
while( P6FALSE == m_ctx.bDone ) {
m_ctx.pCV->wait();
}
m_ctx.bDone = P6FALSE;
m_ctx.pLock->unlock();
// [6] Notify operation should arrive to our server here due to the destroy
destroyObject( cpClient, m_keyId, P6FALSE );
// -> SELF-TEST send a fake notify to our waiting server with info about the destroyed key
if (bSelfTest) {
err = fakeClientNotify( &m_keyId, NULL, 0 );
}
// -> wait for thread to let us know it's done
m_ctx.pLock->lock();
while( P6FALSE == m_ctx.bDone ) {
m_ctx.pCV->wait();
}
m_ctx.bDone = P6FALSE;
m_ctx.pLock->unlock();
// -> clean up test over
err = cpClient->close();
err = cpServer->stop();
if (NULL != m_keyId.pString) delete [] m_keyId.pString;
if (NULL != m_ctx.pCV ) m_ctx.pCV->release();
if (NULL != m_ctx.pLock) m_ctx.pLock->release();
return err;
}
P6VOID KMIP_TC_NP_2_12( p6IDataStream* pDataStream )
{
P6ERR err = eOk;
CKmipExample6 example;
P6CHAR szTmp[32];
if (P6SUCCEEDED( err = example.initialize())) {
err = example.run( pDataStream );
}
printf( "KMIP client result: [ %s ]\n", p6ErrToStr(err, &szTmp[0], P6CHARCNT(szTmp)) );
}
} // namespace
// p6InitializeLoader() must be called before anything else can run.
//
int main(int argc,char *argv[])
{
P6ERR err = eOk;
if ( P6SUCCEEDED( err = P6EXAMPLES::CConsoleStream::createInstance( NULL, VALIDATECOMPTR( p6IDataStream, cpDataStream ))))
{
if ( P6SUCCEEDED( err = p6InitializeLoader( cpDataStream, 9, P6SCLF_ALLLOG )))
{
KMIP_TC_NP_2_12( cpDataStream );
}
else printf("ERROR: Failed to initialize the loader [ %x ]\n", err );
}
else printf( "ERROR: Failed to create CConsoleStream [ %x ]\n", err );
return err;
}