Skip to content

Commit 5cb58b8

Browse files
GULSecureCoding introduced. (#3707)
* GULKeyedArchiver introduced. * Bump GoogleUtilities version. * Rename GULKeyedArchiver to GULSecureCodingTests. API cleanup. * API notes. * ./scripts/style.sh * GoogleUtilities version 6.3.0
1 parent 89bae97 commit 5cb58b8

File tree

4 files changed

+223
-1
lines changed

4 files changed

+223
-1
lines changed

GoogleUtilities.podspec

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'GoogleUtilities'
3-
s.version = '6.2.5'
3+
s.version = '6.3.0'
44
s.summary = 'Google Utilities for iOS (plus community support for macOS and tvOS)'
55

66
s.description = <<-DESC
@@ -104,6 +104,11 @@ other Google CocoaPods. They're not intended for direct public usage.
104104
ud.dependency 'GoogleUtilities/Logger'
105105
end
106106

107+
s.subspec 'SecureCoding' do |sc|
108+
sc.source_files = 'GoogleUtilities/SecureCoding/**/*.[hm]'
109+
sc.public_header_files = 'GoogleUtilities/SecureCoding/Public/*.h'
110+
end
111+
107112
s.test_spec 'unit' do |unit_tests|
108113
# All tests require arc except Tests/Network/third_party/GTMHTTPServer.m
109114
unit_tests.source_files = 'GoogleUtilities/Example/Tests/**/*.[mh]'
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2019 Google
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import <XCTest/XCTest.h>
16+
17+
#import <GoogleUtilities/GULSecureCoding.h>
18+
19+
@interface SecureCodingIncompatibleObject : NSObject <NSCoding>
20+
@end
21+
22+
@implementation SecureCodingIncompatibleObject
23+
24+
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
25+
}
26+
27+
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
28+
return [self init];
29+
}
30+
31+
@end
32+
33+
@interface GULSecureCodingTests : XCTestCase
34+
35+
@end
36+
37+
@implementation GULSecureCodingTests
38+
39+
- (void)testArchiveUnarchive {
40+
NSDictionary *objectToArchive = @{@"key1" : @"value1", @"key2" : @(2)};
41+
42+
NSError *error;
43+
NSData *archiveData = [GULSecureCoding archivedDataWithRootObject:objectToArchive error:&error];
44+
XCTAssertNil(error);
45+
XCTAssertNotNil(archiveData);
46+
47+
NSDictionary *unarchivedObject = [GULSecureCoding unarchivedObjectOfClass:[NSDictionary class]
48+
fromData:archiveData
49+
error:&error];
50+
XCTAssertNil(error);
51+
XCTAssert([objectToArchive isEqualToDictionary:unarchivedObject]);
52+
}
53+
54+
- (void)testArchivingIncompatibleObjectError {
55+
SecureCodingIncompatibleObject *objectToArchive = [[SecureCodingIncompatibleObject alloc] init];
56+
57+
NSError *error;
58+
NSData *archiveData = [GULSecureCoding archivedDataWithRootObject:objectToArchive error:&error];
59+
XCTAssertNotNil(error);
60+
XCTAssertNil(archiveData);
61+
}
62+
63+
- (void)testUnarchivingClassMismatchError {
64+
NSDictionary *objectToArchive = @{@"key1" : @"value1", @"key2" : @(2)};
65+
NSError *error;
66+
NSData *archiveData = [GULSecureCoding archivedDataWithRootObject:objectToArchive error:&error];
67+
XCTAssertNil(error);
68+
XCTAssertNotNil(archiveData);
69+
70+
NSArray *unarchivedObject = [GULSecureCoding unarchivedObjectOfClass:[NSArray class]
71+
fromData:archiveData
72+
error:&error];
73+
XCTAssertNotNil(error);
74+
XCTAssertNil(unarchivedObject);
75+
}
76+
77+
- (void)testUnarchivingCorruptedDataError {
78+
NSData *corruptedData = [@"abc" dataUsingEncoding:NSUTF8StringEncoding];
79+
NSError *error;
80+
NSString *unarchivedObject = [GULSecureCoding unarchivedObjectOfClass:[NSString class]
81+
fromData:corruptedData
82+
error:&error];
83+
XCTAssertNotNil(error);
84+
XCTAssertNil(unarchivedObject);
85+
}
86+
87+
- (void)testArchiveUnarchiveWithNULLError {
88+
SecureCodingIncompatibleObject *objectToArchive = [[SecureCodingIncompatibleObject alloc] init];
89+
90+
NSData *archiveData = [GULSecureCoding archivedDataWithRootObject:objectToArchive error:NULL];
91+
XCTAssertNil(archiveData);
92+
93+
NSData *corruptedData = [@"abc" dataUsingEncoding:NSUTF8StringEncoding];
94+
NSDictionary *unarchivedObject = [GULSecureCoding unarchivedObjectOfClass:[NSDictionary class]
95+
fromData:corruptedData
96+
error:NULL];
97+
XCTAssertNil(unarchivedObject);
98+
}
99+
100+
@end
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2019 Google
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import "Public/GULSecureCoding.h"
16+
17+
NSString *const kGULSecureCodingError = @"GULSecureCodingError";
18+
19+
@implementation GULSecureCoding
20+
21+
+ (nullable id)unarchivedObjectOfClass:(Class)class
22+
fromData:(NSData *)data
23+
error:(NSError **)outError {
24+
id object;
25+
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
26+
object = [NSKeyedUnarchiver unarchivedObjectOfClass:class fromData:data error:outError];
27+
} else {
28+
@try {
29+
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
30+
unarchiver.requiresSecureCoding = YES;
31+
32+
object = [unarchiver decodeObjectOfClass:class forKey:NSKeyedArchiveRootObjectKey];
33+
} @catch (NSException *exception) {
34+
if (outError) {
35+
*outError = [self archivingErrorWithException:exception];
36+
}
37+
}
38+
39+
if (object == nil && outError && *outError == nil) {
40+
NSString *failureReason = @"NSKeyedUnarchiver failed to unarchive data.";
41+
*outError = [NSError errorWithDomain:kGULSecureCodingError
42+
code:-1
43+
userInfo:@{NSLocalizedFailureReasonErrorKey : failureReason}];
44+
}
45+
}
46+
47+
return object;
48+
}
49+
50+
+ (nullable NSData *)archivedDataWithRootObject:(id<NSCoding>)object error:(NSError **)outError {
51+
NSData *archiveData;
52+
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
53+
archiveData = [NSKeyedArchiver archivedDataWithRootObject:object
54+
requiringSecureCoding:YES
55+
error:outError];
56+
} else {
57+
@try {
58+
NSMutableData *data = [NSMutableData data];
59+
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
60+
archiver.requiresSecureCoding = YES;
61+
62+
[archiver encodeObject:object forKey:NSKeyedArchiveRootObjectKey];
63+
[archiver finishEncoding];
64+
65+
archiveData = [data copy];
66+
} @catch (NSException *exception) {
67+
if (outError) {
68+
*outError = [self archivingErrorWithException:exception];
69+
}
70+
}
71+
}
72+
73+
return archiveData;
74+
}
75+
76+
+ (NSError *)archivingErrorWithException:(NSException *)exception {
77+
NSString *failureReason = [NSString
78+
stringWithFormat:@"NSKeyedArchiver exception with name: %@, reason: %@, userInfo: %@",
79+
exception.name, exception.reason, exception.userInfo];
80+
NSDictionary *errorUserInfo = @{NSLocalizedFailureReasonErrorKey : failureReason};
81+
82+
return [NSError errorWithDomain:kGULSecureCodingError code:-1 userInfo:errorUserInfo];
83+
}
84+
85+
@end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2019 Google
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import <Foundation/Foundation.h>
16+
17+
NS_ASSUME_NONNULL_BEGIN
18+
19+
/** The class wraps `NSKeyedArchiver` and `NSKeyedUnarchiver` API to provide a unified secure coding
20+
* methods for iOS versions before and after 11.
21+
*/
22+
@interface GULSecureCoding : NSObject
23+
24+
+ (nullable id)unarchivedObjectOfClass:(Class)class
25+
fromData:(NSData *)data
26+
error:(NSError **)outError;
27+
28+
+ (nullable NSData *)archivedDataWithRootObject:(id<NSCoding>)object error:(NSError **)outError;
29+
30+
@end
31+
32+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)