blob: 1ca18a2b6f1b8981b7655a50076579e367c66be7 [file] [log] [blame]
/***************************************************************************************************************************************************************************************************************************
*
This file is part of CertiVox M-Pin Client and Server Libraries. *
The CertiVox M-Pin Client and Server Libraries provide developers with an extensive and efficient set of strong authentication and cryptographic functions. *
For further information about its features and functionalities please refer to http://www.certivox.com *
The CertiVox M-Pin Client and Server Libraries are free software: you can redistribute it and/or modify it under the terms of the BSD 3-Clause License http://opensource.org/licenses/BSD-3-Clause as stated below. *
The CertiVox M-Pin Client and Server Libraries are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
Note that CertiVox Ltd issues a patent grant for use of this software under specific terms and conditions, which you can find here: http://certivox.com/about-certivox/patents/ *
Copyright (c) 2013, CertiVox UK Ltd *
All rights reserved. *
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: *
� Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. *
� Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. *
� Neither the name of CertiVox UK Ltd nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. *
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, *
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS *
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE *
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
*
***************************************************************************************************************************************************************************************************************************/
/*! \file CvEccsi.cpp
\brief C++ wrapper for the MIRACL ECCSI functionality
*- Project : SkyKey SDK
*- Authors : Mony Aladjem
*- Company : Certivox
*- Created : January 28, 2013, 1:59 PM
*- Last update : February 15, 2013
*- Platform : Windows XP SP3 - Windows 7
*- Dependency : MIRACL library
C++ wrapper for the MIRACK/SkyKeyXT ECCSI functionality.
The class API is adapted to the existing SkyKey soltuion.
*/
#include "CvXcode.h"
#include "CvString.h"
#include "CvMiraclDefs.h"
#include "CvEccsi.h"
#include "big.h"
#include <memory>
using namespace CvShared;
/* global hash functions used here only*/
static Big hash1( const ECn& point, const ECn& public_key, const char* identity, int length, const ECn& pvt, const Big& order )
{
int len, prefix = 0x04;
Big x, y;
char buffer[BN_BYTES + 1];
char h[HASH_LEN];
sha256 hash;
shs256_init( &hash );
point.get( x, y );
len = to_binary( x, 2 * AES_SECURITY / 8, buffer, TRUE );
shs256_process( &hash, prefix );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
len = to_binary( y, 2 * AES_SECURITY / 8, buffer, TRUE );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
public_key.get( x, y );
len = to_binary( x, 2 * AES_SECURITY / 8, buffer, TRUE );
shs256_process( &hash, prefix );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
len = to_binary( y, 2 * AES_SECURITY / 8, buffer, TRUE );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
for ( int i = 0; i < length; i++ )
{
shs256_process( &hash, identity[i] );
}
pvt.get( x, y );
len = to_binary( x, 2 * AES_SECURITY / 8, buffer, TRUE );
shs256_process( &hash, prefix );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
len = to_binary( y, 2 * AES_SECURITY / 8, buffer, TRUE );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
shs256_hash( &hash, h );
x = from_binary( HASH_LEN, h );
return (x % order );
}
static Big hash2( const Big& hs, const Big& r, const char* message, int length, const Big& order )
{
int len;
Big x;
char buffer[BN_BYTES + 1];
char h[HASH_LEN];
sha256 hash;
shs256_init( &hash );
len = to_binary( hs, 2 * AES_SECURITY / 8, buffer, TRUE );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
len = to_binary( r, 2 * AES_SECURITY / 8, buffer, TRUE );
for ( int i = 0; i < len; i++ )
{
shs256_process( &hash, buffer[i] );
}
for ( int i = 0; i < length; i++ )
{
shs256_process( &hash, message[i] );
}
shs256_hash( &hash, h );
x = from_binary( HASH_LEN, h );
return (x % order );
}
CvEccsi::CMiracl::CMiracl( ecp_domain& aEccsiDomain ) //about to be deprecated
{
#ifdef MR_GENERIC_AND_STATIC
m_pMip = mirsys( &m_instance, aEccsiDomain.nibbles, 16 );
#else
m_pMip = mirsys( aEccsiDomain.nibbles, 16 );
#endif
Big a = from_binary( FS, aEccsiDomain.A );
Big b = from_binary( FS, aEccsiDomain.B );
Big q = from_binary( FS, aEccsiDomain.Q );
m_pMip->ERCON=TRUE;
ecurve_init( _MIPP_ a.getbig(), b.getbig(), q.getbig(), MR_PROJECTIVE );
}
CvEccsi::CMiracl::~CMiracl()
{
mirexit( _MIPPO_ );
}
miracl* CvEccsi::mriniteccsi()
{
miracl* pMip = mirsys( 64, 16 );
pMip->IOBASE = 16;
return pMip;
}
CvEccsi::CvEccsi( csprng* apRng ) :
m_pRng(apRng), m_lastError(ECCSI_OK)
{
m_lastError = ECP_DOMAIN_INIT( &m_eccsiDomain, rom_ecp );
}
CvEccsi::~CvEccsi()
{
ECP_DOMAIN_KILL( &m_eccsiDomain );
}
bool CvEccsi::GenerateMasterKeyPair(OUT String& aPrivateKey, OUT String& aPublicKey)
{
char arrPublicKey [2*FS+1],
arrPrivateKey[2*FS+1];
octet public_key = {0, 2*FS+1, arrPublicKey},
private_key = {0, 2*FS+1, arrPrivateKey};
m_lastError = ECCSI_MASTER_KEY_PAIR_GENERATE(&m_eccsiDomain, m_pRng, &private_key, &public_key);
CvBase64::Encode((uint8_t*)public_key.val, public_key.len, aPublicKey);
CvBase64::Encode((uint8_t*)private_key.val, private_key.len, aPrivateKey);
return (m_lastError == ECCSI_OK);
}
bool CvEccsi::GenerateMasterPublicWithExternalPrivate(IN const String& aPrivateKey, OUT String& aPublicKey)
{
char arrPublicKey[2*FS+1];
String decPrivateKey;
octet public_key = {0, 2*FS+1, arrPublicKey};
CvBase64::Decode(aPrivateKey, decPrivateKey);
std::unique_ptr<char[]> arrPrivateKey( new char[decPrivateKey.size()] );
octet octPVK = {0, (int)decPrivateKey.size(), arrPrivateKey.get()};
OCTET_JOIN_BYTES(decPrivateKey.data(), octPVK.max, &octPVK);
m_lastError = ECCSI_MASTER_KEY_PAIR_GENERATE(&m_eccsiDomain, NULL, &octPVK, &public_key);
CvBase64::Encode((uint8_t*)public_key.val, public_key.len, aPublicKey);
return (m_lastError == ECCSI_OK);
}
bool CvEccsi::GenerateUserKeyPair(IN const String& aUserId, IN const String& aPrivateKeyKSAK, IN const String& aPublicKeyKPAK, OUT String& SSK, OUT String& PVT)
{
String decTrg;
char chV[FS], // size = ? chV receives id unique entropy
ch_kpak[2*FS+1],
ch_ssk[FS],
ch_pvt[2*FS+1];
octet V = {0, 2*FS+1, chV},
ssk = {0, FS, ch_ssk},
pvt = {0, 2*FS, ch_pvt};
CvBase64::Decode(aPrivateKeyKSAK, decTrg);
std::unique_ptr<char[]> ch_ksak( new char[decTrg.size( )] );
octet ksak = {0, (int)decTrg.size(), ch_ksak.get()};
OCTET_JOIN_BYTES(decTrg.data(), ksak.max, &ksak);
CvBase64::Decode(aPublicKeyKPAK, decTrg);
octet kpak = {0, (int)decTrg.size(), ch_kpak};
OCTET_JOIN_BYTES(decTrg.data(), kpak.max, &kpak);
octet Id = {0, (int)aUserId.length(), const_cast<char*>(aUserId.c_str())};
m_lastError = ECCSI_USER_KEY_PAIR_GENERATE(&m_eccsiDomain, m_pRng, &V, &Id, &ksak, &kpak, &ssk, &pvt);
CvBase64::Encode((uint8_t*)ssk.val, ssk.len, SSK);
CvBase64::Encode((uint8_t*)pvt.val, pvt.len, PVT);
return (m_lastError == ECCSI_OK);
}
bool CvEccsi::ValidateSecret( const String& aIdentity, const String& aPublicKey, const String& aSecret, const String& aPrivateKey )
{
char KPAK[2*FS+1] = {0};
char SSK[FS] = {0};
char PVT[2*FS+1] = {0};
char HS[FS] = {0};
octet octetID = { (int)aIdentity.length(), (int)aIdentity.length(), (char*)aIdentity.c_str() };
octet octetKPAK = { 0, sizeof(KPAK), KPAK };
octet octetHS = { 0, sizeof(HS), HS };
octet octetSSK = { 0,sizeof(SSK), SSK };
octet octetPVT = { 0, sizeof(PVT), PVT };
if ( !DecodePublicKey( aPublicKey, octetKPAK ) )
return false;
if ( !DecodePrivateKey( aPrivateKey, octetPVT ) )
return false;
if ( !DecodeSecret( aSecret, octetSSK ) )
return false;
HashHS( octetKPAK, aIdentity, octetPVT, octetHS );
m_lastError = ECCSI_USER_KEY_PAIR_VALIDATE( &m_eccsiDomain, &octetID, &octetKPAK, &octetHS, &octetSSK, &octetPVT );
return m_lastError == ECCSI_OK;
}
//static void printBuffer( const char* apPrefix, const char* apBuffer, int aLength )
//{
// printf( "%s [", apPrefix );
// for ( int i = 0; i < aLength; ++i )
// {
// if ( i+1 < aLength )
// printf( "%hhu,", apBuffer[i] );
// else
// printf( "%hhu", apBuffer[i] );
// }
// printf( "]\n" );
//}
/*! \brief Sign a message */
bool CvEccsi::Sign( const char* apMessage, int aMsgLen, const String& aIdentity, const String& aPublicKey,
const String& aSecret, const String& aPrivateKey, OUT String& aSignature )
{
if ( m_pRng == NULL )
return false;
char KPAK[2*FS+1] = {0};
char SSK[FS] = {0};
char PVT[2*FS+1] = {0};
char HS[FS] = {0};
char SIGNATURE[4*FS+1] = {0};
char J[FS];
octet octetM = { aMsgLen, aMsgLen, (char*)apMessage };
octet octetID = { (int)aIdentity.length(), (int)aIdentity.length(), (char*)aIdentity.c_str() };
octet octetKPAK = { 0, sizeof(KPAK), KPAK };
octet octetHS = { 0, sizeof(HS), HS };
octet octetSSK = { 0,sizeof(SSK), SSK };
octet octetPVT = { 0, sizeof(PVT), PVT };
octet octetSIGNATURE = { 0, sizeof(SIGNATURE), SIGNATURE };
/* Random non-zero value \f$ j \in F_q \f$ */
octet octetJ = { 0, sizeof(J), J };
if ( !DecodePublicKey( aPublicKey, octetKPAK ) )
return false;
if ( !DecodePrivateKey( aPrivateKey, octetPVT ) )
return false;
if ( !DecodeSecret( aSecret, octetSSK ) )
return false;
HashHS( octetKPAK, aIdentity, octetPVT, octetHS );
// printBuffer( "Signing message:\n", octetM.val, octetM.len );
while ( octetSIGNATURE.len == 0 )
{
/* By passing &RNG to ECCSI_SIGN a random value of j is used */
m_lastError = ECCSI_SIGN( &m_eccsiDomain, m_pRng, &octetJ, &octetM, &octetID, &octetKPAK,
&octetHS, &octetSSK, &octetPVT, &octetSIGNATURE );
if ( m_lastError != ECCSI_OK )
return false;
}
CvBase64::Encode( (uint8_t*)octetSIGNATURE.val, octetSIGNATURE.len, aSignature );
return true;
}
/*! \brief Verify the signature */
bool CvEccsi::Verify( const char* apMessage, int aMsgLen, const String& aIdentity, const String& aPublicKey, const String& aSignature )
{
String decodedSignature;
CvBase64::Decode( aSignature, decodedSignature );
char KPAK[2*FS+1] = {0};
octet octetM = { aMsgLen, aMsgLen, (char*)apMessage };
octet octetID = { (int)aIdentity.length(), (int)aIdentity.length(), (char*)aIdentity.c_str() };
octet octetKPAK = { 0, sizeof(KPAK), KPAK };
octet octetSIGNATURE = { (int)decodedSignature.size(), (int)decodedSignature.size(), (char*)decodedSignature.data() };
if ( !DecodePublicKey( aPublicKey, octetKPAK ) )
return false;
// printBuffer( "Verifying message:\n", octetM.val, octetM.len );
m_lastError = ECCSI_VERIFY( &m_eccsiDomain, &octetM, &octetID, &octetKPAK, &octetSIGNATURE );
return m_lastError == ECCSI_OK;
}
bool CvEccsi::DecodePrivateKey( const String& aPrivateKey, OUT octet& aPVT )
{
OCTET_CLEAR(&aPVT);
String decodedPrivateKey;
CvBase64::Decode( aPrivateKey, decodedPrivateKey );
if ( decodedPrivateKey.size() != aPVT.max ) // incorrect, no need here, could be > 0
return false;
OCTET_JOIN_BYTES( decodedPrivateKey.data(), aPVT.max, &aPVT );
return true;
}
bool CvEccsi::DecodeSecret( const String& aSecret, OUT octet& aSSK )
{
OCTET_CLEAR(&aSSK);
String decodedSecret;
CvBase64::Decode( aSecret, decodedSecret );
if ( decodedSecret.size() != aSSK.max )
return false;
OCTET_JOIN_BYTES( decodedSecret.data(), aSSK.max, &aSSK );
return true;
}
bool CvEccsi::DecodePublicKey( const String& aPublicKey, OUT octet& aKPAK )
{
OCTET_CLEAR(&aKPAK);
if ( aPublicKey[0] != '[' || aPublicKey[aPublicKey.length()-1] != ']' ) //why ???
return false;
CvString publicKey = aPublicKey;
publicKey.TrimLeft( "[" );
publicKey.TrimRight( "]" );
vector<CvString> tokens;
publicKey.Tokenize( ",", tokens );
if ( tokens.size() != 2 )
return false;
OCTET_JOIN_BYTE( 4, 1, &aKPAK );
for ( vector<CvString>::const_iterator itr = tokens.begin();
itr != tokens.end();
++itr )
{
String decoded;
CvBase64::Decode( *itr, decoded );
if ( decoded.size() != FS )
return false;
OCTET_JOIN_BYTES( decoded.data(), FS, &aKPAK );
}
return true;
}
bool CvEccsi::HashHS( const octet& aKPAK, const String& aIdentity, const octet& aPVT, octet& aHS )
{
CMiracl miracl( m_eccsiDomain ); //D2: need Miracl obj here due to use of Big numbers
OCTET_CLEAR(&aHS);
int prefix = 0x04;
HASHFUNC hash;
SHS_INIT( &hash );
SHS_PROCESS( &hash, prefix );
for ( int i = 0; i < sizeof(m_eccsiDomain.Gx); ++i )
{
SHS_PROCESS( &hash, m_eccsiDomain.Gx[i] );
}
for ( int i = 0; i < sizeof(m_eccsiDomain.Gy); ++i )
{
SHS_PROCESS( &hash, m_eccsiDomain.Gy[i] );
}
for ( int i = 0; i < aKPAK.len; ++i )
{
SHS_PROCESS( &hash, aKPAK.val[i] );
}
for ( size_t i = 0; i < aIdentity.length(); ++i )
{
SHS_PROCESS( &hash, aIdentity[i] );
}
for ( int i = 0; i < aPVT.len; ++i )
{
SHS_PROCESS( &hash, aPVT.val[i] );
}
char h[HASH_BYTES] = {0};
SHS_HASH( &hash, h );
Big x = from_binary( sizeof(h), h );
Big order = from_binary( sizeof(m_eccsiDomain.R), m_eccsiDomain.R ); // R = order eccsi parameter
x = x % order;
aHS.len = to_binary( x, aHS.max, aHS.val );
return true;
}
void CvEccsi::EncodePvt( const ECn& aPvt, OUT String& aEncodedPvt )
{
Big x, y;
aPvt.get( x, y );
int prefix = 0x4;
char tmp[2 * BN_BYTES + 1] = {'\0'};
tmp[0] = prefix;
int len = 1;
len += to_binary( x, 2 * AES_SECURITY / 8, tmp + len, TRUE );
len += to_binary( y, 2 * AES_SECURITY / 8, tmp + len /*1 + 2 * AES_SECURITY / 8*/, TRUE );
CvBase64::Encode( (const uint8_t*)tmp, len, aEncodedPvt );
}
bool CvEccsi::DecodePvt( const String& aEncodedPvt, OUT ECn& aPvt )
{
String str;
CvBase64::Decode( aEncodedPvt, str );
char* pBuffer = (char*)str.c_str();
Big x = from_binary( 2 * AES_SECURITY / 8, pBuffer + 1 );
Big y = from_binary( 2 * AES_SECURITY / 8, pBuffer + 2 * AES_SECURITY / 8 + 1 );
return aPvt.set( x, y );
}
void CvEccsi::GetMasterKey( OUT Big& aMasterKey )
{
aMasterKey = strong_rand( m_pRng, GetOrder() );
}
void CvEccsi::GetPublicKey( const Big& aMasterKey, OUT ECn& aPublicKey )
{
aPublicKey = aMasterKey * GetGenerator();
}
void CvEccsi::GetPrivateKey( const Big& aRand, OUT ECn& aPvt )
{
/* Compute PVT = [v]G in affine coordinates. */
aPvt = aRand * GetGenerator();
}
void CvEccsi::GetPrivateKey( const Big& aRand, OUT String& aPrivateKey )
{
ECn pvt;
GetPrivateKey( aRand, pvt );
/* Export PVT. */
EncodePvt( pvt, aPrivateKey );
}
void CvEccsi::GetSecret( const Big& aMasterKey, const Big& aRand, const char* aIdentity, int aLength, const ECn& aPublicKey, const ECn& aPvt, OUT Big& aSecret )
{
/* Compute HS = hash( G || KPAK || ID || PVT ), an N-octet integer. */
Big hs = hash1( GetGenerator(), aPublicKey, aIdentity, aLength, aPvt, GetOrder() );
/* Compute SSK = ( KSAK + HS * v ) modulo q. */
aSecret = ( aMasterKey + modmult( hs, aRand, GetOrder() ) ) % GetOrder();
}
void CvEccsi::GetSecret( const Big& aMasterKey, const Big& aRand, const char* aIdentity, int aLength, const ECn& aPublicKey, const String& aPrivateKey, OUT Big& aSecret )
{
ECn pvt;
DecodePvt( aPrivateKey, pvt );
GetSecret( aRand, aMasterKey, aIdentity, aLength, aPublicKey, pvt, aSecret );
}