sl@0: /* sl@0: * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * DSA Keypair implementation sl@0: * DSA keypair generation implementation sl@0: * sl@0: */ sl@0: sl@0: sl@0: /** sl@0: @file sl@0: */ sl@0: sl@0: #include "dsakeypairgenimpl.h" sl@0: #include "pluginconfig.h" sl@0: #include "keypair.h" sl@0: #include "common/inlines.h" // For TClassSwap sl@0: #include "mont.h" sl@0: #include "sha1impl.h" sl@0: #include sl@0: #include sl@0: sl@0: sl@0: const TUint KShaSize = 20; sl@0: const TUint KMinPrimeLength = 512; sl@0: const TUint KMaxPrimeLength = 1024; sl@0: const TUint KPrimeLengthMultiple = 64; sl@0: sl@0: using namespace SoftwareCrypto; sl@0: sl@0: sl@0: /* CDSAPrimeCertificate */ sl@0: sl@0: CDSAPrimeCertificate* CDSAPrimeCertificate::NewL(const TDesC8& aSeed, TUint aCounter) sl@0: { sl@0: CDSAPrimeCertificate* self = NewLC(aSeed, aCounter); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: CDSAPrimeCertificate* CDSAPrimeCertificate::NewLC(const TDesC8& aSeed, TUint aCounter) sl@0: { sl@0: CDSAPrimeCertificate* self = new(ELeave) CDSAPrimeCertificate(aCounter); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(aSeed); sl@0: return self; sl@0: } sl@0: sl@0: const TDesC8& CDSAPrimeCertificate::Seed() const sl@0: { sl@0: return *iSeed; sl@0: } sl@0: sl@0: TUint CDSAPrimeCertificate::Counter() const sl@0: { sl@0: return iCounter; sl@0: } sl@0: sl@0: CDSAPrimeCertificate::~CDSAPrimeCertificate() sl@0: { sl@0: delete const_cast(iSeed); sl@0: } sl@0: sl@0: void CDSAPrimeCertificate::ConstructL(const TDesC8& aSeed) sl@0: { sl@0: iSeed = aSeed.AllocL(); sl@0: } sl@0: sl@0: CDSAPrimeCertificate::CDSAPrimeCertificate(TUint aCounter) sl@0: : iCounter(aCounter) sl@0: { sl@0: } sl@0: sl@0: CDSAPrimeCertificate::CDSAPrimeCertificate() sl@0: { sl@0: } sl@0: sl@0: sl@0: /* CDSAKeyPairGenImpl */ sl@0: CDSAKeyPairGenImpl::CDSAKeyPairGenImpl() sl@0: { sl@0: } sl@0: sl@0: CDSAKeyPairGenImpl::~CDSAKeyPairGenImpl() sl@0: { sl@0: delete iPrimeCertificate; sl@0: } sl@0: sl@0: CDSAKeyPairGenImpl* CDSAKeyPairGenImpl::NewL() sl@0: { sl@0: CDSAKeyPairGenImpl* self = CDSAKeyPairGenImpl::NewLC(); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: CDSAKeyPairGenImpl* CDSAKeyPairGenImpl::NewLC() sl@0: { sl@0: CDSAKeyPairGenImpl* self = new(ELeave) CDSAKeyPairGenImpl(); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: return self; sl@0: } sl@0: sl@0: void CDSAKeyPairGenImpl::ConstructL(void) sl@0: { sl@0: CKeyPairGenImpl::ConstructL(); sl@0: } sl@0: sl@0: CExtendedCharacteristics* CDSAKeyPairGenImpl::CreateExtendedCharacteristicsL() sl@0: { sl@0: // All Symbian software plug-ins have unlimited concurrency, cannot be reserved sl@0: // for exclusive use and are not CERTIFIED to be standards compliant. sl@0: return CExtendedCharacteristics::NewL(KMaxTInt, EFalse); sl@0: } sl@0: sl@0: const CExtendedCharacteristics* CDSAKeyPairGenImpl::GetExtendedCharacteristicsL() sl@0: { sl@0: return CDSAKeyPairGenImpl::CreateExtendedCharacteristicsL(); sl@0: } sl@0: sl@0: TUid CDSAKeyPairGenImpl::ImplementationUid() const sl@0: { sl@0: return KCryptoPluginDsaKeyPairGenUid; sl@0: } sl@0: sl@0: void CDSAKeyPairGenImpl::Reset() sl@0: { sl@0: // does nothing in this plugin sl@0: } sl@0: sl@0: TBool CDSAKeyPairGenImpl::ValidPrimeLength(TUint aPrimeBits) sl@0: { sl@0: return (aPrimeBits >= KMinPrimeLength && sl@0: aPrimeBits <= KMaxPrimeLength && sl@0: aPrimeBits % KPrimeLengthMultiple == 0); sl@0: } sl@0: sl@0: TBool CDSAKeyPairGenImpl::GeneratePrimesL(const TDesC8& aSeed, sl@0: TUint& aCounter, sl@0: RInteger& aP, sl@0: TUint aL, sl@0: RInteger& aQ, sl@0: TBool aUseInputCounter) sl@0: { sl@0: //This follows the steps in FIPS 186-2 sl@0: //See DSS Appendix 2.2 sl@0: //Note. Step 1 is performed prior to calling GeneratePrimesL, so that this sl@0: //routine can be used for both generation and validation. sl@0: //Step 1. Choose an arbitrary sequence of at least 160 bits and call it sl@0: //SEED. Let g be the length of SEED in bits. sl@0: sl@0: if(!ValidPrimeLength(aL)) sl@0: { sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: CSHA1Impl* sha1 = CSHA1Impl::NewL(); sl@0: CleanupStack::PushL(sha1); sl@0: sl@0: HBufC8* seedBuf = aSeed.AllocLC(); sl@0: TPtr8 seed = seedBuf->Des(); sl@0: TUint gBytes = aSeed.Size(); sl@0: sl@0: //Note that the DSS's g = BytesToBits(gBytes) ie. the number of random bits sl@0: //in the seed. sl@0: //This function has made the assumption (for ease of computation) that g%8 sl@0: //is 0. Ie the seed is a whole number of random bytes. sl@0: TBuf8 U; sl@0: TBuf8 temp; sl@0: const TUint n = (aL-1)/160; sl@0: const TUint b = (aL-1)%160; sl@0: HBufC8* Wbuf = HBufC8::NewMaxLC((n+1) * KShaSize); sl@0: TUint8* W = const_cast(Wbuf->Ptr()); sl@0: sl@0: U.Copy(sha1->Final(seed)); sl@0: sl@0: //Step 2. U = SHA-1[SEED] XOR SHA-1[(SEED+1) mod 2^g] sl@0: for(TInt i=gBytes - 1, carry=ETrue; i>=0 && carry; i--) sl@0: { sl@0: //!++(TUint) adds one to the current word which if it overflows to zero sl@0: //sets carry to 1 thus letting the loop continue. It's a poor man's sl@0: //multi-word addition. Swift eh? sl@0: carry = !++(seed[i]); sl@0: } sl@0: sl@0: temp.Copy(sha1->Final(seed)); sl@0: XorBuf(const_cast(U.Ptr()), temp.Ptr(), KShaSize); sl@0: sl@0: //Step 3. Form q from U by setting the most significant bit (2^159) sl@0: //and the least significant bit to 1. sl@0: U[0] |= 0x80; sl@0: U[KShaSize-1] |= 1; sl@0: sl@0: aQ = RInteger::NewL(U); sl@0: CleanupStack::PushL(aQ); sl@0: sl@0: //Step 4. Use a robust primality testing algo to test if q is prime sl@0: //The robust part is the calling codes problem. This will use whatever sl@0: //random number generator you set for the thread. To attempt FIPS 186-2 sl@0: //compliance, set a FIPS 186-2 compliant RNG. sl@0: if( !aQ.IsPrimeL() ) sl@0: { sl@0: //Step 5. If not exit and get a new seed sl@0: CleanupStack::PopAndDestroy(4, sha1); sl@0: return EFalse; sl@0: } sl@0: sl@0: TUint counterEnd = aUseInputCounter ? aCounter+1 : 4096; sl@0: sl@0: //Step 6. Let counter = 0 and offset = 2 sl@0: //Note 1. that the DSS speaks of SEED + offset + k because they always sl@0: //refer to a constant SEED. We update our seed as we go so the offset sl@0: //variable has already been added to seed in the previous iterations. sl@0: //Note 2. We've already added 1 to our seed, so the first time through this sl@0: //the offset in DSS speak will be 2. sl@0: for(TUint counter=0; counter < counterEnd; counter++) sl@0: { sl@0: //Step 7. For k=0, ..., n let sl@0: // Vk = SHA-1[(SEED + offset + k) mod 2^g] sl@0: //I'm storing the Vk's inside of a big W buffer. sl@0: for(TUint k=0; k<=n; k++) sl@0: { sl@0: for(TInt i=gBytes-1, carry=ETrue; i>=0 && carry; i--) sl@0: { sl@0: carry = !++(seed[i]); sl@0: } sl@0: if(!aUseInputCounter || counter == aCounter) sl@0: { sl@0: TPtr8 Wptr(W+(n-k)*KShaSize, gBytes); sl@0: Wptr.Copy(sha1->Final(seed)); sl@0: } sl@0: } sl@0: if(!aUseInputCounter || counter == aCounter) sl@0: { sl@0: //Step 8. Let W be the integer... and let X = W + 2^(L-1) sl@0: const_cast((*Wbuf)[KShaSize - 1 - b/8]) |= 0x80; sl@0: TPtr8 Wptr(W + KShaSize - 1 - b/8, aL/8, aL/8); sl@0: RInteger X = RInteger::NewL(Wptr); sl@0: CleanupStack::PushL(X); sl@0: //Step 9. Let c = X mod 2q and set p = X - (c-1) sl@0: RInteger twoQ = aQ.TimesL(TInteger::Two()); sl@0: CleanupStack::PushL(twoQ); sl@0: RInteger c = X.ModuloL(twoQ); sl@0: CleanupStack::PushL(c); sl@0: --c; sl@0: aP = X.MinusL(c); sl@0: CleanupStack::PopAndDestroy(3, &X); //twoQ, c, X sl@0: CleanupStack::PushL(aP); sl@0: sl@0: //Step 10 and 11: if p >= 2^(L-1) and p is prime sl@0: if( aP.Bit(aL-1) && aP.IsPrimeL() ) sl@0: { sl@0: aCounter = counter; sl@0: CleanupStack::Pop(2, &aQ); sl@0: CleanupStack::PopAndDestroy(3, sha1); sl@0: return ETrue; sl@0: } sl@0: CleanupStack::PopAndDestroy(&aP); sl@0: } sl@0: } sl@0: CleanupStack::PopAndDestroy(4, &sha1); sl@0: return EFalse; sl@0: } sl@0: sl@0: void CDSAKeyPairGenImpl::GenerateKeyPairL(TInt aKeySize, sl@0: const CCryptoParams& aKeyParameters, sl@0: CKeyPair*& aKeyPair) sl@0: { sl@0: //This is the first step of DSA prime generation. The remaining steps are sl@0: //performed in CDSAParameters::GeneratePrimesL sl@0: //Step 1. Choose an arbitrary sequence of at least 160 bits and call it sl@0: //SEED. Let g be the length of SEED in bits. sl@0: TBuf8 seed(KShaSize); sl@0: TUint c; sl@0: RInteger p; sl@0: RInteger q; sl@0: sl@0: do sl@0: { sl@0: TRAPD(err, GenerateRandomBytesL(seed)); sl@0: if((err != KErrNone) && (err != KErrNotSecure)) sl@0: User::Leave(err); sl@0: } sl@0: while(!GeneratePrimesL(seed, c, p, aKeySize, q)); sl@0: sl@0: //Double PushL will not fail as GeneratePrimesL uses the CleanupStack sl@0: //(at least one push and pop ;) sl@0: CleanupStack::PushL(p); sl@0: CleanupStack::PushL(q); sl@0: sl@0: iPrimeCertificate = CDSAPrimeCertificate::NewL(seed, c); sl@0: sl@0: // aKeyParameters isn't const here anymore sl@0: CCryptoParams& paramRef=const_cast(aKeyParameters); sl@0: paramRef.AddL(c, KDsaKeyGenerationCounterUid); sl@0: paramRef.AddL(seed, KDsaKeyGenerationSeedUid); sl@0: sl@0: CMontgomeryStructure* montP = CMontgomeryStructure::NewLC(p); sl@0: sl@0: --p; sl@0: sl@0: // e = (p-1)/q sl@0: RInteger e = p.DividedByL(q); sl@0: CleanupStack::PushL(e); sl@0: sl@0: --p; //now it's p-2 :) sl@0: sl@0: RInteger h; sl@0: const TInteger* g = 0; sl@0: do sl@0: { sl@0: // find a random h | 1 < h < p-1 sl@0: h = RInteger::NewRandomL(TInteger::Two(), p); sl@0: CleanupStack::PushL(h); sl@0: // g = h^e mod p sl@0: g = &(montP->ExponentiateL(h, e)); sl@0: CleanupStack::PopAndDestroy(&h); sl@0: } sl@0: while( *g <= TInteger::One() ); sl@0: CleanupStack::PopAndDestroy(&e); sl@0: sl@0: ++p; //reincrement p to original value sl@0: ++p; sl@0: sl@0: sl@0: RInteger g1 = RInteger::NewL(*g); //take a copy of montP's g sl@0: CleanupStack::PushL(g1); sl@0: --q; sl@0: // select random x | 0 < x < q sl@0: RInteger x = RInteger::NewRandomL(TInteger::One(), q); sl@0: CleanupStack::PushL(x); sl@0: ++q; sl@0: sl@0: // sl@0: // create the keys parameters sl@0: CCryptoParams* privateKeyParameters = CCryptoParams::NewLC(); sl@0: privateKeyParameters->AddL(p, KDsaKeyParameterPUid); sl@0: privateKeyParameters->AddL(q, KDsaKeyParameterQUid); sl@0: privateKeyParameters->AddL(g1, KDsaKeyParameterGUid); sl@0: privateKeyParameters->AddL(x, KDsaKeyParameterXUid); sl@0: TKeyProperty privateKeyProperties = {KDSAKeyPairGeneratorUid, sl@0: KCryptoPluginDsaKeyPairGenUid, sl@0: KDsaPrivateKeyUid, sl@0: KNonEmbeddedKeyUid}; sl@0: sl@0: CCryptoParams* publicKeyParameters = CCryptoParams::NewLC(); sl@0: publicKeyParameters->AddL(p, KDsaKeyParameterPUid); sl@0: publicKeyParameters->AddL(q, KDsaKeyParameterQUid); sl@0: publicKeyParameters->AddL(g1, KDsaKeyParameterGUid); sl@0: RInteger y = RInteger::NewL(montP->ExponentiateL(*g, x)); sl@0: CleanupStack::PushL(y); sl@0: publicKeyParameters->AddL(y, KDsaKeyParameterYUid); sl@0: TKeyProperty publicKeyProperties = {KDSAKeyPairGeneratorUid, sl@0: KCryptoPluginDsaKeyPairGenUid, sl@0: KDsaPublicKeyUid, sl@0: KNonEmbeddedKeyUid}; sl@0: sl@0: // sl@0: // create the private key sl@0: // sl@0: CKey* privateKey = CKey::NewL(privateKeyProperties, *privateKeyParameters); sl@0: CleanupStack::PushL(privateKey); sl@0: sl@0: // sl@0: // create the public key sl@0: // sl@0: CKey* publicKey = CKey::NewL(publicKeyProperties, *publicKeyParameters); sl@0: CleanupStack::PushL(publicKey); sl@0: sl@0: aKeyPair = CKeyPair::NewL(publicKey, privateKey); sl@0: sl@0: //publicKey, publicKeyParameters, y, privateKey, privateKeyParameters, x, g1, montP, q, p sl@0: CleanupStack::Pop(2, privateKey); sl@0: CleanupStack::PopAndDestroy(8, &p); sl@0: }