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 "p6com.h"
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6regex.h" // Narrow string regular expression interfaces
#include "p6wregex.h" // Wide string regular expression interfaces
#include "p6split.h" // Narrow split and and explode using regex
#include "p6wsplit.h" // Wide split and and explode using wregex
#include "p6jsonreader.h" // Streaming JSON parser interfaces
#include "p6loader.h" // Standalone Component Loader definitions
#include "p6sax2contenthandler.h" // SAX2 content handler definitions
#include "p6sax2xmlreader.h" // Definitions for the main SAX2 interface
#include "p6sax2errorhandler.h" // SAX2 error handler definitions
#include "p6domxml.h" // DOM Parser interface definitions
#include "p6xmlnode.h" // XML and JSON node definitions
#include "p6xpathexpression.h" // XPATH 2.0 expression compilation interface definitions
#include "p6domnodeset.h" // DOM node enumeration interface definitions
#include "p6domnodesetsort.h" // DOM Node sorting interface definitions
#include "p6keystore.h"
#include "p6kmip.h"
#include "p6kmipclient.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, const 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 keystoreAddRootCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszCertificateFile );
P6CLASSMETHOD keystoreAddClientCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszHostname, const P6WCHAR* pszPrivateKeyFile, const P6WCHAR* pszCertificateFile );
P6CLASSMETHOD createKeystore( const P6WCHAR* pKeystoreName, const P6WCHAR* rootPEM, const P6WCHAR* certPEM, const P6WCHAR* privPEM, p6IKeystoreInit** pInit, p6IKeystore** pKeystore );
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;
}
// 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;
}
P6CLASSMETHODIMPL CKmipExample6::keystoreAddRootCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszCertificateFile )
{
P6WCHAR certPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (!pKeystore || !pszCertificateFile) return eInvalidArg;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, certPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), pszCertificateFile, NULL ))) return err;
p6ComPtr<p6IKeystoreSSL> cpSSLHelp( pKeystore, IID_p6IKeystoreSSL, &err );
if (P6SUCCEEDED( err )) {
err = cpSSLHelp->importTrustedRootCertFromPEMFile( certPath, NULL );
}
return err;
}
P6CLASSMETHODIMPL CKmipExample6::keystoreAddClientCertFromFile( p6IKeystore* pKeystore, const P6WCHAR* pszHostname, const P6WCHAR* pszPrivateKeyFile, const P6WCHAR* pszCertificateFile )
{
P6WCHAR certPath[P6MAXPATH+2] = {0};
P6WCHAR keyPath[P6MAXPATH+2] = {0};
P6ERR err = eOk;
if (!pKeystore || !pszHostname || !pszPrivateKeyFile || !pszCertificateFile ) return eInvalidArg;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, keyPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( keyPath, P6CNTOF(keyPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( keyPath, P6CNTOF(keyPath), pszPrivateKeyFile, NULL ))) return err;
if (P6FAILED( err = p6GetDirectory( P6D_CONF, certPath, P6MAXPATH, NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), P6TEXT("/"), NULL ))) return err;
if (P6FAILED( err = m_cpStr->wstrlcat( certPath, P6CNTOF(certPath), pszCertificateFile, NULL ))) return err;
p6ComPtr<p6IKeystoreSSL> cpSSLHelp( pKeystore, IID_p6IKeystoreSSL, &err );
if (P6SUCCEEDED( err )) {
err = cpSSLHelp->importCredentialsPEM( P6TRUE, pszHostname, keyPath, certPath, NULL, 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, 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 = keystoreAddRootCertFromFile( (*pKeystore), rootPEM ))) return err;
if (P6FAILED( err = keystoreAddClientCertFromFile( (*pKeystore), m_pHostName, privPEM, certPEM ))) 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"), 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, const 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 )
{
P6NETADDR addr;
p6ITcpSocket* pSocket = NULL;
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;
// -> 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[0].value.extension.xType = KMIP_TYPE_TEXTSTRING;
itemList[1].type = KMIP_ATTRIB_UNIQUEIDENTIFIER;
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[2].value.objectType = KMIP_OBJECT_SYMMETRICKEY;
itemList[3].type = KMIP_ATTRIB_CRYPTOALGORITHM;
itemList[3].index = 0;
itemList[3].value.cryptoAlgorithm = KMIP_AES;
itemList[4].type = KMIP_ATTRIB_CRYPTOLENGTH;
itemList[4].index = 0;
itemList[4].value.cryptoLength = 128;
itemList[5].type = KMIP_ATTRIB_CRYPTOUSAGEMASK;
itemList[5].index = 0;
itemList[5].value.cryptoUsageMask = KMIP_USE_ENCRYPT | KMIP_USE_DECRYPT;
itemList[6].type = KMIP_ATTRIB_DIGEST;
itemList[6].index = 0;
itemList[6].value.digest.hashingAlgorithm = KMIP_SHA256;
itemList[6].value.digest.value.pString = digest;
itemList[6].value.digest.value.length = sizeof( digest );
itemList[6].value.digest.keyFormatType = KMIP_KEYFORMAT_RAW;
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].type = KMIP_ATTRIB_LASTCHANGEDATE;
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].type = KMIP_ATTRIB_ORIGCREATIONDATE;
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;
itemList[i].value.extension.xType = KMIP_TYPE_TEXTSTRING;
i++;
}
itemList[i].type = KMIP_ATTRIB_LASTCHANGEDATE;
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;
itemList[i].value.state = KMIP_STATE_DESTROYED;
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 ) {
case KMIP_ATTRIB_EXTENSION:
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;
case KMIP_ATTRIB_LASTCHANGEDATE:
err = pRequest->value.notify.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.lastChangeDate );
break;
case KMIP_ATTRIB_STATE:
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
if ( KMIP_OBJECT_SYMMETRICKEY == pRequest->value.put.pushObject.type )
{
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 ) {
case KMIP_ATTRIB_EXTENSION:
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;
case KMIP_ATTRIB_UNIQUEIDENTIFIER:
nameBuffer[0] = 0;
objAttribute.value.uniqueIdentifier.pString = nameBuffer;
objAttribute.value.uniqueIdentifier.length = 500;
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
break;
case KMIP_ATTRIB_DIGEST:
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;
case KMIP_ATTRIB_OBJECTTYPE:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.objectType
break;
case KMIP_ATTRIB_CRYPTOALGORITHM:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.cryptoAlgorithm
break;
case KMIP_ATTRIB_CRYPTOLENGTH:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.cryptoLength
break;
case KMIP_ATTRIB_LEASETIME:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.leaseTime
break;
case KMIP_ATTRIB_FRESH:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.fresh
break;
case KMIP_ATTRIB_INITIALDATE:
case KMIP_ATTRIB_LASTCHANGEDATE:
case KMIP_ATTRIB_ORIGCREATIONDATE:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.lastChangeDate, or objAttribute.value.initialDate, or objAttribute.value.origCreationDate
break;
case KMIP_ATTRIB_CRYPTOUSAGEMASK:
err = pRequest->value.put.pAttribute->getValue( &objAttribute );
// -> value in objAttribute.value.cryptoUsageMask
break;
case KMIP_ATTRIB_STATE:
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[0].value.cryptoUsageMask = KMIP_USE_ENCRYPT;
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;
attributeList[1].value.extension.xType = KMIP_TYPE_TEXTSTRING;
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.type = KMIP_ATTRIB_EXTENSION;
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";
attribParams.attribute.value.extension.xType = KMIP_TYPE_TEXTSTRING;
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;
}