/* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
*
* NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT
* TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE
* SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE COMPUTER, INC. AND THE
* ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE COMPUTER,
* INC. ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL
* EXPOSE YOU TO LIABILITY.
***************************************************************************
*
* NSFEEPublicKey.m - NSFEEPublicKey class implementation
*
* Revision History
* ----------------
* 17 Jul 97 Doug Mitchell at Apple
* Added ECDSA signature routines.
* 21 Aug 96 Doug Mitchell at NeXT
* Modified to use C-only FeePublicKey module.
* ???? 1994 Blaine Garst at NeXT
* Created.
*/
#import <Foundation/Foundation.h>
#import <Foundation/NSUtilities.h>
#import "NSCryptors.h"
#import "NSFEEPublicKeyPrivate.h"
#import "feePublicKey.h"
#import "feePublicKeyPrivate.h"
#import "ckutilities.h"
#import "mutils.h"
#import "feeTypes.h"
#import "curveParams.h"
#import "falloc.h"
#import "feeDigitalSignature.h"
#import "feeHash.h"
#import "feeFunctions.h"
#import "feeFEEDExp.h"
/*
Elliptic curve algebra over finite fields F(p**k), where p = 2**q -1 is a
Mersenne prime.
q is bit-depth.
A private key (a) is a large integer that when multiplied by an initial
curve point P yields the public key aP.
Public keys can be used to generate one-time pads because multiplication
is commutative:
a(bP) == b(aP)
*/
@implementation NSFEEPublicKey
/*
* Root method to create new public key from private "password" data.
*/
+ keyWithPrivateData:(NSData *)passwd
depth:(unsigned)depth
usageName:(NSString *)uname
{
NSFEEPublicKey *result;
feeReturn frtn;
unichar *uc;
result = [[self alloc] autorelease];
result->_pubKey = feePubKeyAlloc();
uc = fmalloc([uname length] * sizeof(unichar));
[uname getCharacters:uc];
frtn = feePubKeyInitFromPrivData(result->_pubKey,
[passwd bytes], [passwd length],
uc, [uname length],
depth);
ffree(uc);
if(frtn) {
NSLog(@"keyWithPrivateData: return nil;
}
return result;
}
/*
* Create new key with curve parameters matching existing oldKey.
*/
+ keyWithPrivateData:(NSData *)passwd
andKey:(NSFEEPublicKey *)oldKey
usageName:(NSString *)uname
{
NSFEEPublicKey *result;
feeReturn frtn;
unichar *uc;
result = [[self alloc] autorelease];
result->_pubKey = feePubKeyAlloc();
uc = fmalloc([uname length] * sizeof(unichar));
[uname getCharacters:uc];
frtn = feePubKeyInitFromKey(result->_pubKey,
[passwd bytes], [passwd length],
uc, [uname length],
oldKey->_pubKey);
ffree(uc);
if(frtn) {
NSLog(@"keyWithPrivateData:andKey: feeReturnString(frtn));
return nil;
}
return result;
}
+ keyWithPrivateData:(NSData *)passwd
usageName:(NSString *)uname
{
// 4 gives 127 bits of protection
// although the RSA challenge number of 127 bits has been
// broken, FEE is much stronger at the same length
return [self keyWithPrivateData:passwd
depth:FEE_DEPTH_DEFAULT
usageName:uname];
}
/*
* The standard way of creating a new key given a private "password" string.
*/
+ keyWithPrivateString:(NSString *)private
usageName:(NSString *)uname
{
NSData *pdata;
id result;
/*
* FIXME - handle other encodings?
*/
pdata = [private dataUsingEncoding:NSUTF8StringEncoding];
result = [self keyWithPrivateData:pdata usageName:uname];
return result;
}
+ keyWithPrivateString:(NSString *)private
andKey:(NSFEEPublicKey *)oldKey
usageName:(NSString *)uname
{
NSData *pdata;
id result;
if (!uname) return nil;
pdata = [private dataUsingEncoding:NSUTF8StringEncoding];
result = [self keyWithPrivateData:pdata andKey:oldKey usageName:uname];
return result;
}
+ keyWithPrivateString:(NSString *)private
depth:(unsigned)depth
usageName:(NSString *)uname
{
NSData *pdata;
id result;
if (!uname) return nil;
pdata = [private dataUsingEncoding:NSUTF8StringEncoding];
result = [self keyWithPrivateData:pdata depth:depth usageName:uname];
return result;
}
/*
* The standard way of creating a new key given a public key string.
*/
+ keyWithPublicKeyString:(NSString *)hexstr
{
NSFEEPublicKey *result;
feeReturn frtn;
NSStringEncoding defEndoding;
const char *s;
/*
* Protect against gross errors in the key string formatting...
*/
defEndoding = [NSString defaultCStringEncoding];
if([hexstr canBeConvertedToEncoding:defEndoding] == NO) {
NSLog(@"NSFEEPublicKey: Bad Public Key String Format (1)\n");
return nil;
}
/*
* FIXME - docs say this string is "autoreleased". How is a cString
* autoreleased?
*/
s = [hexstr cString];
result = [[self alloc] autorelease];
result->_pubKey = feePubKeyAlloc();
frtn = feePubKeyInitFromKeyString(result->_pubKey,
s, strlen(s));
if(frtn) {
NSLog(@"keyWithPublicKeyString:andKey: feeReturnString(frtn));
return nil;
}
return result;
}
- (void)dealloc
{
if(_pubKey) {
feePubKeyFree(_pubKey);
}
[super dealloc];
}
/*
* Create a public key in the form of a string. This string contains an
* encoded version of all of our ivars except for _private.
*
* See KeyStringFormat.doc for info on the format of the public key string;
* PLEASE UPDATE THIS DOCUMENT WHEN YOU MAKE CHANGES TO THE STRING FORMAT.
*/
- (NSString *)publicKeyString
{
char *keyStr;
unsigned keyStrLen;
feeReturn frtn;
NSString *result;
if(_pubKey == NULL) {
return nil;
}
frtn = feePubKeyCreateKeyString(_pubKey, &keyStr, &keyStrLen);
if(frtn) {
NSLog(@"publicKeyString: feeReturnString(frtn));
return nil;
}
result = [NSString stringWithCString:keyStr];
ffree((void *)keyStr);
return result;
}
- (BOOL)isEqual:(NSFEEPublicKey *)other
{
if((other == nil) || (other->_pubKey == NULL) || (_pubKey == NULL)) {
return NO;
}
if(feePubKeyIsEqual(_pubKey, other->_pubKey)) {
return YES;
}
else {
return NO;
}
}
- (unsigned)keyBitsize
{
if(_pubKey == NULL) {
return 0;
}
return feePubKeyBitsize(_pubKey);
}
- (NSString *)algorithmName
{
return [NSString stringWithCString:feePubKeyAlgorithmName()];
}
- (NSString *)usageName
{
unsigned unameLen;
const feeUnichar *uname;
NSString *result;
if(_pubKey == NULL) {
return nil;
}
uname = feePubKeyUsageName(_pubKey, &unameLen);
result = [NSString stringWithCharacters:uname length:unameLen];
return result;
}
- (NSString *)signer
{
return [self usageName];
}
- (NSData *)padWithPublicKey:(id <NSObject,NSPublicKey>)otherKey
{
NSFEEPublicKey *other;
NSMutableData *result;
feeReturn frtn;
unsigned char *padData;
unsigned padDataLen;
if(_pubKey == NULL) {
return nil;
}
if (![otherKey isMemberOfClass:isa]) {
return nil;
}
other = otherKey;
if(other->_pubKey == NULL) {
return nil;
}
frtn = feePubKeyCreatePad(_pubKey,
other->_pubKey,
&padData,
&padDataLen);
if(frtn) {
NSLog(@"padWithPublicKey: return nil;
}
result = [NSData dataWithBytesNoCopy:padData length:padDataLen];
return result;
}
- (NSData *)encryptData:(NSData *)data
{
feeFEEDExp feed;
NSData *result;
feeReturn frtn;
unsigned char *ctext;
unsigned ctextLen;
if(_pubKey == NULL) {
return nil;
}
feed = feeFEEDExpNewWithPubKey(_pubKey);
frtn = feeFEEDExpEncrypt(feed,
[data bytes],
[data length],
&ctext,
&ctextLen);
if(frtn == FR_Success) {
result = [NSData dataWithBytesNoCopy:ctext length:ctextLen];
}
else {
NSLog(@"feeFEEDEncrypt: result = nil;
}
feeFEEDExpFree(feed);
return result;
}
- (NSData *)decryptData:(NSData *)data
{
feeFEEDExp feed;
NSData *result;
feeReturn frtn;
unsigned char *ptext;
unsigned ptextLen;
if(_pubKey == NULL) {
return nil;
}
feed = feeFEEDExpNewWithPubKey(_pubKey);
frtn = feeFEEDExpDecrypt(feed,
[data bytes],
[data length],
&ptext,
&ptextLen);
if(frtn == FR_Success) {
result = [NSData dataWithBytesNoCopy:ptext length:ptextLen];
}
else {
NSLog(@"feeFEEDDecrypt: result = nil;
}
feeFEEDExpFree(feed);
return result;
}
/*
* When 1, we use ECDSA unless we're using a depth which does not
* have curve orders.
* WARNING - enabling ECDSA by default breaks ICE and compatibility
* with Java signatures, at least until we have a Java ECDSA
* implementation.
*/
#define ECDSA_SIG_DEFAULT 0
- (NSData *)digitalSignatureForData:(NSData *)data
{
NSData *result;
unsigned char *sig;
unsigned sigLen;
feeReturn frtn;
curveParams *cp;
if(_pubKey == NULL) {
return nil;
}
cp = feePubKeyCurveParams(_pubKey);
if(!ECDSA_SIG_DEFAULT || isZero(cp->x1OrderPlus)) {
frtn = feePubKeyCreateSignature(_pubKey,
[data bytes],
[data length],
&sig,
&sigLen);
}
else {
frtn = feePubKeyCreateECDSASignature(_pubKey,
[data bytes],
[data length],
&sig,
&sigLen);
}
if(frtn) {
NSLog(@"digitalSignatureForData: return nil;
}
result = [NSData dataWithBytesNoCopy:sig length:sigLen];
return result;
}
- (BOOL)isValidDigitalSignature:(NSData *)signa
forData:(NSData *)data
{
feeReturn frtn;
feeUnichar *sigSigner;
unsigned sigSignerLen;
curveParams *cp;
if(_pubKey == NULL) {
return NO;
}
cp = feePubKeyCurveParams(_pubKey);
if(!ECDSA_SIG_DEFAULT || isZero(cp->x1OrderPlus)) {
frtn = feePubKeyVerifySignature(_pubKey,
[data bytes],
[data length],
[signa bytes],
[signa length],
&sigSigner,
&sigSignerLen);
}
else {
frtn = feePubKeyVerifyECDSASignature(_pubKey,
[data bytes],
[data length],
[signa bytes],
[signa length],
&sigSigner,
&sigSignerLen);
}
/*
* FIXME - We just throw away the signer for now...
*/
if(sigSignerLen) {
ffree(sigSigner);
}
switch(frtn) {
case FR_Success:
return YES;
case FR_InvalidSignature:
return NO;
default:
/*
* Something other than simple signature mismatch...
*/
NSLog(@"isValidDigitalSignature: return NO;
}
}
@end
@implementation NSFEEPublicKey(Private)
- (key)minus
{
if(_pubKey == NULL) {
return NULL;
}
return feePubKeyMinusCurve(_pubKey);
}
- (key)plus
{
if(_pubKey == NULL) {
return NULL;
}
return feePubKeyPlusCurve(_pubKey);
}
- (feePubKey)feePubKey
{
return _pubKey;
}
#if FEE_DEBUG
- (void)dump
{
printPubKey(_pubKey);
}
#endif FEE_DEBUG
@end