/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


//
// Tester - test driver for securityserver client side.
//
#include "testclient.h"
#include "testutils.h"


//
// Simple run-through.
// This generates an RSA key, tests cleartext retrieval, signs a message,
// and veries it both ways.
// This is a basic integrity regression for the SecurityServer.
//
void signWithRSA()
{
	printf("* RSA key signing test\n");
	CSP csp(gGuidAppleCSP);
	ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
	StringData data("To sign or not to sign, is that the question?");
	
	// set up dummy credentials
	CssmKey dummyKey; memset(&dummyKey, 0, sizeof(dummyKey));
	CssmData nullData;
	
	// generate a key
	detail("Asking for RSA key generation");
	KeyHandle publicKey, privateKey;
	const CssmCryptoData seed(StringData("Seed ye well, my friend, and ye shall reap..."));
	FakeContext genContext(CSSM_ALGCLASS_KEYGEN, CSSM_ALGID_RSA,
		&::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH, 512),
		&::Context::Attr(CSSM_ATTRIBUTE_SEED, seed),
		NULL);
    CssmKey::Header pubHeader, privHeader;
	ss.generateKey(noDb, genContext,
		CSSM_KEYUSE_VERIFY, CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_DATA,
		CSSM_KEYUSE_SIGN, CSSM_KEYATTR_SENSITIVE,
		NULL/*cred*/, NULL/*owner*/, publicKey, pubHeader, privateKey, privHeader);
	detail("Key pair generated");
	
	// retrieve the public key
	CssmKey cpk;
	FakeContext wrapContext(CSSM_ALGCLASS_SYMMETRIC, CSSM_ALGID_NONE, 0);
	ss.wrapKey(wrapContext, noKey, publicKey, &nullCred, NULL, cpk);
    Key clearPublicKey(csp, cpk);
	detail("Retrieved public key");
	
	// make sure we can't retrieve the private key
	CssmKey clearPrivateKey;
	try {
		ss.wrapKey(wrapContext, noKey, privateKey, NULL/*cred*/, NULL, clearPrivateKey);
		error("SecurityServer ACTUALLY gave us the PRIVATE key bits!");
	} catch (CssmError &err) {
		detail(err, "Private key retrieval properly rejected");
	}
	
	// sign a message
	CssmData signature;
	FakeContext signContext(CSSM_ALGCLASS_SIGNATURE, CSSM_ALGID_SHA1WithRSA,
		&::Context::Attr(CSSM_ATTRIBUTE_KEY, dummyKey),
		NULL);
	ss.generateSignature(signContext, privateKey, data, signature);
	detail("Signature generated by SecurityServer");
	
	// verify the signature (local)
	{
		Verify verifier(csp, CSSM_ALGID_SHA1WithRSA);
		verifier.key(clearPublicKey);
		verifier.verify(data, signature);
		detail("Signature verified locally");
	}
	
	// verify the signature (SS)
	ss.verifySignature(signContext, publicKey, data, signature);
	detail("Signature verified by SecurityServer");
	
	// falsify the signature (SS)
	DataBuffer<200> falseData;
	memcpy(falseData.data(), data.data(), data.length());
	falseData.length(data.length());
	((char *)falseData)[3] = '?';	// alter message
	try {
		ss.verifySignature(signContext, publicKey, falseData, signature);
		error("Altered message incorrectly verifies");
	} catch (CssmError &err) {		
		if (err.cssmError() == CSSMERR_CSP_VERIFY_FAILED)
			detail("Verify of altered message successfully failed");
		else
			error(err, "Unexpected exception on verify failure test");
	}
}


//
// Encrypt with DES
//
void desEncryption()
{
	printf("* DES encryption test\n");
	ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
	CSP csp(gGuidAppleCSP);
	
	StringData clearText("Insert witty quotation here.");
	StringData iv("abcdefgh");
	
	// make up a DES key
	StringData keyBits(strdup("Wallaby!"));
	CssmKey keyForm(keyBits);
	keyForm.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
	keyForm.header().BlobType = CSSM_KEYBLOB_RAW;
	keyForm.header().AlgorithmId = CSSM_ALGID_DES;
	keyForm.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
    Key key(csp, keyForm);
	
	// encrypt locally
	DataBuffer<200> localCipher;
	Encrypt localCrypt(csp, CSSM_ALGID_DES);
	localCrypt.mode(CSSM_ALGMODE_CBC_IV8);
	localCrypt.padding(CSSM_PADDING_PKCS1);
	localCrypt.initVector(iv);
	localCrypt.key(key);
	CssmData remData;
	size_t localLen = localCrypt.encrypt(clearText, localCipher, remData);
	if (remData)
		error("LOCAL ENCRYPTION OVERFLOWED");
	localCipher.length(localLen);
	detail("Locally encrypted %ld bytes", localLen);
	
	// wrap in the key
	CssmData unwrappedData;
	ResourceControlContext owner;
	FakeContext unwrapContext(CSSM_ALGCLASS_SYMMETRIC, CSSM_ALGID_NONE, 0);
	KeyHandle keyRef;
    CssmKey::Header keyHeader;
    ss.unwrapKey(noDb, unwrapContext, noKey, noKey,
		key,
		CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
		CSSM_KEYATTR_RETURN_DEFAULT,
		NULL/*cred*/, NULL/*owner*/, unwrappedData, keyRef, keyHeader);
	detail("Placed key into SecurityServer; handle=%lx", keyRef);
	
	// encrypt remotely and compare
	const CssmKey &tKey = key;
	FakeContext cryptoContext(CSSM_ALGCLASS_SYMMETRIC, CSSM_ALGID_DES,
		&::Context::Attr(CSSM_ATTRIBUTE_KEY, keyForm),
		&::Context::Attr(CSSM_ATTRIBUTE_INIT_VECTOR, iv),
		&::Context::Attr(CSSM_ATTRIBUTE_MODE, CSSM_ALGMODE_CBC_IV8),
		&::Context::Attr(CSSM_ATTRIBUTE_PADDING, CSSM_PADDING_PKCS1),
		NULL);
	CssmData remoteCipher;
	ss.encrypt(cryptoContext, keyRef, clearText, remoteCipher);
	detail("Plaintext encrypted on SecurityServer");
	if (remoteCipher == localCipher)
		detail("Ciphertexts verified");
	else
		error("CIPHERTEXTS DIFFER");
	
	// decrypt in SecurityServer
	DataBuffer<200> clearRecovered;
	ss.decrypt(cryptoContext, keyRef, localCipher, clearRecovered);
	detail("Decrypted ciphertext in SecurityServer");
	if (clearRecovered == clearText)
		detail("Plaintext recovered");
	else
		error("PLAINTEXT MISMATCH");
}

