--- /dev/null
+/* Copyright (c) 1998,2011,2014 Apple 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, INC. AND THE
+ * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE,
+ * 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 at Apple
+ * Added ECDSA signature routines.
+ * 21 Aug 96 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: %s\n", feeReturnString(frtn));
+ 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: %s\n",
+ 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: %s\n",
+ 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: %s\n",
+ 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: %s\n", feeReturnString(frtn));
+ 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: %s\n", feeReturnString(frtn));
+ 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: %s\n", feeReturnString(frtn));
+ 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: %s\n", feeReturnString(frtn));
+ 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: %s\n", feeReturnString(frtn));
+ 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