snej@8
|
1 |
//
|
snej@8
|
2 |
// MYEncoder.m
|
snej@8
|
3 |
// MYCrypto
|
snej@8
|
4 |
//
|
snej@8
|
5 |
// Created by Jens Alfke on 1/16/08.
|
snej@8
|
6 |
// Copyright 2008-2009 Jens Alfke. All rights reserved.
|
snej@8
|
7 |
//
|
snej@8
|
8 |
|
snej@8
|
9 |
#import "MYEncoder.h"
|
snej@8
|
10 |
#import "MYIdentity.h"
|
snej@8
|
11 |
#import "MYCrypto_Private.h"
|
snej@8
|
12 |
#import "Test.h"
|
snej@8
|
13 |
#import "MYErrorUtils.h"
|
snej@8
|
14 |
|
snej@8
|
15 |
|
snej@8
|
16 |
@implementation MYEncoder
|
snej@8
|
17 |
|
snej@8
|
18 |
|
snej@8
|
19 |
- (id) init
|
snej@8
|
20 |
{
|
snej@8
|
21 |
self = [super init];
|
snej@8
|
22 |
if (self != nil) {
|
snej@8
|
23 |
if( ! checksave(CMSEncoderCreate(&_encoder)) ) {
|
snej@8
|
24 |
[self release];
|
snej@8
|
25 |
return nil;
|
snej@8
|
26 |
}
|
snej@8
|
27 |
}
|
snej@8
|
28 |
return self;
|
snej@8
|
29 |
}
|
snej@8
|
30 |
|
snej@8
|
31 |
- (void) dealloc
|
snej@8
|
32 |
{
|
snej@8
|
33 |
if(_encoder) CFRelease(_encoder);
|
snej@8
|
34 |
[super dealloc];
|
snej@8
|
35 |
}
|
snej@8
|
36 |
|
snej@8
|
37 |
|
snej@8
|
38 |
|
snej@8
|
39 |
- (BOOL) addSigner: (MYIdentity*)signer
|
snej@8
|
40 |
{
|
snej@8
|
41 |
Assert(signer);
|
snej@8
|
42 |
return checksave( CMSEncoderAddSigners(_encoder, signer.identityRef) );
|
snej@8
|
43 |
}
|
snej@8
|
44 |
|
snej@8
|
45 |
- (BOOL) addRecipient: (MYCertificate*)recipient
|
snej@8
|
46 |
{
|
snej@8
|
47 |
Assert(recipient);
|
snej@8
|
48 |
return checksave( CMSEncoderAddRecipients(_encoder, recipient.certificateRef) );
|
snej@8
|
49 |
}
|
snej@8
|
50 |
|
snej@8
|
51 |
- (BOOL) addSupportingCert: (MYCertificate*)supportingCert
|
snej@8
|
52 |
{
|
snej@8
|
53 |
Assert(supportingCert);
|
snej@8
|
54 |
return checksave( CMSEncoderAddSupportingCerts(_encoder, supportingCert.certificateRef) );
|
snej@8
|
55 |
}
|
snej@8
|
56 |
|
snej@8
|
57 |
- (BOOL) addTimestamp
|
snej@8
|
58 |
{
|
snej@8
|
59 |
return checksave( CMSEncoderAddSignedAttributes(_encoder, kCMSAttrSigningTime) );
|
snej@8
|
60 |
}
|
snej@8
|
61 |
|
snej@8
|
62 |
|
snej@8
|
63 |
- (NSError*) error
|
snej@8
|
64 |
{
|
snej@8
|
65 |
if( _error )
|
snej@8
|
66 |
return MYError(_error, NSOSStatusErrorDomain,
|
snej@8
|
67 |
@"%@", MYErrorName(NSOSStatusErrorDomain,_error));
|
snej@8
|
68 |
else
|
snej@8
|
69 |
return nil;
|
snej@8
|
70 |
}
|
snej@8
|
71 |
|
snej@8
|
72 |
|
snej@8
|
73 |
- (CMSCertificateChainMode) certificateChainMode
|
snej@8
|
74 |
{
|
snej@8
|
75 |
CMSCertificateChainMode mode;
|
snej@8
|
76 |
if( CMSEncoderGetCertificateChainMode(_encoder, &mode) == noErr )
|
snej@8
|
77 |
return mode;
|
snej@8
|
78 |
else
|
snej@8
|
79 |
return -1;
|
snej@8
|
80 |
}
|
snej@8
|
81 |
|
snej@8
|
82 |
- (void) setCertificateChainMode: (CMSCertificateChainMode)mode
|
snej@8
|
83 |
{
|
snej@8
|
84 |
checksave( CMSEncoderSetCertificateChainMode(_encoder, mode) );
|
snej@8
|
85 |
}
|
snej@8
|
86 |
|
snej@8
|
87 |
- (BOOL) hasDetachedContent
|
snej@8
|
88 |
{
|
snej@8
|
89 |
Boolean detached;
|
snej@8
|
90 |
return CMSEncoderGetHasDetachedContent(_encoder, &detached)==noErr && detached;
|
snej@8
|
91 |
}
|
snej@8
|
92 |
|
snej@8
|
93 |
- (void) setHasDetachedContent: (BOOL)detached
|
snej@8
|
94 |
{
|
snej@8
|
95 |
checksave( CMSEncoderSetHasDetachedContent(_encoder, detached) );
|
snej@8
|
96 |
}
|
snej@8
|
97 |
|
snej@8
|
98 |
- (NSData*) _dataFromFunction: (OSStatus (*)(CMSEncoderRef,CFDataRef*))function
|
snej@8
|
99 |
{
|
snej@8
|
100 |
CFDataRef data=NULL;
|
snej@8
|
101 |
if( checksave( (*function)(_encoder, &data) ) )
|
snej@8
|
102 |
return [(NSData*)CFMakeCollectable(data) autorelease];
|
snej@8
|
103 |
else
|
snej@8
|
104 |
return nil;
|
snej@8
|
105 |
}
|
snej@8
|
106 |
|
snej@8
|
107 |
|
snej@8
|
108 |
- (CSSM_OID) contentType
|
snej@8
|
109 |
{
|
snej@8
|
110 |
NSData *data = [self _dataFromFunction: &CMSEncoderCopyEncapsulatedContentType];
|
snej@8
|
111 |
return (CSSM_OID){data.length,(uint8*)data.bytes};
|
snej@8
|
112 |
}
|
snej@8
|
113 |
|
snej@8
|
114 |
- (void) setContentType: (CSSM_OID)contentType
|
snej@8
|
115 |
{
|
snej@8
|
116 |
checksave( CMSEncoderSetEncapsulatedContentType(_encoder, &contentType) );
|
snej@8
|
117 |
}
|
snej@8
|
118 |
|
snej@8
|
119 |
|
snej@8
|
120 |
- (BOOL) addData: (NSData*)data
|
snej@8
|
121 |
{
|
snej@8
|
122 |
Assert(data);
|
snej@8
|
123 |
return ! _error && checksave( CMSEncoderUpdateContent(_encoder, data.bytes, data.length) );
|
snej@8
|
124 |
}
|
snej@8
|
125 |
|
snej@8
|
126 |
|
snej@8
|
127 |
- (NSData*) encodedData
|
snej@8
|
128 |
{
|
snej@8
|
129 |
if( ! _error )
|
snej@8
|
130 |
return [self _dataFromFunction: &CMSEncoderCopyEncodedContent];
|
snej@8
|
131 |
else
|
snej@8
|
132 |
return nil;
|
snej@8
|
133 |
}
|
snej@8
|
134 |
|
snej@8
|
135 |
|
snej@8
|
136 |
+ (NSData*) encodeData: (NSData*)data
|
snej@8
|
137 |
signer: (MYIdentity*)signer
|
snej@8
|
138 |
recipient: (MYCertificate*)recipient
|
snej@8
|
139 |
error: (NSError**)outError
|
snej@8
|
140 |
{
|
snej@8
|
141 |
MYEncoder *e = [[self alloc] init];
|
snej@8
|
142 |
if( signer )
|
snej@8
|
143 |
[e addSigner: signer];
|
snej@8
|
144 |
if( recipient )
|
snej@8
|
145 |
[e addRecipient: recipient];
|
snej@8
|
146 |
[e addData: data];
|
snej@8
|
147 |
*outError = e.error;
|
snej@8
|
148 |
NSData *result = e.encodedData;
|
snej@8
|
149 |
[e release];
|
snej@8
|
150 |
return result;
|
snej@8
|
151 |
}
|
snej@8
|
152 |
|
snej@8
|
153 |
|
snej@8
|
154 |
@end
|
snej@8
|
155 |
|
snej@8
|
156 |
|
snej@8
|
157 |
#if DEBUG
|
snej@8
|
158 |
|
snej@8
|
159 |
#import "MYCrypto+Cocoa.h"
|
snej@8
|
160 |
|
snej@8
|
161 |
TestCase(MYEncoder) {
|
snej@8
|
162 |
MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
|
snej@8
|
163 |
if (!me) {
|
snej@8
|
164 |
NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
|
snej@8
|
165 |
SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
|
snej@8
|
166 |
[panel setAlternateButtonTitle: @"Cancel"];
|
snej@8
|
167 |
if ([panel my_runModalForIdentities: idents
|
snej@8
|
168 |
message: @"Choose an identity for the MYEncoder test case:"]
|
snej@8
|
169 |
!= NSOKButton) {
|
snej@8
|
170 |
[NSException raise: NSGenericException format: @"User canceled"];
|
snej@8
|
171 |
}
|
snej@8
|
172 |
me = [panel my_identity];
|
snej@8
|
173 |
[me makePreferredIdentityForName: @"MYCryptoTest"];
|
snej@8
|
174 |
}
|
snej@8
|
175 |
CAssert(me,@"No default identity has been set up in the Keychain");
|
snej@8
|
176 |
|
snej@8
|
177 |
NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
|
snej@8
|
178 |
CAssert(source);
|
snej@8
|
179 |
|
snej@8
|
180 |
NSError *error;
|
snej@8
|
181 |
NSData *encoded;
|
snej@8
|
182 |
|
snej@8
|
183 |
Log(@"Testing signing...");
|
snej@8
|
184 |
encoded = [MYEncoder encodeData: source signer: me recipient: nil error: &error];
|
snej@8
|
185 |
CAssertEq(error,nil);
|
snej@8
|
186 |
CAssert([encoded length]);
|
snej@8
|
187 |
Log(@"MYEncoder signed %u bytes into %u bytes", source.length,encoded.length);
|
snej@8
|
188 |
|
snej@8
|
189 |
Log(@"Testing encryption...");
|
snej@8
|
190 |
encoded = [MYEncoder encodeData: source signer: nil recipient: me error: &error];
|
snej@8
|
191 |
CAssertEq(error,nil);
|
snej@8
|
192 |
CAssert([encoded length]);
|
snej@8
|
193 |
Log(@"MYEncoder encrypted %u bytes into %u bytes", source.length,encoded.length);
|
snej@8
|
194 |
|
snej@8
|
195 |
Log(@"Testing signing+encryption...");
|
snej@8
|
196 |
encoded = [MYEncoder encodeData: source signer: me recipient: me error: &error];
|
snej@8
|
197 |
CAssertEq(error,nil);
|
snej@8
|
198 |
CAssert([encoded length]);
|
snej@8
|
199 |
Log(@"MYEncoder signed/encrypted %u bytes into %u bytes", source.length,encoded.length);
|
snej@8
|
200 |
}
|
snej@8
|
201 |
#endif
|