1/* Copyright (c) 1998,2011,2014 Apple Inc.  All Rights Reserved.
2 *
3 * NOTICE: USE OF THE MATERIALS ACCOMPANYING THIS NOTICE IS SUBJECT
4 * TO THE TERMS OF THE SIGNED "FAST ELLIPTIC ENCRYPTION (FEE) REFERENCE
5 * SOURCE CODE EVALUATION AGREEMENT" BETWEEN APPLE, INC. AND THE
6 * ORIGINAL LICENSEE THAT OBTAINED THESE MATERIALS FROM APPLE,
7 * INC.  ANY USE OF THESE MATERIALS NOT PERMITTED BY SUCH AGREEMENT WILL
8 * EXPOSE YOU TO LIABILITY.
9 ***************************************************************************
10 *
11 * NSFEEPublicKey.m - NSFEEPublicKey class implementation
12 *
13 * Revision History
14 * ----------------
15 * 17 Jul 97 at Apple
16 *	Added ECDSA signature routines.
17 * 21 Aug 96 at NeXT
18 *	Modified to use C-only FeePublicKey module.
19 *  ???? 1994	Blaine Garst at NeXT
20 *	Created.
21 */
22
23#import <Foundation/Foundation.h>
24#import <Foundation/NSUtilities.h>
25
26#import "NSCryptors.h"
27#import "NSFEEPublicKeyPrivate.h"
28#import "feePublicKey.h"
29#import "feePublicKeyPrivate.h"
30#import "ckutilities.h"
31#import "mutils.h"
32#import "feeTypes.h"
33#import "curveParams.h"
34#import "falloc.h"
35#import "feeDigitalSignature.h"
36#import "feeHash.h"
37#import "feeFunctions.h"
38#import "feeFEEDExp.h"
39
40/*
41   Elliptic curve algebra over finite fields F(p**k), where p = 2**q -1 is a
42   	Mersenne prime.
43   q is bit-depth.
44   A private key (a) is a large integer that when multiplied by an initial
45   	curve point P yields the public key aP.
46   Public keys can be used to generate one-time pads because multiplication
47   	is commutative:
48
49	a(bP) == b(aP)
50 */
51
52@implementation NSFEEPublicKey
53
54/*
55 * Root method to create new public key from private "password" data.
56 */
57+ keyWithPrivateData:(NSData *)passwd
58	depth:(unsigned)depth
59	usageName:(NSString *)uname
60{
61	NSFEEPublicKey *result;
62	feeReturn frtn;
63	unichar *uc;
64
65	result = [[self alloc] autorelease];
66	result->_pubKey = feePubKeyAlloc();
67	uc = fmalloc([uname length] * sizeof(unichar));
68	[uname getCharacters:uc];
69	frtn = feePubKeyInitFromPrivData(result->_pubKey,
70		[passwd bytes],	[passwd length],
71		uc, [uname length],
72		depth);
73	ffree(uc);
74	if(frtn) {
75		NSLog(@"keyWithPrivateData: %s\n", feeReturnString(frtn));
76		return nil;
77	}
78	return result;
79}
80
81/*
82 * Create new key with curve parameters matching existing oldKey.
83 */
84+ keyWithPrivateData:(NSData *)passwd
85	andKey:(NSFEEPublicKey *)oldKey
86	usageName:(NSString *)uname
87{
88	NSFEEPublicKey *result;
89	feeReturn frtn;
90	unichar *uc;
91
92	result = [[self alloc] autorelease];
93	result->_pubKey = feePubKeyAlloc();
94	uc = fmalloc([uname length] * sizeof(unichar));
95	[uname getCharacters:uc];
96	frtn = feePubKeyInitFromKey(result->_pubKey,
97		[passwd bytes],	[passwd length],
98		uc, [uname length],
99		oldKey->_pubKey);
100	ffree(uc);
101	if(frtn) {
102		NSLog(@"keyWithPrivateData:andKey: %s\n",
103			feeReturnString(frtn));
104		return nil;
105	}
106	return result;
107}
108
109+ keyWithPrivateData:(NSData *)passwd
110	usageName:(NSString *)uname
111{
112	// 4 gives 127 bits of protection
113	// although the RSA challenge number of 127 bits has been
114	// broken, FEE is much stronger at the same length
115	return [self keyWithPrivateData:passwd
116		depth:FEE_DEPTH_DEFAULT
117		usageName:uname];
118}
119
120/*
121 * The standard way of creating a new key given a private "password" string.
122 */
123+ keyWithPrivateString:(NSString *)private
124	usageName:(NSString *)uname
125{
126	NSData *pdata;
127	id result;
128
129	/*
130	 * FIXME - handle other encodings?
131	 */
132	pdata = [private dataUsingEncoding:NSUTF8StringEncoding];
133	result = [self keyWithPrivateData:pdata usageName:uname];
134	return result;
135}
136
137+ keyWithPrivateString:(NSString *)private
138	andKey:(NSFEEPublicKey *)oldKey
139	usageName:(NSString *)uname
140{
141	NSData *pdata;
142	id result;
143
144	if (!uname) return nil;
145
146	pdata = [private dataUsingEncoding:NSUTF8StringEncoding];
147	result = [self keyWithPrivateData:pdata andKey:oldKey usageName:uname];
148	return result;
149}
150
151+ keyWithPrivateString:(NSString *)private
152	depth:(unsigned)depth
153	usageName:(NSString *)uname
154{
155	NSData *pdata;
156	id result;
157
158	if (!uname) return nil;
159
160	pdata = [private dataUsingEncoding:NSUTF8StringEncoding];
161	result = [self keyWithPrivateData:pdata depth:depth usageName:uname];
162	return result;
163}
164
165/*
166 * The standard way of creating a new key given a public key string.
167 */
168+ keyWithPublicKeyString:(NSString *)hexstr
169{
170	NSFEEPublicKey 		*result;
171	feeReturn		frtn;
172	NSStringEncoding	defEndoding;
173	const char 		*s;
174
175	/*
176	 * Protect against gross errors in the key string formatting...
177	 */
178	defEndoding = [NSString defaultCStringEncoding];
179	if([hexstr canBeConvertedToEncoding:defEndoding] == NO) {
180		NSLog(@"NSFEEPublicKey: Bad Public Key String Format (1)\n");
181		return nil;
182	}
183
184	/*
185	 * FIXME - docs say this string is "autoreleased". How is a cString
186	 * autoreleased?
187	 */
188	s = [hexstr cString];
189	result = [[self alloc] autorelease];
190	result->_pubKey = feePubKeyAlloc();
191
192	frtn = feePubKeyInitFromKeyString(result->_pubKey,
193		s, strlen(s));
194	if(frtn) {
195		NSLog(@"keyWithPublicKeyString:andKey: %s\n",
196			feeReturnString(frtn));
197		return nil;
198	}
199	return result;
200}
201
202- (void)dealloc
203{
204	if(_pubKey) {
205		feePubKeyFree(_pubKey);
206	}
207	[super dealloc];
208}
209
210/*
211 * Create a public key in the form of a string. This string contains an
212 * encoded version of all of our ivars except for _private.
213 *
214 * See KeyStringFormat.doc for info on the format of the public key string;
215 * PLEASE UPDATE THIS DOCUMENT WHEN YOU MAKE CHANGES TO THE STRING FORMAT.
216 */
217- (NSString *)publicKeyString
218{
219	char		*keyStr;
220	unsigned	keyStrLen;
221	feeReturn	frtn;
222	NSString 	*result;
223
224	if(_pubKey == NULL) {
225		return nil;
226	}
227	frtn = feePubKeyCreateKeyString(_pubKey, &keyStr, &keyStrLen);
228	if(frtn) {
229		NSLog(@"publicKeyString: %s\n",
230			feeReturnString(frtn));
231		return nil;
232	}
233	result = [NSString stringWithCString:keyStr];
234	ffree((void *)keyStr);
235	return result;
236}
237
238- (BOOL)isEqual:(NSFEEPublicKey *)other
239{
240	if((other == nil) || (other->_pubKey == NULL) || (_pubKey == NULL)) {
241		return NO;
242	}
243	if(feePubKeyIsEqual(_pubKey, other->_pubKey)) {
244		return YES;
245	}
246	else {
247		return NO;
248	}
249}
250
251- (unsigned)keyBitsize
252{
253	if(_pubKey == NULL) {
254		return 0;
255	}
256	return feePubKeyBitsize(_pubKey);
257}
258
259- (NSString *)algorithmName
260{
261	return [NSString stringWithCString:feePubKeyAlgorithmName()];
262}
263
264- (NSString *)usageName
265{
266	unsigned unameLen;
267	const feeUnichar *uname;
268	NSString *result;
269
270	if(_pubKey == NULL) {
271		return nil;
272	}
273	uname = feePubKeyUsageName(_pubKey, &unameLen);
274	result = [NSString stringWithCharacters:uname length:unameLen];
275	return result;
276}
277
278- (NSString *)signer
279{
280	return [self usageName];
281}
282
283- (NSData *)padWithPublicKey:(id <NSObject,NSPublicKey>)otherKey
284{
285	NSFEEPublicKey *other;
286	NSMutableData *result;
287    	feeReturn frtn;
288	unsigned char *padData;
289	unsigned padDataLen;
290
291	if(_pubKey == NULL) {
292		return nil;
293	}
294	if (![otherKey isMemberOfClass:isa]) {
295		return nil;
296	}
297	other = otherKey;
298	if(other->_pubKey == NULL) {
299		return nil;
300	}
301	frtn = feePubKeyCreatePad(_pubKey,
302		other->_pubKey,
303		&padData,
304		&padDataLen);
305	if(frtn) {
306		NSLog(@"padWithPublicKey: %s\n", feeReturnString(frtn));
307		return nil;
308	}
309	result = [NSData dataWithBytesNoCopy:padData length:padDataLen];
310	return result;
311}
312
313- (NSData *)encryptData:(NSData *)data
314{
315	feeFEEDExp	feed;
316	NSData		*result;
317	feeReturn	frtn;
318	unsigned char	*ctext;
319	unsigned	ctextLen;
320
321	if(_pubKey == NULL) {
322		return nil;
323	}
324	feed = feeFEEDExpNewWithPubKey(_pubKey);
325	frtn = feeFEEDExpEncrypt(feed,
326		[data bytes],
327		[data length],
328		&ctext,
329		&ctextLen);
330	if(frtn == FR_Success) {
331		result = [NSData dataWithBytesNoCopy:ctext length:ctextLen];
332	}
333	else {
334		NSLog(@"feeFEEDEncrypt: %s\n", feeReturnString(frtn));
335		result = nil;
336	}
337	feeFEEDExpFree(feed);
338	return result;
339}
340
341- (NSData *)decryptData:(NSData *)data
342{
343	feeFEEDExp	feed;
344	NSData		*result;
345	feeReturn	frtn;
346	unsigned char	*ptext;
347	unsigned	ptextLen;
348
349	if(_pubKey == NULL) {
350		return nil;
351	}
352	feed = feeFEEDExpNewWithPubKey(_pubKey);
353	frtn = feeFEEDExpDecrypt(feed,
354		[data bytes],
355		[data length],
356		&ptext,
357		&ptextLen);
358	if(frtn == FR_Success) {
359		result = [NSData dataWithBytesNoCopy:ptext length:ptextLen];
360	}
361	else {
362		NSLog(@"feeFEEDDecrypt: %s\n", feeReturnString(frtn));
363		result = nil;
364	}
365	feeFEEDExpFree(feed);
366	return result;
367}
368
369/*
370 * When 1, we use ECDSA unless we're using a depth which does not
371 * have curve orders.
372 * WARNING - enabling ECDSA by default breaks ICE and compatibility
373 * with Java signatures, at least until we have a Java ECDSA
374 * implementation.
375 */
376#define ECDSA_SIG_DEFAULT	0
377
378- (NSData *)digitalSignatureForData:(NSData *)data
379{
380	NSData		*result;
381	unsigned char	*sig;
382	unsigned	sigLen;
383	feeReturn	frtn;
384	curveParams	*cp;
385
386	if(_pubKey == NULL) {
387		return nil;
388	}
389	cp = feePubKeyCurveParams(_pubKey);
390	if(!ECDSA_SIG_DEFAULT || isZero(cp->x1OrderPlus)) {
391	    frtn = feePubKeyCreateSignature(_pubKey,
392		[data bytes],
393		[data length],
394		&sig,
395		&sigLen);
396	}
397	else {
398	    frtn = feePubKeyCreateECDSASignature(_pubKey,
399		[data bytes],
400		[data length],
401		&sig,
402		&sigLen);
403	}
404	if(frtn) {
405		NSLog(@"digitalSignatureForData: %s\n", feeReturnString(frtn));
406		return nil;
407	}
408	result = [NSData dataWithBytesNoCopy:sig length:sigLen];
409	return result;
410}
411
412- (BOOL)isValidDigitalSignature:(NSData *)signa
413	forData:(NSData *)data
414{
415	feeReturn	frtn;
416	feeUnichar 	*sigSigner;
417	unsigned	sigSignerLen;
418	curveParams	*cp;
419
420	if(_pubKey == NULL) {
421		return NO;
422	}
423	cp = feePubKeyCurveParams(_pubKey);
424	if(!ECDSA_SIG_DEFAULT || isZero(cp->x1OrderPlus)) {
425	    frtn = feePubKeyVerifySignature(_pubKey,
426		[data bytes],
427		[data length],
428		[signa bytes],
429		[signa length],
430		&sigSigner,
431		&sigSignerLen);
432	}
433	else {
434	    frtn = feePubKeyVerifyECDSASignature(_pubKey,
435		[data bytes],
436		[data length],
437		[signa bytes],
438		[signa length],
439		&sigSigner,
440		&sigSignerLen);
441	}
442
443	/*
444	 * FIXME - We just throw away the signer for now...
445	 */
446	if(sigSignerLen) {
447		ffree(sigSigner);
448	}
449
450	switch(frtn) {
451	    case FR_Success:
452	    	return YES;
453	    case FR_InvalidSignature:
454	    	return NO;
455	    default:
456	    	/*
457		 * Something other than simple signature mismatch...
458		 */
459		NSLog(@"isValidDigitalSignature: %s\n", feeReturnString(frtn));
460		return NO;
461	}
462}
463
464@end
465
466@implementation NSFEEPublicKey(Private)
467
468- (key)minus
469{
470	if(_pubKey == NULL) {
471		return NULL;
472	}
473	return feePubKeyMinusCurve(_pubKey);
474}
475
476- (key)plus
477{
478	if(_pubKey == NULL) {
479		return NULL;
480	}
481	return feePubKeyPlusCurve(_pubKey);
482}
483
484- (feePubKey)feePubKey
485{
486	return _pubKey;
487}
488
489#if 	FEE_DEBUG
490- (void)dump
491{
492	printPubKey(_pubKey);
493}
494#endif	FEE_DEBUG
495
496@end
497