- Upgrade to 1.0
This commit is contained in:
1 parent
4e66c377fd
commit
0c2b3f1c25
452 files changed
+33888
-16806
No files matched your search
@@ -1,6 +1,4 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Gabriel Handford
|
||||
Copyright (c) 2014 axldyb <aksel.dybdal@shortcut.no>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -9,14 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// DDDKeychainWrapper.h
|
||||
// Aksel Dybdal
|
||||
//
|
||||
// Created by Aksel Dybdal on 02.04.14.
|
||||
// Copyright (c) 2014 Aksel Dybdal. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
A small wrapper for Keychain access.
|
||||
|
||||
Inspired by:
|
||||
http://useyourloaf.com/blog/2010/03/29/simple-iphone-keychain-access.html
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface DDDKeychainWrapper : NSObject
|
||||
|
||||
+ (void)setString:(NSString *)string forKey:(NSString *)key;
|
||||
+ (NSString *)stringForKey:(NSString *)key;
|
||||
|
||||
+ (void)setDate:(NSDate *)date forKey:(NSString *)key;
|
||||
+ (NSDate *)dateForKey:(NSString *)key;
|
||||
|
||||
+ (void)setData:(NSData *)data forKey:(NSString *)key;
|
||||
+ (NSData *)dataForKey:(NSString *)key;
|
||||
|
||||
+ (void)setArray:(NSArray *)array forKey:(NSString *)key;
|
||||
+ (NSArray *)arrayForKey:(NSString *)key;
|
||||
|
||||
+ (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key;
|
||||
+ (NSDictionary *)dictionaryForKey:(NSString *)key;
|
||||
|
||||
+ (void)setNumber:(NSNumber *)number forKey:(NSString *)key;
|
||||
+ (NSNumber *)numberForKey:(NSString *)key;
|
||||
|
||||
+ (void)setBoolean:(BOOL)boolean forKey:(NSString *)key;
|
||||
+ (BOOL)booleanForKey:(NSString *)key;
|
||||
|
||||
+ (void)setObject:(id)object forKey:(NSString *)key;
|
||||
+ (id)objectForKey:(NSString *)key;
|
||||
|
||||
+ (void)wipeKeychain;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,373 @@
|
||||
//
|
||||
// DDDKeychainWrapper.m
|
||||
// Aksel Dybdal
|
||||
//
|
||||
// Created by Aksel Dybdal on 02.04.14.
|
||||
// Copyright (c) 2014 Aksel Dybdal. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DDDKeychainWrapper.h"
|
||||
#import <Security/Security.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, DDDKeychainWrapperErrorCode) {
|
||||
DDDKeychainWrapperErrorCreatingKeychainValue = 1,
|
||||
DDDKeychainWrapperErrorUpdatingKeychainValue,
|
||||
DDDKeychainWrapperErrorDeletingKeychainValue,
|
||||
DDDKeychainWrapperErrorSearchingKeychainValue
|
||||
};
|
||||
|
||||
NSString *const kDDDKeychainWrapperServiceName = @"com.dddkeychainwrapper.keychainService";
|
||||
NSString *const kDDDKeychainWrapperErrorDomain = @"DDDKeychainWrapperErrorDomain";
|
||||
|
||||
#ifdef DEBUG
|
||||
# warning "Including NSLog"
|
||||
# define DDDLOG(s, ...) NSLog(s, ## __VA_ARGS__)
|
||||
#else
|
||||
# define DDDLOG(s, ...) while(0){}
|
||||
#endif
|
||||
|
||||
@implementation DDDKeychainWrapper
|
||||
|
||||
|
||||
#pragma mark - String
|
||||
|
||||
+ (void)setString:(NSString *)string forKey:(NSString *)key
|
||||
{
|
||||
NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[self setData:stringData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSString *)stringForKey:(NSString *)key
|
||||
{
|
||||
NSData *stringData = [self dataForIdentifier:key];
|
||||
return [[NSString alloc] initWithData:stringData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Date
|
||||
|
||||
+ (void)setDate:(NSDate *)date forKey:(NSString *)key
|
||||
{
|
||||
NSData *dateData = [NSKeyedArchiver archivedDataWithRootObject:date];
|
||||
[self setData:dateData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSDate *)dateForKey:(NSString *)key
|
||||
{
|
||||
NSData *dateData = [self dataForIdentifier:key];
|
||||
return (NSDate *)[NSKeyedUnarchiver unarchiveObjectWithData:dateData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
+ (void)setData:(NSData *)data forKey:(NSString *)key
|
||||
{
|
||||
[self setData:data forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSData *)dataForKey:(NSString *)key
|
||||
{
|
||||
return [self dataForIdentifier:key];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Array
|
||||
|
||||
+ (void)setArray:(NSArray *)array forKey:(NSString *)key
|
||||
{
|
||||
for (id obj in array) {
|
||||
NSAssert([obj conformsToProtocol:@protocol(NSCoding)], @"Objects must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
}
|
||||
|
||||
NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:array];
|
||||
[self setData:arrayData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSArray *)arrayForKey:(NSString *)key
|
||||
{
|
||||
NSData *arrayData = [self dataForIdentifier:key];
|
||||
return (NSArray *)[NSKeyedUnarchiver unarchiveObjectWithData:arrayData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Dictionary
|
||||
|
||||
+ (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key
|
||||
{
|
||||
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
NSAssert([key conformsToProtocol:@protocol(NSCoding)], @"Keys must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
NSAssert([obj conformsToProtocol:@protocol(NSCoding)], @"Objects must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
}];
|
||||
|
||||
NSData *dictionaryData = [NSKeyedArchiver archivedDataWithRootObject:dictionary];
|
||||
[self setData:dictionaryData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)dictionaryForKey:(NSString *)key
|
||||
{
|
||||
NSData *dictionaryData = [self dataForIdentifier:key];
|
||||
return (NSDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData:dictionaryData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Number
|
||||
|
||||
+ (void)setNumber:(NSNumber *)number forKey:(NSString *)key
|
||||
{
|
||||
NSData *numberData = [NSKeyedArchiver archivedDataWithRootObject:number];
|
||||
[self setData:numberData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSNumber *)numberForKey:(NSString *)key
|
||||
{
|
||||
NSData *numberData = [self dataForIdentifier:key];
|
||||
return (NSNumber *)[NSKeyedUnarchiver unarchiveObjectWithData:numberData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - BOOL
|
||||
|
||||
+ (void)setBoolean:(BOOL)boolean forKey:(NSString *)key
|
||||
{
|
||||
NSNumber *boolNumber = [NSNumber numberWithBool:boolean];
|
||||
[self setNumber:boolNumber forKey:key];
|
||||
}
|
||||
|
||||
+ (BOOL)booleanForKey:(NSString *)key
|
||||
{
|
||||
NSNumber *boolNumber = [self numberForKey:key];
|
||||
return [boolNumber boolValue];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Object
|
||||
|
||||
+ (void)setObject:(id)object forKey:(NSString *)key
|
||||
{
|
||||
NSAssert([object conformsToProtocol:@protocol(NSCoding)], @"Object must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
|
||||
NSData *objectData = [NSKeyedArchiver archivedDataWithRootObject:object];
|
||||
[self setData:objectData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (id)objectForKey:(NSString *)key
|
||||
{
|
||||
NSData *objectData = [self dataForIdentifier:key];
|
||||
return (id)[NSKeyedUnarchiver unarchiveObjectWithData:objectData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Clear Keychain
|
||||
|
||||
+ (void)wipeKeychain
|
||||
{
|
||||
NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword,
|
||||
(__bridge id)kSecClassInternetPassword,
|
||||
(__bridge id)kSecClassCertificate,
|
||||
(__bridge id)kSecClassKey,
|
||||
(__bridge id)kSecClassIdentity];
|
||||
|
||||
for (id secItemClass in secItemClasses) {
|
||||
NSDictionary *spec = @{(__bridge id)kSecClass: (id)secItemClass};
|
||||
SecItemDelete((__bridge CFDictionaryRef)spec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (void)setData:(NSData *)data forIdentifier:(NSString *)identifier
|
||||
{
|
||||
NSError *error = nil;
|
||||
|
||||
// If no data provided we assume we want to delete the value
|
||||
if (nil == data || NO == [data bytes]) {
|
||||
[self deleteKeychainValueForIdentifier:identifier error:&error];
|
||||
if (error) {
|
||||
DDDLOG(@"Error deleting keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We first look up the key in order to see if we need to update or create the value
|
||||
if ([self searchKeychainCopyMatching:identifier error:&error]) {
|
||||
if (error) {
|
||||
DDDLOG(@"Error finding keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self updateKeychainValue:data forIdentifier:identifier error:&error]) {
|
||||
DDDLOG(@"Error updating keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
}
|
||||
} else {
|
||||
if (![self createKeychainValue:data forIdentifier:identifier error:&error]) {
|
||||
DDDLOG(@"Error creating keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSData *)dataForIdentifier:(NSString *)identifier
|
||||
{
|
||||
NSError *error = nil;
|
||||
NSData *stringData = [self searchKeychainCopyMatching:identifier error:&error];
|
||||
if (error) {
|
||||
DDDLOG(@"Error finding keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
return nil;
|
||||
}
|
||||
return stringData;
|
||||
}
|
||||
|
||||
+ (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
|
||||
|
||||
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
|
||||
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
|
||||
[searchDictionary setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
|
||||
[searchDictionary setObject:kDDDKeychainWrapperServiceName forKey:(__bridge id)kSecAttrService];
|
||||
|
||||
return searchDictionary;
|
||||
}
|
||||
|
||||
+ (NSData *)searchKeychainCopyMatching:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
|
||||
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
|
||||
[searchDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
|
||||
|
||||
CFDataRef result;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, (CFTypeRef *)&result);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorSearchingKeychainValue];
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *data = (__bridge NSData *)result;
|
||||
return data;
|
||||
}
|
||||
|
||||
+ (BOOL)createKeychainValue:(NSData *)data forIdentifier:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
|
||||
[dictionary setObject:data forKey:(__bridge id)kSecValueData];
|
||||
|
||||
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorCreatingKeychainValue];
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)updateKeychainValue:(NSData *)data forIdentifier:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
|
||||
[updateDictionary setObject:data forKey:(__bridge id)kSecValueData];
|
||||
|
||||
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)searchDictionary,
|
||||
(__bridge CFDictionaryRef)updateDictionary);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorUpdatingKeychainValue];
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)deleteKeychainValueForIdentifier:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)searchDictionary);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorDeletingKeychainValue];
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Error
|
||||
|
||||
+ (void)keychainError:(NSError **)error forStatus:(OSStatus)status domain:(NSUInteger)domain
|
||||
{
|
||||
NSString *errorString = @"";
|
||||
|
||||
switch (status) {
|
||||
case errSecUnimplemented:
|
||||
errorString = @"Function or operation not implemented.";
|
||||
break;
|
||||
|
||||
case errSecIO:
|
||||
errorString = @"I/O error (bummers)";
|
||||
break;
|
||||
|
||||
case errSecOpWr:
|
||||
errorString = @"File already open with write permission";
|
||||
break;
|
||||
|
||||
case errSecParam:
|
||||
errorString = @"One or more parameters passed to a function where not valid.";
|
||||
break;
|
||||
|
||||
case errSecAllocate:
|
||||
errorString = @"Failed to allocate memory.";
|
||||
break;
|
||||
|
||||
case errSecUserCanceled:
|
||||
errorString = @"User canceled the operation.";
|
||||
break;
|
||||
|
||||
case errSecBadReq:
|
||||
errorString = @"Bad parameter or invalid state for operation.";
|
||||
break;
|
||||
|
||||
case errSecInternalComponent:
|
||||
errorString = @"errSecInternalComponent";
|
||||
break;
|
||||
|
||||
case errSecNotAvailable:
|
||||
errorString = @"No keychain is available. You may need to restart your computer.";
|
||||
break;
|
||||
|
||||
case errSecDuplicateItem:
|
||||
errorString = @"The specified item already exists in the keychain.";
|
||||
break;
|
||||
|
||||
case errSecItemNotFound:
|
||||
errorString = @"The specified item could not be found in the keychain.";
|
||||
break;
|
||||
|
||||
case errSecInteractionNotAllowed:
|
||||
errorString = @"User interaction is not allowed.";
|
||||
break;
|
||||
|
||||
case errSecDecode:
|
||||
errorString = @"Unable to decode the provided data.";
|
||||
break;
|
||||
|
||||
case errSecAuthFailed:
|
||||
errorString = @"The user name or passphrase you entered is not correct.";
|
||||
break;
|
||||
|
||||
default:
|
||||
errorString = @"Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
*error = [NSError errorWithDomain:kDDDKeychainWrapperErrorDomain
|
||||
code:domain
|
||||
userInfo:@{NSLocalizedDescriptionKey: errorString}];
|
||||
}
|
||||
|
||||
@end
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# DDDKeychainWrapper
|
||||
|
||||
[](https://travis-ci.org/axldyb/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
|
||||
## Usage
|
||||
|
||||
Storing your sensitive data in the keychain can take up a lot of time and effort. It should be easy to just drop something in there and retrive it with a few simple lines of code. DDDKeychainWrapper offers this simplicity and here is how we do it:
|
||||
|
||||
```objective-c
|
||||
// Writing to the Keychain
|
||||
[DDDKeychainWrapper setString:@"Secret string" forKey:@"my_key"];
|
||||
|
||||
// Reading from the Keychain
|
||||
NSString *secretString = [DDDKeychainWrapper stringForKey:@"my_key"];
|
||||
```
|
||||
|
||||
DDDKeychainWrapper has support for the following types
|
||||
|
||||
* NSString
|
||||
* NSDate
|
||||
* NSData
|
||||
* NSArray
|
||||
* NSDictionary
|
||||
* NSNumber
|
||||
* BOOL
|
||||
* Objects (Must confirm to NSCoding)
|
||||
|
||||
A method for wiping the keychain data inserted via the wrapper exists as well.
|
||||
|
||||
## Installation
|
||||
|
||||
DDDKeychainWrapper is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile:
|
||||
|
||||
pod "DDDKeychainWrapper"
|
||||
|
||||
## Author
|
||||
|
||||
axldyb, aksel.dybdal@shortcut.no
|
||||
|
||||
## License
|
||||
|
||||
DDDKeychainWrapper is available under the MIT license. See the LICENSE file for more info.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#import <Expecta/Expecta.h>
|
||||
#import "ExpectaObject+FBSnapshotTest.h"
|
||||
|
||||
@interface EXPExpectFBSnapshotTest : NSObject
|
||||
@end
|
||||
|
||||
/// Set the default folder for image tests to run in
|
||||
extern void setGlobalReferenceImageDir(char *reference);
|
||||
|
||||
EXPMatcherInterface(haveValidSnapshot, (void));
|
||||
EXPMatcherInterface(recordSnapshot, (void));
|
||||
|
||||
EXPMatcherInterface(haveValidSnapshotNamed, (NSString *snapshot));
|
||||
EXPMatcherInterface(recordSnapshotNamed, (NSString *snapshot));
|
||||
@@ -1,284 +0,0 @@
|
||||
#import "EXPMatchers+FBSnapshotTest.h"
|
||||
#import <Expecta/EXPMatcherHelpers.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
|
||||
@interface EXPExpectFBSnapshotTest()
|
||||
@property (nonatomic, strong) NSString *referenceImagesDirectory;
|
||||
@end
|
||||
|
||||
@implementation EXPExpectFBSnapshotTest
|
||||
|
||||
+ (id)instance
|
||||
{
|
||||
static EXPExpectFBSnapshotTest *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[self alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer snapshot:(NSString *)snapshot testCase:(id)testCase record:(BOOL)record referenceDirectory:(NSString *)referenceDirectory error:(NSError **)error
|
||||
|
||||
{
|
||||
FBSnapshotTestController *snapshotController = [[FBSnapshotTestController alloc] initWithTestClass:[testCase class]];
|
||||
snapshotController.recordMode = record;
|
||||
snapshotController.referenceImagesDirectory = referenceDirectory;
|
||||
snapshotController.usesDrawViewHierarchyInRect = [Expecta usesDrawViewHierarchyInRect];
|
||||
|
||||
if (! snapshotController.referenceImagesDirectory) {
|
||||
[NSException raise:@"Missing value for referenceImagesDirectory" format:@"Call [[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory"];
|
||||
}
|
||||
|
||||
return [snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
|
||||
selector:NSSelectorFromString(snapshot)
|
||||
identifier:nil
|
||||
tolerance:0
|
||||
error:error];
|
||||
}
|
||||
|
||||
+ (NSString *)combinedError:(NSString *)message test:(NSString *)test error:(NSError *)error
|
||||
{
|
||||
NSAssert(message, @"missing message");
|
||||
NSAssert(test, @"missing test name");
|
||||
|
||||
NSMutableArray *ary = [NSMutableArray array];
|
||||
|
||||
[ary addObject:[NSString stringWithFormat:@"%@ %@", message, test]];
|
||||
|
||||
for(NSString *key in error.userInfo.keyEnumerator) {
|
||||
[ary addObject:[NSString stringWithFormat:@" %@: %@", key, [error.userInfo valueForKey:key]]];
|
||||
}
|
||||
|
||||
return [ary componentsJoinedByString:@"\n"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void setGlobalReferenceImageDir(char *reference) {
|
||||
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%s", reference];
|
||||
[[EXPExpectFBSnapshotTest instance] setReferenceImagesDirectory:referenceImagesDirectory];
|
||||
};
|
||||
|
||||
@interface EXPExpect(ReferenceDirExtension)
|
||||
- (NSString *)_getDefaultReferenceDirectory;
|
||||
@end
|
||||
|
||||
@implementation EXPExpect(ReferenceDirExtension)
|
||||
|
||||
- (NSString *)_getDefaultReferenceDirectory
|
||||
{
|
||||
NSString *globalReference = [[EXPExpectFBSnapshotTest instance] referenceImagesDirectory];
|
||||
if (globalReference) {
|
||||
return globalReference;
|
||||
}
|
||||
|
||||
// Search the test file's path to find the first folder with the substring "tests"
|
||||
// then append "/ReferenceImages" and use that
|
||||
|
||||
NSString *testFileName = [NSString stringWithCString:self.fileName encoding:NSUTF8StringEncoding];
|
||||
NSArray *pathComponents = [testFileName pathComponents];
|
||||
|
||||
for (NSString *folder in pathComponents) {
|
||||
if ([folder.lowercaseString rangeOfString:@"tests"].location != NSNotFound) {
|
||||
|
||||
NSArray *folderPathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents indexOfObject:folder] + 1)];
|
||||
return [NSString stringWithFormat:@"%@/ReferenceImages", [folderPathComponents componentsJoinedByString:@"/"]];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[NSException raise:@"Could not infer reference image folder" format:@"You should provide a reference dir using setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);"];
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
#import <Specta/Specta.h>
|
||||
#import <Specta/SpectaUtility.h>
|
||||
#import <Specta/SPTExample.h>
|
||||
|
||||
NSString *sanitizedTestPath();
|
||||
|
||||
NSString *sanitizedTestPath(){
|
||||
id compiledExample = [[NSThread currentThread] threadDictionary][@"SPTCurrentSpec"]; // SPTSpec
|
||||
NSString *name;
|
||||
if ([compiledExample respondsToSelector:@selector(name)]) {
|
||||
// Specta 0.3 syntax
|
||||
name = [compiledExample performSelector:@selector(name)];
|
||||
} else if ([compiledExample respondsToSelector:@selector(fileName)]) {
|
||||
// Specta 0.2 syntax
|
||||
name = [compiledExample performSelector:@selector(fileName)];
|
||||
}
|
||||
name = [[[[name componentsSeparatedByString:@" test_"] lastObject] stringByReplacingOccurrencesOfString:@"__" withString:@"_"] stringByReplacingOccurrencesOfString:@"]" withString:@""];
|
||||
return name;
|
||||
}
|
||||
|
||||
EXPMatcherImplementationBegin(haveValidSnapshot, (void)){
|
||||
__block NSError *error = nil;
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actual;
|
||||
});
|
||||
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
NSString *name = sanitizedTestPath();
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
|
||||
actual = [actual view];
|
||||
}
|
||||
|
||||
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:name testCase:[self testCase] record:NO referenceDirectory:referenceImageDir error:&error];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (!actual) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into haveValidSnapshot." test:sanitizedTestPath() error:nil];
|
||||
}
|
||||
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected a matching snapshot in" test:sanitizedTestPath() error:error];
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to not have a matching snapshot in" test:sanitizedTestPath() error:error];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherImplementationBegin(recordSnapshot, (void)) {
|
||||
__block NSError *error = nil;
|
||||
|
||||
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actual && actualIsViewLayerOrViewController;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
|
||||
// For view controllers do the viewWill/viewDid dance, then pass view through
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
actual = [actual view];
|
||||
}
|
||||
|
||||
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:sanitizedTestPath() testCase:[self testCase] record:YES referenceDirectory:referenceImageDir error:&error];
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (!actual) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into recordSnapshot." test:sanitizedTestPath() error:nil];
|
||||
}
|
||||
|
||||
if (!actualIsViewLayerOrViewController) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:sanitizedTestPath() error:nil];
|
||||
}
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a snapshot in" test:sanitizedTestPath() error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", sanitizedTestPath()];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a snapshot in" test:sanitizedTestPath() error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", sanitizedTestPath()];
|
||||
}
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherImplementationBegin(haveValidSnapshotNamed, (NSString *snapshot)){
|
||||
BOOL snapshotIsNil = (snapshot == nil);
|
||||
__block NSError *error = nil;
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actual && !(snapshotIsNil);
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
|
||||
actual = [actual view];
|
||||
}
|
||||
return [EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:NO referenceDirectory:referenceImageDir error:&error];
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (!actual) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into haveValidSnapshotNamed." test:sanitizedTestPath() error:nil];
|
||||
}
|
||||
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected a matching snapshot named" test:snapshot error:error];
|
||||
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected not to have a matching snapshot named" test:snapshot error:error];
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
|
||||
EXPMatcherImplementationBegin(recordSnapshotNamed, (NSString *snapshot)) {
|
||||
BOOL snapshotExists = (snapshot != nil);
|
||||
BOOL actualIsViewLayerOrViewController = ([actual isKindOfClass:UIView.class] || [actual isKindOfClass:CALayer.class] || [actual isKindOfClass:UIViewController.class]);
|
||||
__block NSError *error = nil;
|
||||
id actualRef = actual;
|
||||
|
||||
prerequisite(^BOOL{
|
||||
return actualRef && snapshotExists && actualIsViewLayerOrViewController;
|
||||
});
|
||||
|
||||
match(^BOOL{
|
||||
NSString *referenceImageDir = [self _getDefaultReferenceDirectory];
|
||||
|
||||
// For view controllers do the viewWill/viewDid dance, then pass view through
|
||||
if ([actual isKindOfClass:UIViewController.class]) {
|
||||
[actual beginAppearanceTransition:YES animated:NO];
|
||||
[actual endAppearanceTransition];
|
||||
actual = [actual view];
|
||||
}
|
||||
|
||||
[EXPExpectFBSnapshotTest compareSnapshotOfViewOrLayer:actual snapshot:snapshot testCase:[self testCase] record:YES referenceDirectory:referenceImageDir error:&error];
|
||||
return NO;
|
||||
});
|
||||
|
||||
failureMessageForTo(^NSString *{
|
||||
if (!actual) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Nil was passed into recordSnapshotNamed." test:sanitizedTestPath() error:nil];
|
||||
}
|
||||
if (!actualIsViewLayerOrViewController) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:snapshot error:nil];
|
||||
}
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a matching snapshot named" test:snapshot error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", snapshot];
|
||||
}
|
||||
});
|
||||
|
||||
failureMessageForNotTo(^NSString *{
|
||||
if (!actualIsViewLayerOrViewController) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"Expected a View, Layer or View Controller." test:snapshot error:nil];
|
||||
}
|
||||
if (error) {
|
||||
return [EXPExpectFBSnapshotTest combinedError:@"expected to record a matching snapshot named" test:snapshot error:error];
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"snapshot %@ successfully recorded, replace recordSnapshot with a check", snapshot];
|
||||
}
|
||||
});
|
||||
}
|
||||
EXPMatcherImplementationEnd
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// ExpectaObject+FBSnapshotTest.h
|
||||
// Expecta+Snapshots
|
||||
//
|
||||
// Created by John Boiles on 8/3/15.
|
||||
// Copyright (c) 2015 Expecta+Snapshots All rights reserved.
|
||||
//
|
||||
|
||||
#import <Expecta/ExpectaObject.h>
|
||||
|
||||
@interface Expecta (FBSnapshotTest)
|
||||
|
||||
+ (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect;
|
||||
|
||||
+ (BOOL)usesDrawViewHierarchyInRect;
|
||||
|
||||
@end
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// ExpectaObject+FBSnapshotTest.m
|
||||
// Expecta+Snapshots
|
||||
//
|
||||
// Created by John Boiles on 8/3/15.
|
||||
// Copyright (c) 2015 Expecta+Snapshots All rights reserved.
|
||||
//
|
||||
|
||||
#import "ExpectaObject+FBSnapshotTest.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
static NSString const *kUsesDrawViewHierarchyInRectKey = @"ExpectaObject+FBSnapshotTest.usesDrawViewHierarchyInRect";
|
||||
|
||||
@implementation Expecta (FBSnapshotTest)
|
||||
|
||||
+ (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect {
|
||||
objc_setAssociatedObject(self, (__bridge const void *)(kUsesDrawViewHierarchyInRectKey), @(usesDrawViewHierarchyInRect), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
+ (BOOL)usesDrawViewHierarchyInRect {
|
||||
NSNumber *usesDrawViewHierarchyInRect = objc_getAssociatedObject(self, (__bridge const void *)(kUsesDrawViewHierarchyInRectKey));
|
||||
return usesDrawViewHierarchyInRect.boolValue;
|
||||
}
|
||||
|
||||
@end
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014 Daniel Doubrovkine, Artsy Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Generated
-87
@@ -1,87 +0,0 @@
|
||||
Expecta Matchers for FBSnapshotTestCase
|
||||
=======================================
|
||||
|
||||
[Expecta](https://github.com/specta/expecta) matchers for [ios-snapshot-test-case](https://github.com/facebook/ios-snapshot-test-case).
|
||||
|
||||
[](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta)
|
||||
|
||||
### Usage
|
||||
|
||||
Add `Expecta+Snapshots` to your Podfile, the latest `FBSnapshotTestCase` will come in as a dependency.
|
||||
|
||||
``` ruby
|
||||
pod 'Expecta+Snapshots'
|
||||
```
|
||||
|
||||
### App setup
|
||||
|
||||
Use `expect(view).to.recordSnapshotNamed(@"unique snapshot name")` to record a snapshot and `expect(view).to.haveValidSnapshotNamed(@"unique snapshot name")` to check it.
|
||||
|
||||
If you project was compiled with Specta included, you have two extra methods that use the spec hierarchy to generate the snapshot name for you: `recordSnapshot()` and `haveValidSnapshot()`. You should only call these once per `it()` block.
|
||||
|
||||
If you need the `usesDrawViewHierarchyInRect` property in order to correctly render UIVisualEffect, UIAppearance and Size Classes, call `[Expecta setUsesDrawViewHierarchyInRect:NO];` inside `beforeAll`.
|
||||
|
||||
``` Objective-C
|
||||
#define EXP_SHORTHAND
|
||||
#include <Specta/Specta.h>
|
||||
#include <Expecta/Expecta.h>
|
||||
#include <Expecta+Snapshots/EXPMatchers+FBSnapshotTest.h>
|
||||
#include "FBExampleView.h"
|
||||
|
||||
SpecBegin(FBExampleView)
|
||||
|
||||
describe(@"manual matching", ^{
|
||||
|
||||
it(@"matches view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).to.recordSnapshotNamed(@"FBExampleView");
|
||||
expect(view).to.haveValidSnapshotNamed(@"FBExampleView");
|
||||
});
|
||||
|
||||
it(@"doesn't match a view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).toNot.haveValidSnapshotNamed(@"FBExampleViewDoesNotExist");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe(@"test name derived matching", ^{
|
||||
|
||||
it(@"matches view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).to.recordSnapshot();
|
||||
expect(view).to.haveValidSnapshot();
|
||||
});
|
||||
|
||||
it(@"doesn't match a view", ^{
|
||||
FBExampleView *view = [[FBExampleView alloc] initWithFrame:CGRectMake(0, 0, 64, 64)];
|
||||
expect(view).toNot.haveValidSnapshot();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
SpecEnd
|
||||
```
|
||||
|
||||
### Sane defaults
|
||||
|
||||
`EXPMatchers+FBSnapshotTest` will automatically figure out the tests folder, and [add a reference image](https://github.com/dblock/ios-snapshot-test-case-expecta/blob/master/EXPMatchers%2BFBSnapshotTest.m#L84-L85) directory, if you'd like to override this, you should include a `beforeAll` block setting the `setGlobalReferenceImageDir` in each file containing tests.
|
||||
|
||||
```
|
||||
beforeAll(^{
|
||||
setGlobalReferenceImageDir(FB_REFERENCE_IMAGE_DIR);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
A complete project can be found in [FBSnapshotTestCaseDemo](FBSnapshotTestCaseDemo).
|
||||
|
||||
Notably, take a look at [FBSnapshotTestCaseDemoSpecs.m](FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemoTests/FBSnapshotTestCaseDemoSpecs.m) for a complete example, which is an expanded Specta version version of [FBSnapshotTestCaseDemoTests.m](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCaseDemo/FBSnapshotTestCaseDemoTests/FBSnapshotTestCaseDemoTests.m).
|
||||
|
||||
Finally you can consult the tests for [ARTiledImageView](https://github.com/dblock/ARTiledImageView/tree/master/IntegrationTests) or [NAMapKit](https://github.com/neilang/NAMapKit/tree/master/Demo/DemoTests).
|
||||
|
||||
### License
|
||||
|
||||
MIT, see [LICENSE](LICENSE.md)
|
||||
Generated
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIApplication (StrictKeyWindow)
|
||||
|
||||
/**
|
||||
@return The receiver's @c keyWindow. Raises an assertion if @c nil.
|
||||
*/
|
||||
- (UIWindow *)fb_strictKeyWindow;
|
||||
|
||||
@end
|
||||
Generated
-27
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
|
||||
|
||||
@implementation UIApplication (StrictKeyWindow)
|
||||
|
||||
- (UIWindow *)fb_strictKeyWindow
|
||||
{
|
||||
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
|
||||
if (!keyWindow) {
|
||||
[NSException raise:@"FBSnapshotTestCaseNilKeyWindowException"
|
||||
format:@"Snapshot tests must be hosted by an application with a key window. Please ensure your test"
|
||||
" host sets up a key window at launch (either via storyboards or programmatically) and doesn't"
|
||||
" do anything to remove it while snapshot tests are running."];
|
||||
}
|
||||
return keyWindow;
|
||||
}
|
||||
|
||||
@end
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (Compare)
|
||||
|
||||
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
|
||||
|
||||
@end
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||
|
||||
// This makes debugging much more fun
|
||||
typedef union {
|
||||
uint32_t raw;
|
||||
unsigned char bytes[4];
|
||||
struct {
|
||||
char red;
|
||||
char green;
|
||||
char blue;
|
||||
char alpha;
|
||||
} __attribute__ ((packed)) pixels;
|
||||
} FBComparePixel;
|
||||
|
||||
@implementation UIImage (Compare)
|
||||
|
||||
- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
|
||||
{
|
||||
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");
|
||||
|
||||
CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage));
|
||||
CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
|
||||
|
||||
// The images have the equal size, so we could use the smallest amount of bytes because of byte padding
|
||||
size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage));
|
||||
size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow;
|
||||
void *referenceImagePixels = calloc(1, referenceImageSizeBytes);
|
||||
void *imagePixels = calloc(1, referenceImageSizeBytes);
|
||||
|
||||
if (!referenceImagePixels || !imagePixels) {
|
||||
free(referenceImagePixels);
|
||||
free(imagePixels);
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels,
|
||||
referenceImageSize.width,
|
||||
referenceImageSize.height,
|
||||
CGImageGetBitsPerComponent(self.CGImage),
|
||||
minBytesPerRow,
|
||||
CGImageGetColorSpace(self.CGImage),
|
||||
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
|
||||
);
|
||||
CGContextRef imageContext = CGBitmapContextCreate(imagePixels,
|
||||
imageSize.width,
|
||||
imageSize.height,
|
||||
CGImageGetBitsPerComponent(image.CGImage),
|
||||
minBytesPerRow,
|
||||
CGImageGetColorSpace(image.CGImage),
|
||||
(CGBitmapInfo)kCGImageAlphaPremultipliedLast
|
||||
);
|
||||
|
||||
if (!referenceImageContext || !imageContext) {
|
||||
CGContextRelease(referenceImageContext);
|
||||
CGContextRelease(imageContext);
|
||||
free(referenceImagePixels);
|
||||
free(imagePixels);
|
||||
return NO;
|
||||
}
|
||||
|
||||
CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage);
|
||||
CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage);
|
||||
|
||||
CGContextRelease(referenceImageContext);
|
||||
CGContextRelease(imageContext);
|
||||
|
||||
BOOL imageEqual = YES;
|
||||
|
||||
// Do a fast compare if we can
|
||||
if (tolerance == 0) {
|
||||
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
|
||||
} else {
|
||||
// Go through each pixel in turn and see if it is different
|
||||
const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
|
||||
|
||||
FBComparePixel *p1 = referenceImagePixels;
|
||||
FBComparePixel *p2 = imagePixels;
|
||||
|
||||
NSInteger numDiffPixels = 0;
|
||||
for (int n = 0; n < pixelCount; ++n) {
|
||||
// If this pixel is different, increment the pixel diff count and see
|
||||
// if we have hit our limit.
|
||||
if (p1->raw != p2->raw) {
|
||||
numDiffPixels ++;
|
||||
|
||||
CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
|
||||
if (percent > tolerance) {
|
||||
imageEqual = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p1++;
|
||||
p2++;
|
||||
}
|
||||
}
|
||||
|
||||
free(referenceImagePixels);
|
||||
free(imagePixels);
|
||||
|
||||
return imageEqual;
|
||||
}
|
||||
|
||||
@end
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (Diff)
|
||||
|
||||
- (UIImage *)fb_diffWithImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
//
|
||||
// Created by Gabriel Handford on 3/1/09.
|
||||
// Copyright 2009-2013. All rights reserved.
|
||||
// Created by John Boiles on 10/20/11.
|
||||
// Copyright (c) 2011. All rights reserved
|
||||
// Modified by Felix Schulze on 2/11/13.
|
||||
// Copyright 2013. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||
|
||||
@implementation UIImage (Diff)
|
||||
|
||||
- (UIImage *)fb_diffWithImage:(UIImage *)image
|
||||
{
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height));
|
||||
UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
|
||||
CGContextSetAlpha(context, 0.5);
|
||||
CGContextBeginTransparencyLayer(context, NULL);
|
||||
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
|
||||
CGContextSetBlendMode(context, kCGBlendModeDifference);
|
||||
CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor);
|
||||
CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height));
|
||||
CGContextEndTransparencyLayer(context);
|
||||
UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return returnImage;
|
||||
}
|
||||
|
||||
@end
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (Snapshot)
|
||||
|
||||
/// Uses renderInContext: to get a snapshot of the layer.
|
||||
+ (UIImage *)fb_imageForLayer:(CALayer *)layer;
|
||||
|
||||
/// Uses renderInContext: to get a snapshot of the view layer.
|
||||
+ (UIImage *)fb_imageForViewLayer:(UIView *)view;
|
||||
|
||||
/// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed.
|
||||
+ (UIImage *)fb_imageForView:(UIView *)view;
|
||||
|
||||
@end
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
|
||||
|
||||
@implementation UIImage (Snapshot)
|
||||
|
||||
+ (UIImage *)fb_imageForLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect bounds = layer.bounds;
|
||||
NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer);
|
||||
NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
NSAssert1(context, @"Could not generate context for layer %@", layer);
|
||||
CGContextSaveGState(context);
|
||||
[layer layoutIfNeeded];
|
||||
[layer renderInContext:context];
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
+ (UIImage *)fb_imageForViewLayer:(UIView *)view
|
||||
{
|
||||
[view layoutIfNeeded];
|
||||
return [self fb_imageForLayer:view.layer];
|
||||
}
|
||||
|
||||
+ (UIImage *)fb_imageForView:(UIView *)view
|
||||
{
|
||||
CGRect bounds = view.bounds;
|
||||
NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view);
|
||||
NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view);
|
||||
|
||||
// If the input view is already a UIWindow, then just use that. Otherwise wrap in a window.
|
||||
UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *)view : view.window;
|
||||
BOOL removeFromSuperview = NO;
|
||||
if (!window) {
|
||||
window = [[UIApplication sharedApplication] fb_strictKeyWindow];
|
||||
}
|
||||
|
||||
if (!view.window && view != window) {
|
||||
[window addSubview:view];
|
||||
removeFromSuperview = YES;
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
||||
[view layoutIfNeeded];
|
||||
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
|
||||
|
||||
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
if (removeFromSuperview) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
/*
|
||||
There are three ways of setting reference image directories.
|
||||
|
||||
1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted
|
||||
c-string with the path.
|
||||
2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This
|
||||
takes precedence over the preprocessor macro to allow for run-time override.
|
||||
3. Keep everything unset, which will cause the reference images to be looked up
|
||||
inside the bundle holding the current test, in the
|
||||
Resources/ReferenceImages_* directories.
|
||||
*/
|
||||
#ifndef FB_REFERENCE_IMAGE_DIR
|
||||
#define FB_REFERENCE_IMAGE_DIR ""
|
||||
#endif
|
||||
|
||||
/**
|
||||
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||
@param view The view to snapshot
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||
@param tolerance The percentage of pixels that can differ and still count as an 'identical' view
|
||||
*/
|
||||
#define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \
|
||||
FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__)
|
||||
|
||||
#define FBSnapshotVerifyView(view__, identifier__) \
|
||||
FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
|
||||
|
||||
|
||||
/**
|
||||
Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though.
|
||||
@param layer The layer to snapshot
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||
@param tolerance The percentage of pixels that can differ and still count as an 'identical' layer
|
||||
*/
|
||||
#define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \
|
||||
FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__)
|
||||
|
||||
#define FBSnapshotVerifyLayer(layer__, identifier__) \
|
||||
FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0)
|
||||
|
||||
|
||||
#define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \
|
||||
{ \
|
||||
NSString *errorDescription = [self snapshotVerifyViewOrLayer:viewOrLayer__ identifier:identifier__ suffixes:suffixes__ tolerance:tolerance__]; \
|
||||
BOOL noErrors = (errorDescription == nil); \
|
||||
XCTAssertTrue(noErrors, @"%@", errorDescription); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test
|
||||
and compare an image of the view to a reference image that write lots of complex layout-code tests.
|
||||
|
||||
In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES.
|
||||
|
||||
@attention When recording, the reference image directory should be explicitly
|
||||
set, otherwise the images may be written to somewhere inside the
|
||||
simulator directory.
|
||||
|
||||
For example:
|
||||
@code
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
self.recordMode = YES;
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
@interface FBSnapshotTestCase : XCTestCase
|
||||
|
||||
/**
|
||||
When YES, the test macros will save reference images, rather than performing an actual test.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||
|
||||
/**
|
||||
When @c YES appends the name of the device model and OS to the snapshot file name.
|
||||
The default value is @c NO.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
|
||||
|
||||
/**
|
||||
When YES, renders a snapshot of the complete view hierarchy as visible onscreen.
|
||||
There are several things that do not work if renderInContext: is used.
|
||||
- UIVisualEffect #70
|
||||
- UIAppearance #91
|
||||
- Size Classes #92
|
||||
|
||||
@attention If the view does't belong to a UIWindow, it will create one and add the view as a subview.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||
|
||||
- (void)setUp NS_REQUIRES_SUPER;
|
||||
- (void)tearDown NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
Performs the comparison or records a snapshot of the layer if recordMode is YES.
|
||||
@param viewOrLayer The UIView or CALayer to snapshot
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param suffixes An NSOrderedSet of strings for the different suffixes
|
||||
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||
@returns nil if the comparison (or saving of the reference image) succeeded. Otherwise it contains an error description.
|
||||
*/
|
||||
- (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
|
||||
identifier:(NSString *)identifier
|
||||
suffixes:(NSOrderedSet *)suffixes
|
||||
tolerance:(CGFloat)tolerance;
|
||||
|
||||
/**
|
||||
Performs the comparison or records a snapshot of the layer if recordMode is YES.
|
||||
@param layer The Layer to snapshot
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs the comparison or records a snapshot of the view if recordMode is YES.
|
||||
@param view The view to snapshot
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Checks if reference image with identifier based name exists in the reference images directory.
|
||||
@param referenceImagesDirectory The directory in which reference images are stored.
|
||||
@param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method.
|
||||
@param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if reference image exists.
|
||||
*/
|
||||
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Returns the reference image directory.
|
||||
|
||||
Helper function used to implement the assert macros.
|
||||
|
||||
@param dir directory to use if environment variable not specified. Ignored if null or empty.
|
||||
*/
|
||||
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir;
|
||||
|
||||
@end
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCase.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
|
||||
@implementation FBSnapshotTestCase
|
||||
{
|
||||
FBSnapshotTestController *_snapshotController;
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
_snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
_snapshotController = nil;
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (BOOL)recordMode
|
||||
{
|
||||
return _snapshotController.recordMode;
|
||||
}
|
||||
|
||||
- (void)setRecordMode:(BOOL)recordMode
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.recordMode = recordMode;
|
||||
}
|
||||
|
||||
- (BOOL)isDeviceAgnostic
|
||||
{
|
||||
return _snapshotController.deviceAgnostic;
|
||||
}
|
||||
|
||||
- (void)setDeviceAgnostic:(BOOL)deviceAgnostic
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.deviceAgnostic = deviceAgnostic;
|
||||
}
|
||||
|
||||
- (BOOL)usesDrawViewHierarchyInRect
|
||||
{
|
||||
return _snapshotController.usesDrawViewHierarchyInRect;
|
||||
}
|
||||
|
||||
- (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect;
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer
|
||||
identifier:(NSString *)identifier
|
||||
suffixes:(NSOrderedSet *)suffixes
|
||||
tolerance:(CGFloat)tolerance
|
||||
{
|
||||
if (nil == viewOrLayer) {
|
||||
return @"Object to be snapshotted must not be nil";
|
||||
}
|
||||
NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)];
|
||||
if (referenceImageDirectory == nil) {
|
||||
return @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.";
|
||||
}
|
||||
if (suffixes.count == 0) {
|
||||
return [NSString stringWithFormat:@"Suffixes set cannot be empty %@", suffixes];
|
||||
}
|
||||
|
||||
BOOL testSuccess = NO;
|
||||
NSError *error = nil;
|
||||
NSMutableArray *errors = [NSMutableArray array];
|
||||
|
||||
if (self.recordMode) {
|
||||
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffixes.firstObject];
|
||||
BOOL referenceImageSaved = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:(identifier) tolerance:tolerance error:&error];
|
||||
if (!referenceImageSaved) {
|
||||
[errors addObject:error];
|
||||
}
|
||||
} else {
|
||||
for (NSString *suffix in suffixes) {
|
||||
NSString *referenceImagesDirectory = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix];
|
||||
BOOL referenceImageAvailable = [self referenceImageRecordedInDirectory:referenceImagesDirectory identifier:(identifier) error:&error];
|
||||
|
||||
if (referenceImageAvailable) {
|
||||
BOOL comparisonSuccess = [self _compareSnapshotOfViewOrLayer:viewOrLayer referenceImagesDirectory:referenceImagesDirectory identifier:identifier tolerance:tolerance error:&error];
|
||||
[errors removeAllObjects];
|
||||
if (comparisonSuccess) {
|
||||
testSuccess = YES;
|
||||
break;
|
||||
} else {
|
||||
[errors addObject:error];
|
||||
}
|
||||
} else {
|
||||
[errors addObject:error];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!testSuccess) {
|
||||
return [NSString stringWithFormat:@"Snapshot comparison failed: %@", errors.firstObject];
|
||||
}
|
||||
if (self.recordMode) {
|
||||
return @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!";
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self _compareSnapshotOfViewOrLayer:layer
|
||||
referenceImagesDirectory:referenceImagesDirectory
|
||||
identifier:identifier
|
||||
tolerance:tolerance
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self _compareSnapshotOfViewOrLayer:view
|
||||
referenceImagesDirectory:referenceImagesDirectory
|
||||
identifier:identifier
|
||||
tolerance:tolerance
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)referenceImageRecordedInDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__);
|
||||
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
|
||||
UIImage *referenceImage = [_snapshotController referenceImageForSelector:self.invocation.selector
|
||||
identifier:identifier
|
||||
error:errorPtr];
|
||||
|
||||
return (referenceImage != nil);
|
||||
}
|
||||
|
||||
- (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir
|
||||
{
|
||||
NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"];
|
||||
if (envReferenceImageDirectory) {
|
||||
return envReferenceImageDirectory;
|
||||
}
|
||||
if (dir && dir.length > 0) {
|
||||
return dir;
|
||||
}
|
||||
return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
referenceImagesDirectory:(NSString *)referenceImagesDirectory
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
_snapshotController.referenceImagesDirectory = referenceImagesDirectory;
|
||||
return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer
|
||||
selector:self.invocation.selector
|
||||
identifier:identifier
|
||||
tolerance:tolerance
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
@end
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Returns a Boolean value that indicates whether the snapshot test is running in 64Bit.
|
||||
This method is a convenience for creating the suffixes set based on the architecture
|
||||
that the test is running.
|
||||
|
||||
@returns @c YES if the test is running in 64bit, otherwise @c NO.
|
||||
*/
|
||||
BOOL FBSnapshotTestCaseIs64Bit(void);
|
||||
|
||||
/**
|
||||
Returns a default set of strings that is used to append a suffix based on the architectures.
|
||||
@warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions()
|
||||
|
||||
@returns An @c NSOrderedSet object containing strings that are appended to the reference images directory.
|
||||
*/
|
||||
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void);
|
||||
|
||||
/**
|
||||
Returns a fully «normalized» file name.
|
||||
Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name.
|
||||
|
||||
@returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end.
|
||||
*/
|
||||
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <FBSnapshotTestCase/UIApplication+StrictKeyWindow.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
BOOL FBSnapshotTestCaseIs64Bit(void)
|
||||
{
|
||||
#if __LP64__
|
||||
return YES;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void)
|
||||
{
|
||||
NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init];
|
||||
[suffixesSet addObject:@"_32"];
|
||||
[suffixesSet addObject:@"_64"];
|
||||
if (FBSnapshotTestCaseIs64Bit()) {
|
||||
return [suffixesSet reversedOrderedSet];
|
||||
}
|
||||
return [suffixesSet copy];
|
||||
}
|
||||
|
||||
NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName)
|
||||
{
|
||||
UIDevice *device = [UIDevice currentDevice];
|
||||
UIWindow *keyWindow = [[UIApplication sharedApplication] fb_strictKeyWindow];
|
||||
CGSize screenSize = keyWindow.bounds.size;
|
||||
NSString *os = device.systemVersion;
|
||||
|
||||
fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height];
|
||||
|
||||
NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new];
|
||||
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
|
||||
NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters];
|
||||
fileName = [validComponents componentsJoinedByString:@"_"];
|
||||
|
||||
return fileName;
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) {
|
||||
FBSnapshotTestControllerErrorCodeUnknown,
|
||||
FBSnapshotTestControllerErrorCodeNeedsRecord,
|
||||
FBSnapshotTestControllerErrorCodePNGCreationFailed,
|
||||
FBSnapshotTestControllerErrorCodeImagesDifferentSizes,
|
||||
FBSnapshotTestControllerErrorCodeImagesDifferent,
|
||||
};
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController use this domain.
|
||||
*/
|
||||
extern NSString *const FBSnapshotTestControllerErrorDomain;
|
||||
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
||||
*/
|
||||
extern NSString *const FBReferenceImageFilePathKey;
|
||||
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
||||
*/
|
||||
extern NSString *const FBReferenceImageKey;
|
||||
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
||||
*/
|
||||
extern NSString *const FBCapturedImageKey;
|
||||
|
||||
/**
|
||||
Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary.
|
||||
*/
|
||||
extern NSString *const FBDiffedImageKey;
|
||||
|
||||
/**
|
||||
Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel-
|
||||
by-pixel comparison of images.
|
||||
Instances are initialized with the test class, and directories to read and write to.
|
||||
*/
|
||||
@interface FBSnapshotTestController : NSObject
|
||||
|
||||
/**
|
||||
Record snapshots.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL recordMode;
|
||||
|
||||
/**
|
||||
When @c YES appends the name of the device model and OS to the snapshot file name.
|
||||
The default value is @c NO.
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic;
|
||||
|
||||
/**
|
||||
Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext:
|
||||
*/
|
||||
@property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect;
|
||||
|
||||
/**
|
||||
The directory in which referfence images are stored.
|
||||
*/
|
||||
@property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory;
|
||||
|
||||
/**
|
||||
@param testClass The subclass of FBSnapshotTestCase that is using this controller.
|
||||
@returns An instance of FBSnapshotTestController.
|
||||
*/
|
||||
- (instancetype)initWithTestClass:(Class)testClass;
|
||||
|
||||
/**
|
||||
Designated initializer.
|
||||
@param testName The name of the tests.
|
||||
@returns An instance of FBSnapshotTestController.
|
||||
*/
|
||||
- (instancetype)initWithTestName:(NSString *)testName;
|
||||
|
||||
/**
|
||||
Performs the comparison of the layer.
|
||||
@param layer The Layer to snapshot.
|
||||
@param selector The test method being run.
|
||||
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs the comparison of the view.
|
||||
@param view The view to snapshot.
|
||||
@param selector The test method being run.
|
||||
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs the comparison of a view or layer.
|
||||
@param view The view or layer to snapshot.
|
||||
@param selector The test method being run.
|
||||
@param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method.
|
||||
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
|
||||
@param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc).
|
||||
@returns YES if the comparison (or saving of the reference image) succeeded.
|
||||
*/
|
||||
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Loads a reference image.
|
||||
@param selector The test method being run.
|
||||
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||
@param errorPtr An error, if this methods returns nil, the error will be something useful.
|
||||
@returns An image.
|
||||
*/
|
||||
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Performs a pixel-by-pixel comparison of the two images with an allowable margin of error.
|
||||
@param referenceImage The reference (correct) image.
|
||||
@param image The image to test against the reference.
|
||||
@param tolerance The percentage of pixels that can differ and still be considered 'identical'
|
||||
@param errorPtr An error that indicates why the comparison failed if it does.
|
||||
@returns YES if the comparison succeeded and the images are the same(ish).
|
||||
*/
|
||||
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||
toImage:(UIImage *)image
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr;
|
||||
|
||||
/**
|
||||
Saves the reference image and the test image to `failedOutputDirectory`.
|
||||
@param referenceImage The reference (correct) image.
|
||||
@param testImage The image to test against the reference.
|
||||
@param selector The test method being run.
|
||||
@param identifier The optional identifier, used when multiple images are tested in a single -test method.
|
||||
@param errorPtr An error that indicates why the comparison failed if it does.
|
||||
@returns YES if the save succeeded.
|
||||
*/
|
||||
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||
testImage:(UIImage *)testImage
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr;
|
||||
@end
|
||||
-358
@@ -1,358 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestController.h>
|
||||
#import <FBSnapshotTestCase/FBSnapshotTestCasePlatform.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Compare.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Diff.h>
|
||||
#import <FBSnapshotTestCase/UIImage+Snapshot.h>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain";
|
||||
NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey";
|
||||
NSString *const FBReferenceImageKey = @"FBReferenceImageKey";
|
||||
NSString *const FBCapturedImageKey = @"FBCapturedImageKey";
|
||||
NSString *const FBDiffedImageKey = @"FBDiffedImageKey";
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) {
|
||||
FBTestSnapshotFileNameTypeReference,
|
||||
FBTestSnapshotFileNameTypeFailedReference,
|
||||
FBTestSnapshotFileNameTypeFailedTest,
|
||||
FBTestSnapshotFileNameTypeFailedTestDiff,
|
||||
};
|
||||
|
||||
@implementation FBSnapshotTestController
|
||||
{
|
||||
NSString *_testName;
|
||||
NSFileManager *_fileManager;
|
||||
}
|
||||
|
||||
#pragma mark - Initializers
|
||||
|
||||
- (instancetype)initWithTestClass:(Class)testClass;
|
||||
{
|
||||
return [self initWithTestName:NSStringFromClass(testClass)];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTestName:(NSString *)testName
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_testName = [testName copy];
|
||||
_deviceAgnostic = NO;
|
||||
|
||||
_fileManager = [[NSFileManager alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Overrides
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory];
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (BOOL)compareSnapshotOfLayer:(CALayer *)layer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:layer
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfView:(UIView *)view
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
return [self compareSnapshotOfViewOrLayer:view
|
||||
selector:selector
|
||||
identifier:identifier
|
||||
tolerance:0
|
||||
error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
if (self.recordMode) {
|
||||
return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr];
|
||||
} else {
|
||||
return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)referenceImageForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
|
||||
if (nil == image && NULL != errorPtr) {
|
||||
BOOL exists = [_fileManager fileExistsAtPath:filePath];
|
||||
if (!exists) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeNeedsRecord
|
||||
userInfo:@{
|
||||
FBReferenceImageFilePathKey: filePath,
|
||||
NSLocalizedDescriptionKey: @"Unable to load reference image.",
|
||||
NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode",
|
||||
}];
|
||||
} else {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodeUnknown
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
- (BOOL)compareReferenceImage:(UIImage *)referenceImage
|
||||
toImage:(UIImage *)image
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
BOOL sameImageDimensions = CGSizeEqualToSize(referenceImage.size, image.size);
|
||||
if (sameImageDimensions && [referenceImage fb_compareWithImage:image tolerance:tolerance]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (NULL != errorPtr) {
|
||||
NSString *errorDescription = sameImageDimensions ? @"Images different" : @"Images different sizes";
|
||||
NSString *errorReason = sameImageDimensions ? [NSString stringWithFormat:@"image pixels differed by more than %.2f%% from the reference image", tolerance * 100]
|
||||
: [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImage.size), NSStringFromCGSize(image.size)];
|
||||
FBSnapshotTestControllerErrorCode errorCode = sameImageDimensions ? FBSnapshotTestControllerErrorCodeImagesDifferent : FBSnapshotTestControllerErrorCodeImagesDifferentSizes;
|
||||
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:errorCode
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: errorDescription,
|
||||
NSLocalizedFailureReasonErrorKey: errorReason,
|
||||
FBReferenceImageKey: referenceImage,
|
||||
FBCapturedImageKey: image,
|
||||
FBDiffedImageKey: [referenceImage fb_diffWithImage:image],
|
||||
}];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage
|
||||
testImage:(UIImage *)testImage
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
NSData *referencePNGData = UIImagePNGRepresentation(referenceImage);
|
||||
NSData *testPNGData = UIImagePNGRepresentation(testImage);
|
||||
|
||||
NSString *referencePath = [self _failedFilePathForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedReference];
|
||||
|
||||
NSError *creationError = nil;
|
||||
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&creationError];
|
||||
if (!didCreateDir) {
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = creationError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *testPath = [self _failedFilePathForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedTest];
|
||||
|
||||
if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *diffPath = [self _failedFilePathForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff];
|
||||
|
||||
UIImage *diffImage = [referenceImage fb_diffWithImage:testImage];
|
||||
NSData *diffImageData = UIImagePNGRepresentation(diffImage);
|
||||
|
||||
if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n"
|
||||
@"ksdiff \"%@\" \"%@\"", referencePath, testPath);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (NSString *)_fileNameForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
||||
{
|
||||
NSString *fileName = nil;
|
||||
switch (fileNameType) {
|
||||
case FBTestSnapshotFileNameTypeFailedReference:
|
||||
fileName = @"reference_";
|
||||
break;
|
||||
case FBTestSnapshotFileNameTypeFailedTest:
|
||||
fileName = @"failed_";
|
||||
break;
|
||||
case FBTestSnapshotFileNameTypeFailedTestDiff:
|
||||
fileName = @"diff_";
|
||||
break;
|
||||
default:
|
||||
fileName = @"";
|
||||
break;
|
||||
}
|
||||
fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)];
|
||||
if (0 < identifier.length) {
|
||||
fileName = [fileName stringByAppendingFormat:@"_%@", identifier];
|
||||
}
|
||||
|
||||
if (self.isDeviceAgnostic) {
|
||||
fileName = FBDeviceAgnosticNormalizedFileName(fileName);
|
||||
}
|
||||
|
||||
if ([[UIScreen mainScreen] scale] > 1) {
|
||||
fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]];
|
||||
}
|
||||
fileName = [fileName stringByAppendingPathExtension:@"png"];
|
||||
return fileName;
|
||||
}
|
||||
|
||||
- (NSString *)_referenceFilePathForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
NSString *fileName = [self _fileNameForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:FBTestSnapshotFileNameTypeReference];
|
||||
NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName];
|
||||
filePath = [filePath stringByAppendingPathComponent:fileName];
|
||||
return filePath;
|
||||
}
|
||||
|
||||
- (NSString *)_failedFilePathForSelector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
fileNameType:(FBTestSnapshotFileNameType)fileNameType
|
||||
{
|
||||
NSString *fileName = [self _fileNameForSelector:selector
|
||||
identifier:identifier
|
||||
fileNameType:fileNameType];
|
||||
NSString *folderPath = NSTemporaryDirectory();
|
||||
if (getenv("IMAGE_DIFF_DIR")) {
|
||||
folderPath = @(getenv("IMAGE_DIFF_DIR"));
|
||||
}
|
||||
NSString *filePath = [folderPath stringByAppendingPathComponent:_testName];
|
||||
filePath = [filePath stringByAppendingPathComponent:fileName];
|
||||
return filePath;
|
||||
}
|
||||
|
||||
- (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
tolerance:(CGFloat)tolerance
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr];
|
||||
if (nil != referenceImage) {
|
||||
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||
BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr];
|
||||
if (!imagesSame) {
|
||||
NSError *saveError = nil;
|
||||
if ([self saveFailedReferenceImage:referenceImage testImage:snapshot selector:selector identifier:identifier error:&saveError] == NO) {
|
||||
NSLog(@"Error saving test images: %@", saveError);
|
||||
}
|
||||
}
|
||||
return imagesSame;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer];
|
||||
return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr];
|
||||
}
|
||||
|
||||
- (BOOL)_saveReferenceImage:(UIImage *)image
|
||||
selector:(SEL)selector
|
||||
identifier:(NSString *)identifier
|
||||
error:(NSError **)errorPtr
|
||||
{
|
||||
BOOL didWrite = NO;
|
||||
if (nil != image) {
|
||||
NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier];
|
||||
NSData *pngData = UIImagePNGRepresentation(image);
|
||||
if (nil != pngData) {
|
||||
NSError *creationError = nil;
|
||||
BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&creationError];
|
||||
if (!didCreateDir) {
|
||||
if (NULL != errorPtr) {
|
||||
*errorPtr = creationError;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr];
|
||||
if (didWrite) {
|
||||
NSLog(@"Reference image save at: %@", filePath);
|
||||
}
|
||||
} else {
|
||||
if (nil != errorPtr) {
|
||||
*errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain
|
||||
code:FBSnapshotTestControllerErrorCodePNGCreationFailed
|
||||
userInfo:@{
|
||||
FBReferenceImageFilePathKey: filePath,
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return didWrite;
|
||||
}
|
||||
|
||||
- (UIImage *)_imageForViewOrLayer:(id)viewOrLayer
|
||||
{
|
||||
if ([viewOrLayer isKindOfClass:[UIView class]]) {
|
||||
if (_usesDrawViewHierarchyInRect) {
|
||||
return [UIImage fb_imageForView:viewOrLayer];
|
||||
} else {
|
||||
return [UIImage fb_imageForViewLayer:viewOrLayer];
|
||||
}
|
||||
} else if ([viewOrLayer isKindOfClass:[CALayer class]]) {
|
||||
return [UIImage fb_imageForLayer:viewOrLayer];
|
||||
} else {
|
||||
[NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#if swift(>=3)
|
||||
public extension FBSnapshotTestCase {
|
||||
public func FBSnapshotVerifyView(_ view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
|
||||
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
|
||||
}
|
||||
|
||||
public func FBSnapshotVerifyLayer(_ layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
|
||||
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
|
||||
}
|
||||
|
||||
private func FBSnapshotVerifyViewOrLayer(_ viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
|
||||
let envReferenceImageDirectory = self.getReferenceImageDirectory(withDefault: FB_REFERENCE_IMAGE_DIR)
|
||||
var error: NSError?
|
||||
var comparisonSuccess = false
|
||||
|
||||
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||
for suffix in suffixes {
|
||||
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||
if viewOrLayer.isKind(of: UIView.self) {
|
||||
do {
|
||||
try compareSnapshot(of: viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else if viewOrLayer.isKind(of: CALayer.self) {
|
||||
do {
|
||||
try compareSnapshot(of: viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else {
|
||||
assertionFailure("Only UIView and CALayer classes can be snapshotted")
|
||||
}
|
||||
|
||||
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||
|
||||
if comparisonSuccess || recordMode {
|
||||
break
|
||||
}
|
||||
|
||||
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||
}
|
||||
} else {
|
||||
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
|
||||
}
|
||||
}
|
||||
|
||||
func assert(_ assertion: Bool, message: String, file: StaticString, line: UInt) {
|
||||
if !assertion {
|
||||
XCTFail(message, file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public extension FBSnapshotTestCase {
|
||||
public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
|
||||
FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
|
||||
}
|
||||
|
||||
public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
|
||||
FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes, tolerance: tolerance, file: file, line: line)
|
||||
}
|
||||
|
||||
private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), tolerance: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
|
||||
let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR)
|
||||
var error: NSError?
|
||||
var comparisonSuccess = false
|
||||
|
||||
if let envReferenceImageDirectory = envReferenceImageDirectory {
|
||||
for suffix in suffixes {
|
||||
let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)"
|
||||
if viewOrLayer.isKindOfClass(UIView) {
|
||||
do {
|
||||
try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else if viewOrLayer.isKindOfClass(CALayer) {
|
||||
do {
|
||||
try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: tolerance)
|
||||
comparisonSuccess = true
|
||||
} catch let error1 as NSError {
|
||||
error = error1
|
||||
comparisonSuccess = false
|
||||
}
|
||||
} else {
|
||||
assertionFailure("Only UIView and CALayer classes can be snapshotted")
|
||||
}
|
||||
|
||||
assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line)
|
||||
|
||||
if comparisonSuccess || recordMode {
|
||||
break
|
||||
}
|
||||
|
||||
assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line)
|
||||
}
|
||||
} else {
|
||||
XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.")
|
||||
}
|
||||
}
|
||||
|
||||
func assert(assertion: Bool, message: String, file: StaticString, line: UInt) {
|
||||
if !assertion {
|
||||
XCTFail(message, file: file, line: line)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Generated
-29
@@ -1,29 +0,0 @@
|
||||
BSD License
|
||||
|
||||
For the FBSnapshotTestCase software
|
||||
|
||||
Copyright (c) 2013, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
FBSnapshotTestCase
|
||||
======================
|
||||
|
||||
[](https://travis-ci.org/facebook/ios-snapshot-test-case) [](http://cocoadocs.org/docsets/FBSnapshotTestCase/)
|
||||
|
||||
What it does
|
||||
------------
|
||||
|
||||
A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the
|
||||
`renderInContext:` method to get an image snapshot of its contents. It
|
||||
compares this snapshot to a "reference image" stored in your source code
|
||||
repository and fails the test if the two images don't match.
|
||||
|
||||
Why?
|
||||
----
|
||||
|
||||
At Facebook we write a lot of UI code. As you might imagine, each type of
|
||||
feed story is rendered using a subclass of `UIView`. There are a lot of edge
|
||||
cases that we want to handle correctly:
|
||||
|
||||
- What if there is more text than can fit in the space available?
|
||||
- What if an image doesn't match the size of an image view?
|
||||
- What should the highlighted state look like?
|
||||
|
||||
It's straightforward to test logic code, but less obvious how you should test
|
||||
views. You can do a lot of rectangle asserts, but these are hard to understand
|
||||
or visualize. Looking at an image diff shows you exactly what changed and how
|
||||
it will look to users.
|
||||
|
||||
We developed `FBSnapshotTestCase` to make snapshot tests easy.
|
||||
|
||||
Installation with CocoaPods
|
||||
---------------------------
|
||||
|
||||
1. Add the following lines to your Podfile:
|
||||
|
||||
```
|
||||
target "Tests" do
|
||||
pod 'FBSnapshotTestCase'
|
||||
end
|
||||
```
|
||||
|
||||
If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support.
|
||||
|
||||
Replace "Tests" with the name of your test project.
|
||||
|
||||
2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this:
|
||||
|
||||
|Name|Value|
|
||||
|:---|:----|
|
||||
|`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`|
|
||||
|
||||
|
||||

|
||||
|
||||
Creating a snapshot test
|
||||
------------------------
|
||||
|
||||
1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`.
|
||||
2. From within your test, use `FBSnapshotVerifyView`.
|
||||
3. Run the test once with `self.recordMode = YES;` in the test's `-setUp`
|
||||
method. (This creates the reference images on disk.)
|
||||
4. Remove the line enabling record mode and run the test.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Automatically names reference images on disk according to test class and
|
||||
selector.
|
||||
- Prints a descriptive error message to the console on failure. (Bonus:
|
||||
failure message includes a one-line command to see an image diff if
|
||||
you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.)
|
||||
- Supply an optional "identifier" if you want to perform multiple snapshots
|
||||
in a single test method.
|
||||
- Support for `CALayer` via `FBSnapshotVerifyLayer`.
|
||||
- `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes.
|
||||
- `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices).
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Your unit test must be an "application test", not a "logic test." (That is, it
|
||||
must be run within the Simulator so that it has access to UIKit.) In Xcode 5
|
||||
and later new projects only offer application tests, but older projects will
|
||||
have separate targets for the two types.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
`FBSnapshotTestCase` was written at Facebook by
|
||||
[Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by
|
||||
[Todd Krabach](https://facebook.com/toddkrabach).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
`FBSnapshotTestCase` is BSD-licensed. See `LICENSE`.
|
||||
+11
-6
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"name": "PNObject",
|
||||
"version": "0.9.0",
|
||||
"version": "1.0.0",
|
||||
"summary": "PNObject is a simple replica of the more complex ParseObject",
|
||||
"homepage": "https://git.giuseppenucifora.com/giuseppenucifora/PNObject",
|
||||
"license": "MIT",
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"file": "LICENSE"
|
||||
},
|
||||
"authors": {
|
||||
"Giuseppe Nucifora": "me@giuseppenucifora.com"
|
||||
},
|
||||
"source": {
|
||||
"git": "https://git.giuseppenucifora.com/giuseppenucifora/PNObject.git",
|
||||
"tag": "0.9.0"
|
||||
"tag": "1.0.0"
|
||||
},
|
||||
"platforms": {
|
||||
"ios": "8.0"
|
||||
},
|
||||
"requires_arc": true,
|
||||
"source_files": "Pod/Classes/**/*",
|
||||
"source_files": "PNObject/Classes/**/*",
|
||||
"dependencies": {
|
||||
"AFNetworking": [
|
||||
|
||||
@@ -50,7 +52,10 @@
|
||||
"FBSDKLoginKit": [
|
||||
|
||||
],
|
||||
"NACrypto": [
|
||||
"NAChloride": [
|
||||
|
||||
],
|
||||
"DDDKeychainWrapper": [
|
||||
|
||||
],
|
||||
"NSUserDefaults-AESEncryptor": [
|
||||
|
||||
Generated
+14
-32
@@ -22,40 +22,35 @@ PODS:
|
||||
- Bolts/Tasks (1.8.4)
|
||||
- CocoaSecurity (1.2.4)
|
||||
- CodFis-Helper (0.1.3)
|
||||
- DDDKeychainWrapper (1.0.0)
|
||||
- DJLocalization (1.2.2):
|
||||
- DJLocalization/Core (= 1.2.2)
|
||||
- DJLocalization/Core (1.2.2)
|
||||
- Expecta (1.0.5)
|
||||
- Expecta+Snapshots (3.0.0):
|
||||
- Expecta (~> 1.0)
|
||||
- FBSnapshotTestCase/Core (~> 2.0)
|
||||
- Specta (~> 1.0)
|
||||
- FBSDKCoreKit (4.18.0):
|
||||
- Bolts (~> 1.7)
|
||||
- FBSDKLoginKit (4.18.0):
|
||||
- FBSDKCoreKit
|
||||
- FBSDKShareKit (4.18.0):
|
||||
- FBSDKCoreKit
|
||||
- FBSnapshotTestCase (2.1.4):
|
||||
- FBSnapshotTestCase/SwiftSupport (= 2.1.4)
|
||||
- FBSnapshotTestCase/Core (2.1.4)
|
||||
- FBSnapshotTestCase/SwiftSupport (2.1.4):
|
||||
- FBSnapshotTestCase/Core
|
||||
- NACrypto (1.0.6)
|
||||
- NSDate_Utils (0.1.3)
|
||||
- libsodium (1.0.11)
|
||||
- NAChloride (2.2.1):
|
||||
- libsodium
|
||||
- NSDate_Utils (1.0.0)
|
||||
- NSString-Helper (1.0.5)
|
||||
- NSUserDefaults-AESEncryptor (0.0.4):
|
||||
- CocoaSecurity (~> 1.2.2)
|
||||
- nv-ios-http-status (0.0.1)
|
||||
- PEAR-FileManager-iOS (1.3.1)
|
||||
- PNObject (0.9.0):
|
||||
- PNObject (1.0.0):
|
||||
- AFNetworking
|
||||
- CodFis-Helper
|
||||
- DDDKeychainWrapper
|
||||
- DJLocalization
|
||||
- FBSDKCoreKit
|
||||
- FBSDKLoginKit
|
||||
- FBSDKShareKit
|
||||
- NACrypto
|
||||
- NAChloride
|
||||
- NSDate_Utils
|
||||
- NSString-Helper
|
||||
- NSUserDefaults-AESEncryptor
|
||||
@@ -71,23 +66,10 @@ PODS:
|
||||
- UIDevice-Utils (0.1.6)
|
||||
|
||||
DEPENDENCIES:
|
||||
- AFNetworking
|
||||
- CodFis-Helper
|
||||
- Expecta
|
||||
- Expecta+Snapshots
|
||||
- FBSDKCoreKit
|
||||
- FBSDKLoginKit
|
||||
- FBSDKShareKit
|
||||
- FBSnapshotTestCase
|
||||
- NSDate_Utils
|
||||
- NSString-Helper
|
||||
- nv-ios-http-status
|
||||
- PEAR-FileManager-iOS
|
||||
- PNObject (from `../`)
|
||||
- PureLayout
|
||||
- Specta
|
||||
- StrongestPasswordValidator
|
||||
- UIDevice-Utils
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
PNObject:
|
||||
@@ -98,26 +80,26 @@ SPEC CHECKSUMS:
|
||||
Bolts: 8a7995239dbe724f9cba2248b766d48b7ebdd322
|
||||
CocoaSecurity: d288a6f87e0f363823d2cb83e753814a6944f71a
|
||||
CodFis-Helper: 28be4c74d7202542459d72354f59b1215871de87
|
||||
DDDKeychainWrapper: e681a4daba6448786fa83b4941f58102a33b1897
|
||||
DJLocalization: 0c84029af375647d4104a42ae36be87194c46c47
|
||||
Expecta: e1c022fcd33910b6be89c291d2775b3fe27a89fe
|
||||
Expecta+Snapshots: c343f410c7a6392f3e22e78f94c44b6c0749a516
|
||||
FBSDKCoreKit: 15fef8804a4629f98c6f4e55e81a76c9d725d85e
|
||||
FBSDKLoginKit: 6773073e970b2b15fb12e451ce7f11da0532b880
|
||||
FBSDKShareKit: 0b8d6cc3f103c75297eb3c62caec284a2ccf1b9e
|
||||
FBSnapshotTestCase: '094f9f314decbabe373b87cc339bea235a63e07a'
|
||||
NACrypto: ce3900f1775f1b0cc27ce7c4953b94c598a74149
|
||||
NSDate_Utils: 68669d2c81f310ee13026c791f4f0ed227b94c65
|
||||
libsodium: 9aba161d2ee096977ecbdcce1ada69ffe511970c
|
||||
NAChloride: 8f3d4f9a20df6b68840789a22a70aa4fcc437b0c
|
||||
NSDate_Utils: 45d47afab329001ccafe056308d0cc05460e5298
|
||||
NSString-Helper: 459e1b6a62b3bf7db10f01b0d102548608e945c4
|
||||
NSUserDefaults-AESEncryptor: da02cfef056f1e18ebe2748767915f08b274c9c5
|
||||
nv-ios-http-status: b6c2b5fc8656cc19e0d3000dadce2080b99d0e2f
|
||||
PEAR-FileManager-iOS: 3bc403f68a53483f5629aa822f4649e40275c4d3
|
||||
PNObject: f8d8116d0f72252552bddcbc3703d6d1e4719354
|
||||
PNObject: 34839792d6fcc437b3123d0770b4d45e845bf2c9
|
||||
PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd
|
||||
RZDataBinding: 6981e90ddaae2f5e02028323b1043f8c31013109
|
||||
Specta: ac94d110b865115fe60ff2c6d7281053c6f8e8a2
|
||||
StrongestPasswordValidator: 921e42615bdf353513c6f925bffd4fc29865dbd7
|
||||
UIDevice-Utils: 11c10b18d3c6489b45a97436e5ae6064a3622820
|
||||
|
||||
PODFILE CHECKSUM: 7c7096ec3fa75c9e7a1d2b538e5719592b9f5e60
|
||||
PODFILE CHECKSUM: 57090c7ea88a91b49ca8dd73d9e7c6b866e772ba
|
||||
|
||||
COCOAPODS: 1.2.0.beta.1
|
||||
Generated
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Gabriel Handford
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// NAAEAD.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NAAEAD : NSObject
|
||||
|
||||
@property (getter=isSecureDataEnabled) BOOL secureDataEnabled;
|
||||
|
||||
- (NSData *)encryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error;
|
||||
|
||||
- (NSData *)decryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error;
|
||||
|
||||
@end
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// NAAEAD.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAAEAD.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
#import "NASecureData.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NAAEAD
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (NSData *)encryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error {
|
||||
if (!nonce || [nonce length] != NAAEADNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!additionalData) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidAdditionalData, @"Invalid additional data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != NAAEADKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:[data length] + NAAEADASize];
|
||||
|
||||
unsigned long long outLength;
|
||||
int retval = crypto_aead_chacha20poly1305_encrypt([outData mutableBytes], &outLength,
|
||||
[data bytes], [data length],
|
||||
[additionalData bytes], [additionalData length],
|
||||
NULL,
|
||||
[nonce bytes],
|
||||
[key bytes]);
|
||||
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"AEAD encrypt failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (NSData *)decryptChaCha20Poly1305:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key additionalData:(NSData *)additionalData error:(NSError **)error {
|
||||
if (!nonce || [nonce length] != NAAEADNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!additionalData) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidAdditionalData, @"Invalid additional data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != NAAEADKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block unsigned long long outLength;
|
||||
__block int retval = -1;
|
||||
NSMutableData *outData = NAData(self.secureDataEnabled, data.length, ^(void *bytes, NSUInteger length) {
|
||||
retval = crypto_aead_chacha20poly1305_decrypt(bytes, &outLength,
|
||||
NULL,
|
||||
[data bytes], [data length],
|
||||
[additionalData bytes], [additionalData length],
|
||||
[nonce bytes],
|
||||
[key bytes]);
|
||||
});
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [outData na_truncate:outData.length - (NSUInteger)outLength];
|
||||
}
|
||||
|
||||
@end
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// NAAuth.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/16/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*!
|
||||
Computes an authentication tag for a message and a secret key, and provides a way to verify that a given tag is valid for a given message and a key.
|
||||
*/
|
||||
@interface NAAuth : NSObject
|
||||
|
||||
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
/*!
|
||||
Returns YES if verifies OK.
|
||||
*/
|
||||
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
|
||||
@end
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// NAAuth.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/16/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAAuth.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NAAuth
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error {
|
||||
if (!key || [key length] != NAAuthKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:NAAuthSize];
|
||||
|
||||
crypto_auth([outData mutableBytes], [data bytes], [data length], [key bytes]);
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error {
|
||||
if (!key || [key length] != NAAuthKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!auth || [auth length] != NAAuthSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (crypto_auth_verify([auth bytes], [data bytes], [data length], [key bytes]) != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
|
||||
return NO; // Message forged!
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// NABox.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "NABoxKeypair.h"
|
||||
|
||||
@interface NABox : NSObject
|
||||
|
||||
@property (getter=isSecureDataEnabled) BOOL secureDataEnabled;
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error;
|
||||
|
||||
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error;
|
||||
|
||||
@end
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// NABox.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NABox.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NABox
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error {
|
||||
if (!nonce || [nonce length] != NABoxNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!keypair) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid keypair");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:[data length] + NABoxMACSize];
|
||||
|
||||
int retval = crypto_box_easy([outData mutableBytes],
|
||||
[data bytes], [data length],
|
||||
[nonce bytes],
|
||||
[keypair.publicKey bytes],
|
||||
[keypair.secretKey bytes]);
|
||||
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"Encrypt (box) failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce keypair:(NABoxKeypair *)keypair error:(NSError **)error {
|
||||
if (!nonce || [nonce length] != NABoxNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block int retval = -1;
|
||||
NSMutableData *outData = NAData(self.secureDataEnabled, data.length, ^(void *bytes, NSUInteger length) {
|
||||
retval = crypto_box_open_easy(bytes,
|
||||
[data bytes], [data length],
|
||||
[nonce bytes],
|
||||
[keypair.publicKey bytes],
|
||||
[keypair.secretKey bytes]);
|
||||
});
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [outData na_truncate:NABoxMACSize];
|
||||
}
|
||||
|
||||
@end
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// NABoxKeypair.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "NASecureData.h"
|
||||
|
||||
@interface NABoxKeypair : NSObject
|
||||
|
||||
@property (readonly) NSData *publicKey;
|
||||
@property (readonly) NASecureData *secretKey;
|
||||
|
||||
- (instancetype)initWithPublicKey:(NSData *)publicKey secretKey:(NASecureData *)secretKey error:(NSError **)error;
|
||||
|
||||
+ (instancetype)generate:(NSError **)error;
|
||||
|
||||
@end
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// NABoxKeypair.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NABoxKeypair.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
#import "NASecureData.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@interface NABoxKeypair ()
|
||||
@property NSData *publicKey;
|
||||
@property NASecureData *secretKey;
|
||||
@end
|
||||
|
||||
@implementation NABoxKeypair
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (instancetype)initWithPublicKey:(NSData *)publicKey secretKey:(NASecureData *)secretKey error:(NSError **)error {
|
||||
if ((self = [super init])) {
|
||||
|
||||
if (!publicKey || [publicKey length] != NABoxPublicKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid public key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!secretKey || [secretKey length] != NABoxPublicKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid secret key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
_publicKey = publicKey;
|
||||
_secretKey = secretKey;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)generate:(NSError **)error {
|
||||
NSMutableData *publicKey = [NSMutableData dataWithLength:NABoxPublicKeySize];
|
||||
__block int retval = -1;
|
||||
NASecureData *secretKey = [NASecureData secureReadOnlyDataWithLength:NABoxSecretKeySize completion:^(void *bytes, NSUInteger length) {
|
||||
retval = crypto_box_keypair([publicKey mutableBytes], bytes);
|
||||
}];
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"Keypair generate failed");
|
||||
return nil;
|
||||
}
|
||||
return [[NABoxKeypair alloc] initWithPublicKey:publicKey secretKey:secretKey error:error];
|
||||
}
|
||||
|
||||
@end
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// NAChloride.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for NAChloride.
|
||||
FOUNDATION_EXPORT double NAChlorideVersionNumber;
|
||||
|
||||
//! Project version string for NAChloride.
|
||||
FOUNDATION_EXPORT const unsigned char NAChlorideVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <NAChloride/PublicHeader.h>
|
||||
|
||||
#import <NAChloride/NAInterface.h>
|
||||
|
||||
#import <NAChloride/NASecretBox.h>
|
||||
#import <NAChloride/NABox.h>
|
||||
#import <NAChloride/NABoxKeypair.h>
|
||||
#import <NAChloride/NAAuth.h>
|
||||
#import <NAChloride/NAAEAD.h>
|
||||
#import <NAChloride/NAOneTimeAuth.h>
|
||||
#import <NAChloride/NAScrypt.h>
|
||||
#import <NAChloride/NAStream.h>
|
||||
#import <NAChloride/NARandom.h>
|
||||
#import <NAChloride/NASecureData.h>
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// NAInterface.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/25/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM (NSInteger, NAErrorCode) {
|
||||
NAErrorCodeFailure = 1, // Generic failure
|
||||
|
||||
NAErrorCodeInvalidNonce = 100,
|
||||
NAErrorCodeInvalidKey = 101,
|
||||
NAErrorCodeInvalidData = 102,
|
||||
NAErrorCodeInvalidSalt = 103,
|
||||
NAErrorCodeInvalidAdditionalData = 104, // For AEAD
|
||||
|
||||
NAErrorCodeVerificationFailed = 205, // Verification failed
|
||||
};
|
||||
|
||||
extern const size_t NASecretBoxKeySize;
|
||||
extern const size_t NASecretBoxNonceSize;
|
||||
extern const size_t NASecretBoxMACSize;
|
||||
|
||||
extern const size_t NABoxPublicKeySize;
|
||||
extern const size_t NABoxSecretKeySize;
|
||||
extern const size_t NABoxNonceSize;
|
||||
extern const size_t NABoxMACSize;
|
||||
|
||||
extern const size_t NAAuthKeySize;
|
||||
extern const size_t NAAuthSize;
|
||||
|
||||
extern const size_t NAOneTimeAuthKeySize;
|
||||
extern const size_t NAOneTimeAuthSize;
|
||||
|
||||
extern const size_t NAScryptSaltSize;
|
||||
|
||||
extern const size_t NAStreamKeySize;
|
||||
extern const size_t NAStreamNonceSize;
|
||||
|
||||
extern const size_t NAXSalsaKeySize;
|
||||
extern const size_t NAXSalsaNonceSize;
|
||||
|
||||
extern const size_t NAAEADKeySize;
|
||||
extern const size_t NAAEADNonceSize;
|
||||
extern const size_t NAAEADASize;
|
||||
|
||||
|
||||
// Thread safe libsodium init
|
||||
void NAChlorideInit(void);
|
||||
|
||||
// Don't call this directly (use NAChlorideInit). This is made accessible for testing.
|
||||
int NASodiumInit(void);
|
||||
|
||||
|
||||
typedef id (^NAWork)(NSError **error);
|
||||
typedef void (^NACompletion)(NSError *error, id output);
|
||||
void NADispatch(dispatch_queue_t queue, NAWork work, NACompletion completion);
|
||||
|
||||
#define NAError(CODE, DESC) [NSError errorWithDomain:@"NAChloride" code:CODE userInfo:@{NSLocalizedDescriptionKey: DESC}];
|
||||
|
||||
typedef void (^NADataCompletion)(void *bytes, NSUInteger length);
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// NAInterface.m
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
const size_t NASecretBoxKeySize = crypto_secretbox_KEYBYTES;
|
||||
const size_t NASecretBoxNonceSize = crypto_secretbox_NONCEBYTES;
|
||||
const size_t NASecretBoxMACSize = crypto_secretbox_MACBYTES;
|
||||
|
||||
const size_t NABoxPublicKeySize = crypto_box_PUBLICKEYBYTES;
|
||||
const size_t NABoxSecretKeySize = crypto_box_SECRETKEYBYTES;
|
||||
const size_t NABoxNonceSize = crypto_box_NONCEBYTES;
|
||||
const size_t NABoxMACSize = crypto_box_MACBYTES;
|
||||
|
||||
const size_t NAAuthKeySize = crypto_auth_KEYBYTES;
|
||||
const size_t NAAuthSize = crypto_auth_BYTES;
|
||||
|
||||
const size_t NAOneTimeAuthKeySize = crypto_onetimeauth_KEYBYTES;
|
||||
const size_t NAOneTimeAuthSize = crypto_onetimeauth_BYTES;
|
||||
|
||||
const size_t NAScryptSaltSize = crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
|
||||
|
||||
const size_t NAStreamKeySize = crypto_stream_KEYBYTES;
|
||||
const size_t NAStreamNonceSize = crypto_stream_NONCEBYTES;
|
||||
|
||||
const size_t NAXSalsaKeySize = crypto_stream_xsalsa20_KEYBYTES;
|
||||
const size_t NAXSalsaNonceSize = crypto_stream_xsalsa20_NONCEBYTES;
|
||||
|
||||
const size_t NAAEADKeySize = crypto_aead_chacha20poly1305_KEYBYTES;
|
||||
const size_t NAAEADNonceSize = crypto_aead_chacha20poly1305_NPUBBYTES;
|
||||
const size_t NAAEADASize = crypto_aead_chacha20poly1305_ABYTES;
|
||||
|
||||
|
||||
void NAChlorideInit(void) {
|
||||
static dispatch_once_t sodiumInit;
|
||||
dispatch_once(&sodiumInit, ^{ NASodiumInit(); });
|
||||
}
|
||||
|
||||
int NASodiumInit(void) {
|
||||
return sodium_init();
|
||||
}
|
||||
|
||||
void NADispatch(dispatch_queue_t queue, NAWork work, NACompletion completion) {
|
||||
dispatch_async(queue, ^{
|
||||
|
||||
NSError *error = nil;
|
||||
id output = work(&error);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(error, output);
|
||||
});
|
||||
});
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// NAOneTimeAuth.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 9/24/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*!
|
||||
Generates a MAC for a given message and shared key using Poly1305 algorithm
|
||||
(key may NOT be reused across messages).
|
||||
*/
|
||||
@interface NAOneTimeAuth : NSObject
|
||||
|
||||
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
/*!
|
||||
Returns YES if verifies OK.
|
||||
*/
|
||||
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
@end
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// NAOneTimeAuth.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 9/24/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAOneTimeAuth.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NAOneTimeAuth
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (NSData *)auth:(NSData *)data key:(NSData *)key error:(NSError **)error {
|
||||
if (!key || [key length] != NAOneTimeAuthKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:NAOneTimeAuthSize];
|
||||
|
||||
crypto_onetimeauth([outData mutableBytes], [data bytes], [data length], [key bytes]);
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (BOOL)verify:(NSData *)auth data:(NSData *)data key:(NSData *)key error:(NSError **)error {
|
||||
if (!key || [key length] != NAOneTimeAuthKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!auth || [auth length] != NAOneTimeAuthSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (crypto_onetimeauth_verify([auth bytes], [data bytes], [data length], [key bytes]) != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
|
||||
return NO; // Message forged!
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// NARandom.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/16/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "NASecureData.h"
|
||||
|
||||
@interface NARandom : NSObject
|
||||
|
||||
/*!
|
||||
Random data of length bytes.
|
||||
*/
|
||||
+ (NSData *)randomData:(NSUInteger)length;
|
||||
|
||||
/*!
|
||||
Random & secure data of length bytes.
|
||||
*/
|
||||
+ (NASecureData *)randomSecureReadOnlyData:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// NARandom.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/16/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NARandom.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NARandom
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
+ (NSData *)randomData:(NSUInteger)length {
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:length];
|
||||
randombytes_buf([outData mutableBytes], length);
|
||||
return outData;
|
||||
}
|
||||
|
||||
+ (NASecureData *)randomSecureReadOnlyData:(NSUInteger)length {
|
||||
NASecureData *secureData = [NASecureData secureReadOnlyDataWithLength:length completion:^(void *bytes, NSUInteger length) {
|
||||
randombytes_buf(bytes, length);
|
||||
}];
|
||||
return secureData;
|
||||
}
|
||||
|
||||
@end
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// NAScrypt.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/19/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NAScrypt : NSObject
|
||||
|
||||
/*!
|
||||
Key derivation.
|
||||
|
||||
@param password Password
|
||||
@param salt Must be NAScryptSaltSize
|
||||
|
||||
Default opslimit is crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE.
|
||||
Default memlimit is crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE.
|
||||
*/
|
||||
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt error:(NSError **)error;
|
||||
|
||||
/*!
|
||||
Use the default scrypt. This is for advanced use only.
|
||||
*/
|
||||
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt N:(uint64_t)N r:(uint32_t)r p:(uint32_t)p length:(size_t)length error:(NSError **)error;
|
||||
|
||||
@end
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// NAScrypt.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/19/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAScrypt.h"
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NAScrypt
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt error:(NSError **)error {
|
||||
if (!salt || [salt length] != NAScryptSaltSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidSalt, @"Invalid salt")
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *key = [NSMutableData dataWithLength:crypto_box_SEEDBYTES];
|
||||
|
||||
int retval = crypto_pwhash_scryptsalsa208sha256([key mutableBytes], key.length, password.bytes, password.length, salt.bytes, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE);
|
||||
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"Scrypt failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
+ (NSData *)scrypt:(NSData *)password salt:(NSData *)salt N:(uint64_t)N r:(uint32_t)r p:(uint32_t)p length:(size_t)length error:(NSError **)error {
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:length];
|
||||
|
||||
int retval = crypto_pwhash_scryptsalsa208sha256_ll((uint8_t *)password.bytes, password.length, (uint8_t *)salt.bytes, salt.length, N, r, p, [outData mutableBytes], outData.length);
|
||||
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"Scrypt failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSAssert([outData length] == length, @"Mismatched output length");
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
+9
-4
@@ -1,14 +1,19 @@
|
||||
//
|
||||
// NATwoFish.h
|
||||
// NACrypto
|
||||
// NASecretBox.h
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NATwoFish : NSObject
|
||||
/*!
|
||||
Encrypts and authenticates a message using a shared key and nonce.
|
||||
*/
|
||||
@interface NASecretBox : NSObject
|
||||
|
||||
@property (getter=isSecureDataEnabled) BOOL secureDataEnabled;
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// NASecretBox.m
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NASecretBox.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
#import "NASecureData.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NASecretBox
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
if (!nonce || [nonce length] != NASecretBoxNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != NASecretBoxKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Add space for authentication tag of size MACBYTES
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:[data length] + NASecretBoxMACSize];
|
||||
|
||||
int retval = crypto_secretbox_easy([outData mutableBytes],
|
||||
[data bytes], [data length],
|
||||
[nonce bytes],
|
||||
[key bytes]);
|
||||
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"Encrypt (secret box) failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
if (!nonce || [nonce length] != NASecretBoxNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidData, @"Invalid data");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != NASecretBoxKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block int retval = -1;
|
||||
NSMutableData *outData = NAData(self.secureDataEnabled, data.length, ^(void *bytes, NSUInteger length) {
|
||||
retval = crypto_secretbox_open_easy(bytes,
|
||||
[data bytes], [data length],
|
||||
[nonce bytes], [key bytes]);
|
||||
});
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeVerificationFailed, @"Verification failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Remove MAC bytes from data
|
||||
return [outData na_truncate:NASecretBoxMACSize];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// NASecureData.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/19/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
typedef NS_ENUM (NSInteger, NASecureDataProtection) {
|
||||
NASecureDataProtectionReadWrite = 0, // Default no protection
|
||||
NASecureDataProtectionReadOnly,
|
||||
NASecureDataProtectionNoAccess,
|
||||
};
|
||||
|
||||
/*!
|
||||
Secure memory using libsodium.
|
||||
*/
|
||||
@interface NASecureData : NSMutableData // Subclassing for convienience
|
||||
|
||||
@property (nonatomic) NASecureDataProtection protection;
|
||||
|
||||
/*!
|
||||
Secure and read only data.
|
||||
*/
|
||||
+ (instancetype)secureReadOnlyDataWithLength:(NSUInteger)length completion:(NADataCompletion)completion;
|
||||
|
||||
/*!
|
||||
Secure data is has read/write protection in this block.
|
||||
*/
|
||||
- (void)readWrite:(void (^)(NASecureData *secureData))completion;
|
||||
|
||||
/*!
|
||||
Truncate.
|
||||
*/
|
||||
- (NASecureData *)truncate:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// Optional building of secure NSData
|
||||
NSMutableData *NAData(BOOL secure, NSUInteger length, NADataCompletion completion);
|
||||
|
||||
|
||||
@interface NSMutableData (NASecureData)
|
||||
|
||||
- (NSData *)na_truncate:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// NASecureData.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/19/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NASecureData.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@interface NASecureData ()
|
||||
@property void *secureBytes;
|
||||
@property NSUInteger secureLength;
|
||||
@end
|
||||
|
||||
@implementation NASecureData
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (instancetype)initWithLength:(NSUInteger)length {
|
||||
if ((self = [super init])) {
|
||||
NAChlorideInit(); // It's already init'ed, but just to be safe
|
||||
_secureLength = length;
|
||||
_secureBytes = sodium_malloc(length);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)secureReadOnlyDataWithLength:(NSUInteger)length completion:(NADataCompletion)completion {
|
||||
NASecureData *secureData = [[NASecureData alloc] initWithLength:length];
|
||||
completion(secureData.secureBytes, secureData.length);
|
||||
secureData.protection = NASecureDataProtectionReadOnly;
|
||||
return secureData;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
sodium_free(_secureBytes);
|
||||
}
|
||||
|
||||
- (void)setProtection:(NASecureDataProtection)protection {
|
||||
switch (protection) {
|
||||
// Keep these case statements order from most secure to least secure in case some jerk removes a break;
|
||||
case NASecureDataProtectionReadWrite: sodium_mprotect_readwrite(_secureBytes); break;
|
||||
case NASecureDataProtectionReadOnly: sodium_mprotect_readonly(_secureBytes); break;
|
||||
case NASecureDataProtectionNoAccess: sodium_mprotect_noaccess(_secureBytes); break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)length {
|
||||
return _secureLength;
|
||||
}
|
||||
|
||||
- (const void *)bytes {
|
||||
return _secureBytes;
|
||||
}
|
||||
|
||||
- (void *)mutableBytes {
|
||||
return _secureBytes;
|
||||
}
|
||||
|
||||
- (void)readWrite:(void (^)(NASecureData *secureData))completion {
|
||||
NASecureDataProtection protection = self.protection;
|
||||
self.protection = NASecureDataProtectionReadWrite;
|
||||
completion(self);
|
||||
self.protection = protection;
|
||||
}
|
||||
|
||||
- (NASecureData *)truncate:(NSUInteger)length {
|
||||
if (length == 0) return self;
|
||||
return [NASecureData secureReadOnlyDataWithLength:(self.length - length) completion:^(void *bytes, NSUInteger length) {
|
||||
memcpy(bytes, self.bytes, length);
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSData *)na_truncate:(NSUInteger)length { return [self truncate:length]; }
|
||||
|
||||
@end
|
||||
|
||||
NSMutableData *NAData(BOOL secure, NSUInteger length, NADataCompletion completion) {
|
||||
if (!secure) {
|
||||
NSMutableData *data = [NSMutableData dataWithLength:length];
|
||||
completion([data mutableBytes], length);
|
||||
return data;
|
||||
} else {
|
||||
return [NASecureData secureReadOnlyDataWithLength:length completion:completion];
|
||||
}
|
||||
}
|
||||
|
||||
@implementation NSMutableData (NASecureData)
|
||||
|
||||
- (NSData *)na_truncate:(NSUInteger)length {
|
||||
if (length == 0) return self;
|
||||
return [NSData dataWithBytes:self.bytes length:self.length - length];
|
||||
}
|
||||
|
||||
@end
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// NAStream.h
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NAStream : NSObject
|
||||
|
||||
- (NSData *)xor:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
@end
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// NAStream.m
|
||||
// NAChloride
|
||||
//
|
||||
// Created by Gabriel on 6/18/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAStream.h"
|
||||
|
||||
#import "NAInterface.h"
|
||||
|
||||
#import "sodium.h"
|
||||
|
||||
@implementation NAStream
|
||||
|
||||
+ (void)initialize { NAChlorideInit(); }
|
||||
|
||||
- (NSData *)xor:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
if (!nonce || [nonce length] < NAStreamNonceSize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidNonce, @"Invalid stream nonce");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != NAStreamKeySize) {
|
||||
if (error) *error = NAError(NAErrorCodeInvalidKey, @"Invalid stream key");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:[data length]];
|
||||
|
||||
int retval = crypto_stream_xor([outData mutableBytes], [data bytes], [data length], [nonce bytes], [key bytes]);
|
||||
if (retval != 0) {
|
||||
if (error) *error = NAError(NAErrorCodeFailure, @"Stream failed");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
+189
@@ -0,0 +1,189 @@
|
||||
NAChloride
|
||||
===========
|
||||
|
||||
This project wraps [libsodium](https://github.com/jedisct1/libsodium) for:
|
||||
|
||||
* Secure Memory
|
||||
* Random Data
|
||||
* Secret-Key
|
||||
* Authenticated Encryption
|
||||
* Authentication
|
||||
* AEAD
|
||||
* Public-Key
|
||||
* Authenticated Encryption
|
||||
* One-Time Authentication
|
||||
* Password Hashing: *Scrypt*
|
||||
* Stream Ciphers: *XSalsa20*
|
||||
|
||||
More wrappers are coming soon.
|
||||
|
||||
Do you want to work on crypto at Keybase? [We're hiring](https://keybase.io/jobs).
|
||||
|
||||
If you are looking for other non-libsodium related crypto (that used to be here), see [NACrypto](https://github.com/gabriel/NACrypto).
|
||||
|
||||
# Podfile
|
||||
|
||||
```ruby
|
||||
pod "NAChloride"
|
||||
```
|
||||
|
||||
# Init
|
||||
|
||||
You should call `NAChlorideInit()` to initialize on app start. It is thread safe and multiple calls are ignored. We automatically call this as well as a safety measure.
|
||||
|
||||
```objc
|
||||
NAChlorideInit();
|
||||
```
|
||||
|
||||
# Secure Memory
|
||||
|
||||
See [Securing Memory Allocations](https://download.libsodium.org/doc/helpers/memory_management.html).
|
||||
|
||||
```objc
|
||||
NASecureData *secureData = [NASecureData secureReadOnlyDataWithLength:length completion:^(void *bytes, NSUInteger length) {
|
||||
// Set the bytes here. After this it will be read-only.
|
||||
}];
|
||||
|
||||
// After the block executes, secureData is read-only. You can set it to no access (or read/write).
|
||||
// If you set it to no access and secureData.bytes is accessed, it will SIGABRT. For example,
|
||||
// secureData.protection = NASecureDataProtectionNoAccess;
|
||||
```
|
||||
|
||||
Some classes like NASecretBox, NABox and NAAEAD have an option to enable secureMemory (on decrypt).
|
||||
|
||||
NASecureData subclasses NSMutableData for compatibility and usage with other APIs.
|
||||
|
||||
# Generating Random Data
|
||||
|
||||
See [Generating Random Data](https://download.libsodium.org/doc/generating_random_data/index.html).
|
||||
|
||||
```objc
|
||||
NSData *data = [NARandom randomData:32]; // 32 bytes of random data
|
||||
NSData *data = [NARandom randomSecureReadOnlyData:32]; // 32 bytes of random, secure, read-only data
|
||||
```
|
||||
|
||||
# Secret-Key Cryptography
|
||||
|
||||
## Authenticated Encryption
|
||||
|
||||
Encrypts and authenticates a message using a shared key and nonce.
|
||||
|
||||
See [Authenticated Encryption](https://download.libsodium.org/doc/secret-key_cryptography/authenticated_encryption.html).
|
||||
|
||||
```objc
|
||||
NSData *key = [NARandom randomData:NASecretBoxKeySize];
|
||||
NSData *nonce = [NARandom randomData:NASecretBoxNonceSize];
|
||||
NSData *message = [@"This is a secret message" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NASecretBox *secretBox = [[NASecretBox alloc] init];
|
||||
NSError *error = nil;
|
||||
NSData *encrypted = [secretBox encrypt:message nonce:nonce key:key error:&error];
|
||||
// If an error occurred encrypted will be nil and error set
|
||||
|
||||
NSData *decrypted = [secretBox decrypt:encrypted nonce:nonce key:key error:&error];
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
See [Authentication](https://download.libsodium.org/doc/secret-key_cryptography/secret-key_authentication.html).
|
||||
|
||||
```objc
|
||||
NSData *key = [NARandom randomData:NAAuthKeySize];
|
||||
NSData *message = [@"This is a message" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
NAAuth *auth = [[NAAuth alloc] init];
|
||||
NSData *authData = [auth auth:message key:key &error];
|
||||
BOOL verified = [auth verify:authData data:message key:key error:&error];
|
||||
```
|
||||
|
||||
## AEAD
|
||||
|
||||
See [Authenticated Encryption with Additional Data](https://download.libsodium.org/doc/secret-key_cryptography/aead.html).
|
||||
|
||||
```objc
|
||||
NSData *key = [NARandom randomData:NAAEADKeySize];
|
||||
NSData *nonce = [NARandom randomData:NAAEADNonceSize];
|
||||
NSData *message = [@"This is a secret message" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *additionalData = [@"Additional data" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NAAEAD *AEAD = [[NAAEAD alloc] init];
|
||||
NSError *error = nil;
|
||||
NSData *encryptedData = [AEAD encryptChaCha20Poly1305:message nonce:nonce key:key additionalData:additionalData error:&error];
|
||||
NSData *decryptedData = [AEAD decryptChaCha20Poly1305:encryptedData nonce:nonce key:key additionalData:additionalData error:&error];
|
||||
```
|
||||
|
||||
# Public-Key Cryptography
|
||||
|
||||
## Authenticated Encryption
|
||||
|
||||
See [Authenticated Encryption](https://download.libsodium.org/doc/public-key_cryptography/authenticated_encryption.html).
|
||||
|
||||
```objc
|
||||
NSError *error = nil;
|
||||
NABoxKeypair *keypair = [NABoxKeypair generate:&error];
|
||||
|
||||
NSData *nonce = [NARandom randomData:NABoxNonceSize];
|
||||
NSData *message = [@"This is a secret message" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NABox *box = [[NABox alloc] init];
|
||||
NSData *encryptedData = [box encrypt:message nonce:nonce keypair:keypair error:&error];
|
||||
NSData *decryptedData = [box decrypt:encryptedData nonce:nonce keypair:keypair error:&error];
|
||||
```
|
||||
|
||||
# Password Hashing
|
||||
|
||||
See [Password Hashing](https://download.libsodium.org/doc/password_hashing/index.html).
|
||||
|
||||
```objc
|
||||
NSData *key = [@"toomanysecrets" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *salt = [NARandom randomData:NAScryptSaltSize];
|
||||
NSError *error = nil;
|
||||
NSData *data = [NAScrypt scrypt:key salt:salt error:&error];
|
||||
```
|
||||
|
||||
# Advanced
|
||||
|
||||
## One-Time Authentication
|
||||
|
||||
Generates a MAC for a given message and shared key using Poly1305 algorithm.
|
||||
Key may NOT be reused across messages.
|
||||
|
||||
See [One-Time Authentication](https://download.libsodium.org/doc/advanced/poly1305.html).
|
||||
|
||||
```objc
|
||||
NSData *key = [NARandom randomData:NAOneTimeAuthKeySize];
|
||||
NSData *message = [@"This is a message" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
NAOneTimeAuth *oneTimeAuth = [[NAOneTimeAuth alloc] init];
|
||||
NSData *auth = [oneTimeAuth auth:message key:key error:&error];
|
||||
BOOL verified = [oneTimeAuth verify:auth data:message key:key error:&error];
|
||||
```
|
||||
|
||||
## Stream Ciphers
|
||||
|
||||
See [XSalsa20](https://download.libsodium.org/doc/advanced/xsalsa20.html).
|
||||
|
||||
```objc
|
||||
NSData *key = [NARandom randomData:NAStreamKeySize];
|
||||
NSData *nonce = [NARandom randomData:NAStreamNonceSize];
|
||||
NAStream *stream = [[NAStream alloc] init];
|
||||
NSError *error = nil;
|
||||
NSData *encrypted = [stream xor:message nonce:nonce key:key error:&error];
|
||||
NSData *decrypted = [stream xor:encrypted nonce:nonce key:key error:&error];
|
||||
```
|
||||
|
||||
## Dispatch
|
||||
|
||||
There is a helper to dispatch these operations on a queue:
|
||||
|
||||
```objc
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
NADispatch(queue, ^id(NSError **error) {
|
||||
return [NAScrypt scrypt:password salt:salt error:error];
|
||||
}, ^(NSError *error, NSData *data) {
|
||||
// This is on the main queue.
|
||||
// Error is set if it failed.
|
||||
});
|
||||
```
|
||||
Generated
-25
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// NAAES.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM (NSUInteger, NAAESAlgorithm) {
|
||||
NAAESAlgorithm256CTR = 1,
|
||||
};
|
||||
|
||||
@interface NAAES : NSObject
|
||||
|
||||
@property (readonly) NAAESAlgorithm algorithm;
|
||||
|
||||
- (instancetype)initWithAlgorithm:(NAAESAlgorithm)algorithm;
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error;
|
||||
|
||||
@end
|
||||
Generated
-72
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// NAAES.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAAES.h"
|
||||
|
||||
//#include "crypto_stream_aes256estream.h"
|
||||
#include <CommonCrypto/CommonCryptor.h>
|
||||
|
||||
#import "NACounter.h"
|
||||
#import "NANSMutableData+Utils.h"
|
||||
|
||||
#define AES_256_CTR_NONCE_BYTES (16)
|
||||
#define AES_256_CTR_KEY_BYTES (32)
|
||||
|
||||
@implementation NAAES
|
||||
|
||||
- (instancetype)initWithAlgorithm:(NAAESAlgorithm)algorithm {
|
||||
if ((self = [super init])) {
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
NSAssert(_algorithm == NAAESAlgorithm256CTR, @"Unsupported algorithm");
|
||||
|
||||
if (!nonce || [nonce length] < AES_256_CTR_NONCE_BYTES) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:600 userInfo:@{NSLocalizedDescriptionKey: @"Invalid AES nonce"}];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != AES_256_CTR_KEY_BYTES) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:601 userInfo:@{NSLocalizedDescriptionKey: @"Invalid AES key"}];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// This libsodium version doesn't return what I expected
|
||||
//int retval = crypto_stream_aes256estream_xor([outData mutableBytes], [data bytes], [data length], [nonce bytes], [key bytes]);
|
||||
|
||||
NSMutableData *outData = [data mutableCopy];
|
||||
|
||||
CCCryptorStatus cryptStatus;
|
||||
CCCryptorRef cryptor;
|
||||
cryptStatus = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR, kCCAlgorithmAES128, ccNoPadding, [nonce bytes], [key bytes], [key length], NULL, 0, 0, kCCModeOptionCTR_BE, &cryptor);
|
||||
|
||||
if (cryptStatus != kCCSuccess) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:604 userInfo:@{NSLocalizedDescriptionKey: @"Failed AES (1)"}];
|
||||
return nil;
|
||||
}
|
||||
|
||||
size_t bytesEncrypted;
|
||||
cryptStatus = CCCryptorUpdate(cryptor, [data bytes],[ data length], [outData mutableBytes], [outData length], &bytesEncrypted);
|
||||
|
||||
if (cryptStatus != kCCSuccess) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:605 userInfo:@{NSLocalizedDescriptionKey: @"Failed AES (2)"}];
|
||||
return nil;
|
||||
}
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
NSAssert(_algorithm == NAAESAlgorithm256CTR, @"Unsupported algorithm");
|
||||
return [self encrypt:data nonce:nonce key:key error:error];
|
||||
}
|
||||
|
||||
@end
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// NACounter.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Counter for CTR mode
|
||||
@interface NACounter : NSObject
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data;
|
||||
|
||||
- (void)increment;
|
||||
|
||||
- (NSData *)data;
|
||||
|
||||
@end
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
//
|
||||
// NACounter.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NACounter.h"
|
||||
|
||||
@interface NACounter ()
|
||||
@property NSMutableData *counterData;
|
||||
@end
|
||||
|
||||
#define DEFAULT_LENGTH (16)
|
||||
|
||||
@implementation NACounter
|
||||
|
||||
- (instancetype)init {
|
||||
NSAssert(NO, @"Use initWithData:");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data {
|
||||
if ((self = [super init])) {
|
||||
_counterData = [data mutableCopy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)increment {
|
||||
for (NSInteger i = [_counterData length] - 1; i >= 0; i--) {
|
||||
uint8_t *bytes = [_counterData mutableBytes];
|
||||
bytes[i]++;
|
||||
if (bytes[i])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)data {
|
||||
return _counterData;
|
||||
}
|
||||
|
||||
@end
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// NACrypto.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/16/15.
|
||||
// Copyright (c) 2015 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//! Project version number for NACrypto.
|
||||
FOUNDATION_EXPORT double NACryptoVersionNumber;
|
||||
|
||||
//! Project version string for NACrypto.
|
||||
FOUNDATION_EXPORT const unsigned char NACryptoVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <NACrypto/PublicHeader.h>
|
||||
|
||||
#import <NACrypto/NASecRandom.h>
|
||||
#import <NACrypto/NAHMAC.h>
|
||||
#import <NACrypto/NAKeychain.h>
|
||||
|
||||
#import <NACrypto/NATwoFish.h>
|
||||
#import <NACrypto/NAAES.h>
|
||||
#import <NACrypto/NACounter.h>
|
||||
|
||||
#import <NACrypto/NADigest.h>
|
||||
#import <NACrypto/NASHA3.h>
|
||||
|
||||
#import <NACrypto/NANSData+Utils.h>
|
||||
#import <NACrypto/NANSString+Utils.h>
|
||||
#import <NACrypto/NANSMutableData+Utils.h>
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// NADigest.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 7/3/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM (NSUInteger, NADigestAlgorithm) {
|
||||
//NADigestAlgorithmSHA1 = 1, // SHA1 might not be secure enough
|
||||
NADigestAlgorithmSHA2_224 = 2,
|
||||
NADigestAlgorithmSHA2_256,
|
||||
NADigestAlgorithmSHA2_384,
|
||||
NADigestAlgorithmSHA2_512,
|
||||
|
||||
// SHA3
|
||||
NADigestAlgorithmSHA3_256,
|
||||
NADigestAlgorithmSHA3_384,
|
||||
NADigestAlgorithmSHA3_512,
|
||||
};
|
||||
|
||||
@interface NADigest : NSObject
|
||||
|
||||
@property NADigestAlgorithm algorithm;
|
||||
|
||||
- (instancetype)initWithAlgorithm:(NADigestAlgorithm)algorithm;
|
||||
|
||||
- (NSData *)digestForData:(NSData *)data;
|
||||
|
||||
+ (NSData *)digestForData:(NSData *)data algorithm:(NADigestAlgorithm)algorithm;
|
||||
|
||||
@end
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
//
|
||||
// NADigest.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 7/3/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NADigest.h"
|
||||
#import "NASHA3.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
@implementation NADigest
|
||||
|
||||
- (instancetype)initWithAlgorithm:(NADigestAlgorithm)algorithm {
|
||||
if ((self = [super init])) {
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)digestForData:(NSData *)data {
|
||||
return [NADigest digestForData:data algorithm:_algorithm];
|
||||
}
|
||||
|
||||
+ (NSData *)digestForData:(NSData *)data algorithm:(NADigestAlgorithm)algorithm {
|
||||
switch (algorithm) {
|
||||
case NADigestAlgorithmSHA2_224:
|
||||
case NADigestAlgorithmSHA2_256:
|
||||
case NADigestAlgorithmSHA2_384:
|
||||
case NADigestAlgorithmSHA2_512:
|
||||
return [self _SHAForData:data algorithm:algorithm];
|
||||
|
||||
case NADigestAlgorithmSHA3_256:
|
||||
return [NASHA3 SHA3ForData:data digestBitLength:256];
|
||||
case NADigestAlgorithmSHA3_384:
|
||||
return [NASHA3 SHA3ForData:data digestBitLength:384];
|
||||
case NADigestAlgorithmSHA3_512:
|
||||
return [NASHA3 SHA3ForData:data digestBitLength:512];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSData *)_SHAForData:(NSData *)data algorithm:(NADigestAlgorithm)algorithm {
|
||||
switch (algorithm) {
|
||||
case NADigestAlgorithmSHA2_224: {
|
||||
uint8_t digest[CC_SHA224_DIGEST_LENGTH];
|
||||
CC_SHA224(data.bytes, (CC_LONG)data.length, digest);
|
||||
return [[NSData alloc] initWithBytes:digest length:sizeof(digest)];
|
||||
}
|
||||
|
||||
case NADigestAlgorithmSHA2_256: {
|
||||
uint8_t digest[CC_SHA256_DIGEST_LENGTH];
|
||||
CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
|
||||
return [[NSData alloc] initWithBytes:digest length:sizeof(digest)];
|
||||
}
|
||||
|
||||
case NADigestAlgorithmSHA2_384: {
|
||||
uint8_t digest[CC_SHA384_DIGEST_LENGTH];
|
||||
CC_SHA384(data.bytes, (CC_LONG)data.length, digest);
|
||||
return [[NSData alloc] initWithBytes:digest length:sizeof(digest)];
|
||||
}
|
||||
|
||||
case NADigestAlgorithmSHA2_512: {
|
||||
uint8_t digest[CC_SHA512_DIGEST_LENGTH];
|
||||
CC_SHA512(data.bytes, (CC_LONG)data.length, digest);
|
||||
return [[NSData alloc] initWithBytes:digest length:sizeof(digest)];
|
||||
}
|
||||
|
||||
default: {
|
||||
NSAssert(NO, @"Unsupported algorithm");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
-34
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// NAHMAC.h
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM (NSUInteger, NAHMACAlgorithm) {
|
||||
NAHMACAlgorithmSHA1,
|
||||
NAHMACAlgorithmSHA2_224,
|
||||
NAHMACAlgorithmSHA2_256,
|
||||
NAHMACAlgorithmSHA2_384,
|
||||
NAHMACAlgorithmSHA2_512,
|
||||
|
||||
// SHA3
|
||||
NAHMACAlgorithmSHA3_256,
|
||||
NAHMACAlgorithmSHA3_384,
|
||||
NAHMACAlgorithmSHA3_512,
|
||||
};
|
||||
|
||||
@interface NAHMAC : NSObject
|
||||
|
||||
@property NAHMACAlgorithm algorithm;
|
||||
|
||||
- (instancetype)initWithAlgorithm:(NAHMACAlgorithm)algorithm;
|
||||
|
||||
- (NSData *)HMACForKey:(NSData *)key data:(NSData *)data;
|
||||
|
||||
+ (NSData *)HMACForKey:(NSData *)key data:(NSData *)data algorithm:(NAHMACAlgorithm)algorithm;
|
||||
|
||||
@end
|
||||
Generated
-121
@@ -1,121 +0,0 @@
|
||||
//
|
||||
// NAHMAC.m
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAHMAC.h"
|
||||
|
||||
#include <CommonCrypto/CommonHMAC.h>
|
||||
#import "NANSData+Utils.h"
|
||||
#import "NANSString+Utils.h"
|
||||
#import "NASHA3.h"
|
||||
|
||||
@implementation NAHMAC
|
||||
|
||||
- (instancetype)initWithAlgorithm:(NAHMACAlgorithm)algorithm {
|
||||
if ((self = [super init])) {
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)HMACForKey:(NSData *)key data:(NSData *)data {
|
||||
return [NAHMAC HMACForKey:key data:data algorithm:_algorithm];
|
||||
}
|
||||
|
||||
+ (NSData *)HMACForKey:(NSData *)key data:(NSData *)data algorithm:(NAHMACAlgorithm)algorithm {
|
||||
NSParameterAssert(key);
|
||||
|
||||
switch (algorithm) {
|
||||
case NAHMACAlgorithmSHA1:
|
||||
case NAHMACAlgorithmSHA2_224:
|
||||
case NAHMACAlgorithmSHA2_256:
|
||||
case NAHMACAlgorithmSHA2_384:
|
||||
case NAHMACAlgorithmSHA2_512:
|
||||
return [self _HMACSHAForKey:key data:data algorithm:algorithm];
|
||||
|
||||
case NAHMACAlgorithmSHA3_256:
|
||||
case NAHMACAlgorithmSHA3_384:
|
||||
case NAHMACAlgorithmSHA3_512:
|
||||
return [self _HMACSHA3ForKey:key data:data algorithm:algorithm];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSData *)_HMACSHAForKey:(NSData *)key data:(NSData *)data algorithm:(NAHMACAlgorithm)algorithm {
|
||||
CCHmacAlgorithm ccAlgorithm;
|
||||
NSUInteger dataLength;
|
||||
switch (algorithm) {
|
||||
case NAHMACAlgorithmSHA1: ccAlgorithm = kCCHmacAlgSHA1; dataLength = CC_SHA1_DIGEST_LENGTH; break;
|
||||
case NAHMACAlgorithmSHA2_224: ccAlgorithm = kCCHmacAlgSHA224; dataLength = CC_SHA224_DIGEST_LENGTH; break;
|
||||
case NAHMACAlgorithmSHA2_256: ccAlgorithm = kCCHmacAlgSHA256; dataLength = CC_SHA256_DIGEST_LENGTH; break;
|
||||
case NAHMACAlgorithmSHA2_384: ccAlgorithm = kCCHmacAlgSHA384; dataLength = CC_SHA384_DIGEST_LENGTH; break;
|
||||
case NAHMACAlgorithmSHA2_512: ccAlgorithm = kCCHmacAlgSHA512; dataLength = CC_SHA512_DIGEST_LENGTH; break;
|
||||
default:
|
||||
NSAssert(NO, @"Unsupported algorithm");
|
||||
return nil;
|
||||
}
|
||||
|
||||
CCHmacContext ctx;
|
||||
CCHmacInit(&ctx, ccAlgorithm, [key bytes], [key length]);
|
||||
|
||||
CCHmacUpdate(&ctx, [data bytes], [data length]);
|
||||
|
||||
NSMutableData *macOut = [NSMutableData dataWithLength:dataLength];
|
||||
CCHmacFinal(&ctx, [macOut mutableBytes]);
|
||||
return macOut;
|
||||
}
|
||||
|
||||
+ (NSData *)_HMACSHA3ForKey:(NSData *)key data:(NSData *)data algorithm:(NAHMACAlgorithm)algorithm {
|
||||
NSUInteger blockLength = 72;
|
||||
NSUInteger keyLength = [key length];
|
||||
|
||||
NSUInteger digestBitLength;
|
||||
switch (algorithm) {
|
||||
case NAHMACAlgorithmSHA3_256: digestBitLength = 256; break;
|
||||
case NAHMACAlgorithmSHA3_384: digestBitLength = 384; break;
|
||||
case NAHMACAlgorithmSHA3_512: digestBitLength = 512; break;
|
||||
|
||||
default:
|
||||
NSAssert(NO, @"Unsupported algorithm");
|
||||
return nil;
|
||||
}
|
||||
|
||||
//
|
||||
// Used this as a reference:
|
||||
// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/API/CommonHMAC.c
|
||||
//
|
||||
|
||||
uint8_t *keyP;
|
||||
|
||||
if (keyLength > blockLength) {
|
||||
key = [NASHA3 SHA3ForData:key digestBitLength:digestBitLength];
|
||||
keyLength = [key length];
|
||||
}
|
||||
|
||||
NSMutableData *keyIn = [NSMutableData dataWithLength:blockLength];
|
||||
uint8_t *k_ipad = [keyIn mutableBytes];
|
||||
NSMutableData *keyOut = [NSMutableData dataWithLength:blockLength];
|
||||
uint8_t *k_opad = [keyOut mutableBytes];
|
||||
|
||||
keyP = (uint8_t *)[key bytes];
|
||||
/* Copy the key into k_ipad and k_opad while doing the XOR. */
|
||||
for (uint32_t byte = 0; byte < keyLength; byte++) {
|
||||
k_ipad[byte] = keyP[byte] ^ 0x36;
|
||||
k_opad[byte] = keyP[byte] ^ 0x5c;
|
||||
}
|
||||
/* Fill the remainder of k_ipad and k_opad with 0 XORed with the appropriate value. */
|
||||
if (keyLength < blockLength) {
|
||||
memset(k_ipad + keyLength, 0x36, blockLength - keyLength);
|
||||
memset(k_opad + keyLength, 0x5c, blockLength - keyLength);
|
||||
}
|
||||
|
||||
NSData *macOut = [NASHA3 SHA3ForDatas:@[keyIn, data] digestBitLength:digestBitLength];
|
||||
|
||||
/* Perform outer digest */
|
||||
return [NASHA3 SHA3ForDatas:@[keyOut, macOut] digestBitLength:digestBitLength];
|
||||
}
|
||||
|
||||
@end
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// NAKeychain.h
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/21/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NAKeychain : NSObject
|
||||
|
||||
+ (BOOL)addSymmetricKey:(NSData *)symmetricKey applicationLabel:(NSString *)applicationLabel tag:(NSData *)tag label:(NSString *)label;
|
||||
|
||||
+ (NSData *)symmetricKeyWithApplicationLabel:(NSString *)applicationLabel;
|
||||
|
||||
@end
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
//
|
||||
// NAKeychain.m
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/21/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NAKeychain.h"
|
||||
|
||||
@implementation NAKeychain
|
||||
|
||||
+ (BOOL)addKey:(NSData *)key attributes:(NSDictionary *)attributes {
|
||||
if (!key || attributes[(__bridge id)(kSecAttrKeyClass)] == nil || attributes[(__bridge id)(kSecAttrApplicationLabel)] == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableDictionary *allAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
|
||||
allAttributes[(__bridge __strong id)(kSecClass)] = (__bridge id)(kSecClassKey);
|
||||
allAttributes[(__bridge __strong id)(kSecValueData)] = key;
|
||||
if (allAttributes[(__bridge id)(kSecAttrIsPermanent)] == nil) {
|
||||
allAttributes[(__bridge __strong id)(kSecAttrIsPermanent)] = @(YES);
|
||||
}
|
||||
|
||||
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)(allAttributes), NULL);
|
||||
if (status != errSecSuccess) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)addSymmetricKey:(NSData *)symmetricKey applicationLabel:(NSString *)applicationLabel tag:(NSData *)tag label:(NSString *)label {
|
||||
|
||||
if (!symmetricKey || !applicationLabel) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
|
||||
attributes[(__bridge __strong id)kSecAttrKeyClass] = (__bridge id)kSecAttrKeyClassSymmetric;
|
||||
attributes[(__bridge __strong id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly;
|
||||
attributes[(__bridge __strong id)kSecAttrApplicationLabel] = applicationLabel;
|
||||
if (tag) {
|
||||
attributes[(__bridge __strong id)kSecAttrApplicationTag] = tag;
|
||||
}
|
||||
if (label) {
|
||||
attributes[(__bridge __strong id)kSecAttrLabel] = label;
|
||||
}
|
||||
|
||||
return [self addKey:symmetricKey attributes:attributes];
|
||||
}
|
||||
|
||||
+ (NSData *)keyWithApplicationLabel:(NSString *)applicationLabel attributes:(NSDictionary *)attributes {
|
||||
if (!applicationLabel || !attributes[(__bridge id)(kSecAttrKeyClass)]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary *allAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
|
||||
allAttributes[(__bridge __strong id)(kSecClass)] = (__bridge id)(kSecClassKey);
|
||||
allAttributes[(__bridge __strong id)kSecAttrApplicationLabel] = applicationLabel;
|
||||
if (allAttributes[(__bridge id)(kSecAttrIsPermanent)] == nil) {
|
||||
allAttributes[(__bridge __strong id)(kSecAttrIsPermanent)] = @(YES);
|
||||
}
|
||||
allAttributes[(__bridge __strong id)(kSecReturnData)] = @(YES);
|
||||
|
||||
CFTypeRef keyData = nil;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)allAttributes, &keyData);
|
||||
if (status != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return (__bridge_transfer NSData *)keyData;
|
||||
}
|
||||
|
||||
+ (NSData *)symmetricKeyWithApplicationLabel:(NSString *)applicationLabel {
|
||||
NSDictionary *attributes = @{(__bridge __strong id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassSymmetric};
|
||||
return [self keyWithApplicationLabel:applicationLabel attributes:attributes];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)keyAttributesWithApplicationLabel:(NSString *)applicationLabel queryAttributes:(NSDictionary *)queryAttributes {
|
||||
if (!applicationLabel || !queryAttributes || !queryAttributes[(__bridge id)(kSecAttrKeyClass)]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableDictionary *allAttributes = [NSMutableDictionary dictionaryWithDictionary:queryAttributes];
|
||||
allAttributes[(__bridge __strong id)(kSecClass)] = (__bridge id)(kSecClassKey);
|
||||
allAttributes[(__bridge __strong id)(kSecAttrApplicationLabel)] = applicationLabel;
|
||||
allAttributes[(__bridge __strong id)(kSecReturnData)] = @YES;
|
||||
allAttributes[(__bridge __strong id)(kSecReturnAttributes)] = @YES;
|
||||
|
||||
CFTypeRef resultData = nil;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)allAttributes, &resultData);
|
||||
if (status != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return (__bridge_transfer NSDictionary *)resultData;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)symmetricKeyAttributesWithApplicationLabel:(NSString *)applicationLabel {
|
||||
NSDictionary *queryAttributes = @{(__bridge __strong id)(kSecAttrKeyClass): (__bridge id)(kSecAttrKeyClassSymmetric)};
|
||||
return [self keyAttributesWithApplicationLabel:applicationLabel queryAttributes:queryAttributes];
|
||||
}
|
||||
|
||||
@end
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
//
|
||||
// NANSData+Utils.h
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSData (NAUtils)
|
||||
|
||||
- (NSString *)na_hexString;
|
||||
|
||||
+ (NSData *)na_dataWithDatas:(NSArray *)datas;
|
||||
|
||||
- (BOOL)na_isEqualConstantTime:(NSData *)data;
|
||||
|
||||
- (void)na_sliceNoCopyAtIndex:(NSUInteger)index data:(NSData **)data1 data:(NSData **)data2;
|
||||
|
||||
+ (NSData *)na_dataNoCopy:(NSData *)data offset:(NSUInteger)offset length:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
//
|
||||
// NANSData+Utils.m
|
||||
// NACL
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NANSData+Utils.h"
|
||||
|
||||
@implementation NSData (NAUtils)
|
||||
|
||||
- (NSString *)na_hexString {
|
||||
if ([self length] == 0) return nil;
|
||||
NSMutableString *hexString = [NSMutableString stringWithCapacity:[self length] * 2];
|
||||
for (NSUInteger i = 0; i < [self length]; ++i) {
|
||||
[hexString appendFormat:@"%02X", *((uint8_t *)[self bytes] + i)];
|
||||
}
|
||||
return [hexString lowercaseString];
|
||||
}
|
||||
|
||||
+ (NSData *)na_dataWithDatas:(NSArray *)datas {
|
||||
NSInteger length = 0;
|
||||
for (NSData *data in datas) {
|
||||
length += data.length;
|
||||
}
|
||||
NSMutableData *combined = [NSMutableData dataWithCapacity:length];
|
||||
for (NSData *data in datas) {
|
||||
[combined appendData:data];
|
||||
}
|
||||
return combined;
|
||||
}
|
||||
|
||||
- (BOOL)na_isEqualConstantTime:(NSData *)data {
|
||||
if ([self length] != [data length]) return NO;
|
||||
|
||||
const uint8_t *dataPtr = (uint8_t *)self.bytes;
|
||||
const uint8_t *data2Ptr = (uint8_t *)data.bytes;
|
||||
|
||||
uint8_t result = 0;
|
||||
for (int i = 0; i < self.length; i++) {
|
||||
result = result ^ (dataPtr[i] ^ data2Ptr[i]);
|
||||
}
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
- (void)na_sliceNoCopyAtIndex:(NSUInteger)index data:(NSData **)data1 data:(NSData **)data2 {
|
||||
if (index > [self length]) {
|
||||
[NSException raise:NSRangeException format:@"Index is out of bounds"];
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
*data1 = [NSData dataWithBytesNoCopy:(uint8_t *)[self bytes] length:index freeWhenDone:NO];
|
||||
} else {
|
||||
*data1 = [NSData data];
|
||||
}
|
||||
|
||||
if ([self length] > index) {
|
||||
*data2 = [NSData dataWithBytesNoCopy:((uint8_t *)[self bytes] + index) length:([self length] - index) freeWhenDone:NO];
|
||||
} else {
|
||||
*data2 = [NSData data];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSData *)na_dataNoCopy:(NSData *)data offset:(NSUInteger)offset length:(NSUInteger)length {
|
||||
return [NSData dataWithBytesNoCopy:((uint8_t *)[data bytes] + offset) length:length freeWhenDone:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// NANSMutableData+Utils.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSMutableData (NAUtils)
|
||||
|
||||
- (void)na_XORWithData:(NSData *)data index:(NSInteger)index;
|
||||
|
||||
@end
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// NANSMutableData+Utils.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NANSMutableData+Utils.h"
|
||||
|
||||
@implementation NSMutableData (NAUtils)
|
||||
|
||||
- (void)na_XORWithData:(NSData *)data index:(NSInteger)index {
|
||||
uint8_t *dataPtr = (uint8_t *)self.mutableBytes;
|
||||
const uint8_t *data2Ptr = (uint8_t *)data.bytes;
|
||||
|
||||
NSAssert(index < self.length, @"Invalid index");
|
||||
|
||||
for (int i = 0; i < data.length && (i + index) < self.length; i++) {
|
||||
dataPtr[i + index] = dataPtr[i + index] ^ data2Ptr[i];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// NANSString+Utils.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/19/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSString (NAUtils)
|
||||
|
||||
- (NSData *)na_dataFromHexString;
|
||||
|
||||
@end
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// NANSString+Utils.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/19/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NANSString+Utils.h"
|
||||
|
||||
@implementation NSString (NAUtils)
|
||||
|
||||
- (NSData *)na_dataFromHexString {
|
||||
if ((self.length % 2) != 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
const char *chars = [self UTF8String];
|
||||
NSMutableData *data = [NSMutableData dataWithCapacity:self.length / 2];
|
||||
char byteChars[3] = {0, 0, 0};
|
||||
unsigned long wholeByte;
|
||||
|
||||
for (int i = 0; i < self.length; i += 2) {
|
||||
byteChars[0] = chars[i];
|
||||
byteChars[1] = chars[i + 1];
|
||||
wholeByte = strtoul(byteChars, NULL, 16);
|
||||
[data appendBytes:&wholeByte length:1];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
Generated
-17
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// NASHA3.h
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/23/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NASHA3 : NSObject
|
||||
|
||||
+ (NSData *)SHA3ForData:(NSData *)data digestBitLength:(NSUInteger)digestBitLength;
|
||||
|
||||
+ (NSData *)SHA3ForDatas:(NSArray *)datas digestBitLength:(NSUInteger)digestBitLength;
|
||||
|
||||
@end
|
||||
Generated
-37
@@ -1,37 +0,0 @@
|
||||
//
|
||||
// NASHA3.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/23/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NASHA3.h"
|
||||
|
||||
#import "KeccakNISTInterface.h"
|
||||
|
||||
@implementation NASHA3
|
||||
|
||||
+ (NSData *)SHA3ForData:(NSData *)data digestBitLength:(NSUInteger)digestBitLength {
|
||||
return [self SHA3ForDatas:@[data] digestBitLength:digestBitLength];
|
||||
}
|
||||
|
||||
+ (NSData *)SHA3ForDatas:(NSArray *)datas digestBitLength:(NSUInteger)digestBitLength {
|
||||
NSUInteger digestLength = digestBitLength/8;
|
||||
NSMutableData *outData = [NSMutableData dataWithLength:digestLength];
|
||||
|
||||
hashState state;
|
||||
int result;
|
||||
result = Init(&state, (int)digestBitLength); // Bit length
|
||||
NSAssert(result == 0, @"Failed to init");
|
||||
for (NSData *data in datas) {
|
||||
result = Update(&state, (const BitSequence *)[data bytes], [data length] * 8); // Bit length
|
||||
NSAssert(result == 0, @"Failed to update");
|
||||
}
|
||||
result = Final(&state, [outData mutableBytes]);
|
||||
NSAssert(result == 0, @"Failed to finalize");
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
@end
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// NASecRandom.h
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NASecRandom : NSObject
|
||||
|
||||
/*!
|
||||
Generate random data.
|
||||
The source code of SecRandomCopyBytes() is located at
|
||||
http://opensource.apple.com/source/Security/Security-55179.11/sec/Security/SecFramework.c
|
||||
|
||||
@param len Number of random bytes to generate
|
||||
@result NSData of length bytes, or nil if an error occurred
|
||||
*/
|
||||
+ (NSData *)randomData:(size_t)numBytes error:(NSError **)error;
|
||||
|
||||
/*!
|
||||
Generate random base64 string.
|
||||
*/
|
||||
+ (NSString *)randomBase64String:(size_t)length error:(NSError **)error;
|
||||
|
||||
@end
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
//
|
||||
// NASecRandom.m
|
||||
//
|
||||
// Created by Gabriel Handford on 1/16/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NASecRandom.h"
|
||||
#import "NANSData+Utils.h"
|
||||
|
||||
#include <Security/SecRandom.h>
|
||||
|
||||
@implementation NASecRandom
|
||||
|
||||
+ (NSData *)randomData:(size_t)numBytes error:(NSError **)error {
|
||||
NSMutableData *data = [NSMutableData dataWithLength:numBytes];
|
||||
int ret = SecRandomCopyBytes(kSecRandomDefault, numBytes, [data mutableBytes]);
|
||||
if (ret == -1) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:50 userInfo:@{NSLocalizedDescriptionKey: @"Unable to generate random data"}];
|
||||
return nil;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
+ (NSString *)randomBase64String:(size_t)length error:(NSError **)error {
|
||||
size_t numBytes = (size_t)ceil((length * 8.0) / 6.0);
|
||||
NSData *data = [self randomData:numBytes error:error];
|
||||
if (!data) return nil;
|
||||
|
||||
NSString *base64String = [data base64EncodedStringWithOptions:0];
|
||||
if ([base64String length] > length) base64String = [base64String substringToIndex:length];
|
||||
return base64String;
|
||||
}
|
||||
|
||||
@end
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// NATwoFish.m
|
||||
// NACrypto
|
||||
//
|
||||
// Created by Gabriel on 6/20/14.
|
||||
// Copyright (c) 2014 Gabriel Handford. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NATwoFish.h"
|
||||
|
||||
#import "NACounter.h"
|
||||
#import "NANSMutableData+Utils.h"
|
||||
#import "NANSData+Utils.h"
|
||||
|
||||
#include "twofish.h"
|
||||
|
||||
@implementation NATwoFish
|
||||
|
||||
static dispatch_once_t twoFishInit;
|
||||
|
||||
- (NSData *)encrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
if (!nonce || [nonce length] < 16) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:700 userInfo:@{NSLocalizedDescriptionKey: @"Invalid TwoFish nonce"}];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!key || [key length] != 32) {
|
||||
if (error) *error = [NSError errorWithDomain:@"NACrypto" code:701 userInfo:@{NSLocalizedDescriptionKey: @"Invalid TwoFish key"}];
|
||||
return nil;
|
||||
}
|
||||
|
||||
dispatch_once(&twoFishInit, ^{ Twofish_initialise(); });
|
||||
|
||||
Twofish_key xkey;
|
||||
Twofish_prepare_key((uint8_t *)[key bytes], (int)[key length], &xkey);
|
||||
|
||||
NACounter *counter = [[NACounter alloc] initWithData:nonce];
|
||||
|
||||
NSInteger blockLength = [nonce length];
|
||||
|
||||
NSMutableData *outData = [data mutableCopy];
|
||||
|
||||
NSMutableData *blockOutData = [NSMutableData dataWithLength:blockLength];
|
||||
|
||||
for (NSInteger index = 0; index < [data length]; index += blockLength) {
|
||||
Twofish_encrypt(&xkey, (uint8_t *)[counter.data bytes], [blockOutData mutableBytes]);
|
||||
[outData na_XORWithData:blockOutData index:index];
|
||||
[counter increment];
|
||||
}
|
||||
|
||||
return outData;
|
||||
}
|
||||
|
||||
- (NSData *)decrypt:(NSData *)data nonce:(NSData *)nonce key:(NSData *)key error:(NSError **)error {
|
||||
return [self encrypt:data nonce:nonce key:key error:error];
|
||||
}
|
||||
|
||||
@end
|
||||
-1691
File diff suppressed because it is too large.
Load diff
-180
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Fast, portable, and easy-to-use Twofish implementation,
|
||||
* Version 0.3.
|
||||
* Copyright (c) 2002 by Niels Ferguson.
|
||||
*
|
||||
* See the twofish.c file for the details of the how and why of this code.
|
||||
*
|
||||
* The author hereby grants a perpetual license to everybody to
|
||||
* use this code for any purpose as long as the copyright message is included
|
||||
* in the source code of this or any derived work.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* PLATFORM FIXES
|
||||
* ==============
|
||||
*
|
||||
* The following definitions have to be fixed for each particular platform
|
||||
* you work on. If you have a multi-platform program, you no doubt have
|
||||
* portable definitions that you can substitute here without changing
|
||||
* the rest of the code.
|
||||
*
|
||||
* The defaults provided here should work on most PC compilers.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* A Twofish_Byte must be an unsigned 8-bit integer.
|
||||
* It must also be the elementary data size of your C platform,
|
||||
* i.e. sizeof( Twofish_Byte ) == 1.
|
||||
*/
|
||||
typedef uint8_t Twofish_Byte;
|
||||
|
||||
/*
|
||||
* A Twofish_UInt32 must be an unsigned integer of at least 32 bits.
|
||||
*
|
||||
* This type is used only internally in the implementation, so ideally it
|
||||
* would not appear in the header file, but it is used inside the
|
||||
* Twofish_key structure which means it has to be included here.
|
||||
*/
|
||||
typedef uint32_t Twofish_UInt32;
|
||||
|
||||
|
||||
/*
|
||||
* END OF PLATFORM FIXES
|
||||
* =====================
|
||||
*
|
||||
* You should not have to touch the rest of this file, but the code
|
||||
* in twofish.c has a few things you need to fix too.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Structure that contains a prepared Twofish key.
|
||||
* A cipher key is used in two stages. In the first stage it is converted
|
||||
* form the original form to an internal representation.
|
||||
* This internal form is then used to encrypt and decrypt data.
|
||||
* This structure contains the internal form. It is rather large: 4256 bytes
|
||||
* on a platform with 32-bit unsigned values.
|
||||
*
|
||||
* Treat this as an opague structure, and don't try to manipulate the
|
||||
* elements in it. I wish I could hide the inside of the structure,
|
||||
* but C doesn't allow that.
|
||||
*/
|
||||
typedef
|
||||
struct
|
||||
{
|
||||
Twofish_UInt32 s[4][256]; /* pre-computed S-boxes */
|
||||
Twofish_UInt32 K[40]; /* Round key words */
|
||||
}
|
||||
Twofish_key;
|
||||
|
||||
|
||||
/*
|
||||
* Initialise and test the Twofish implementation.
|
||||
*
|
||||
* This function MUST be called before any other function in the
|
||||
* Twofish implementation is called.
|
||||
* It only needs to be called once.
|
||||
*
|
||||
* Apart from initialising the implementation it performs a self test.
|
||||
* If the Twofish_fatal function is not called, the code passed the test.
|
||||
* (See the twofish.c file for details on the Twofish_fatal function.)
|
||||
*/
|
||||
extern void Twofish_initialise();
|
||||
|
||||
|
||||
/*
|
||||
* Convert a cipher key to the internal form used for
|
||||
* encryption and decryption.
|
||||
*
|
||||
* The cipher key is an array of bytes; the Twofish_Byte type is
|
||||
* defined above to a type suitable on your platform.
|
||||
*
|
||||
* Any key must be converted to an internal form in the Twofisk_key structure
|
||||
* before it can be used.
|
||||
* The encryption and decryption functions only work with the internal form.
|
||||
* The conversion to internal form need only be done once for each key value.
|
||||
*
|
||||
* Be sure to wipe all key storage, including the Twofish_key structure,
|
||||
* once you are done with the key data.
|
||||
* A simple memset( TwofishKey, 0, sizeof( TwofishKey ) ) will do just fine.
|
||||
*
|
||||
* Unlike most implementations, this one allows any key size from 0 bytes
|
||||
* to 32 bytes. According to the Twofish specifications,
|
||||
* irregular key sizes are handled by padding the key with zeroes at the end
|
||||
* until the key size is 16, 24, or 32 bytes, whichever
|
||||
* comes first. Note that each key of irregular size is equivalent to exactly
|
||||
* one key of 16, 24, or 32 bytes.
|
||||
*
|
||||
* WARNING: Short keys have low entropy, and result in low security.
|
||||
* Anything less than 8 bytes is utterly insecure. For good security
|
||||
* use at least 16 bytes. I prefer to use 32-byte keys to prevent
|
||||
* any collision attacks on the key.
|
||||
*
|
||||
* The key length argument key_len must be in the proper range.
|
||||
* If key_len is not in the range 0,...,32 this routine attempts to generate
|
||||
* a fatal error (depending on the code environment),
|
||||
* and at best (or worst) returns without having done anything.
|
||||
*
|
||||
* Arguments:
|
||||
* key Array of key bytes
|
||||
* key_len Number of key bytes, must be in the range 0,1,...,32.
|
||||
* xkey Pointer to an Twofish_key structure that will be filled
|
||||
* with the internal form of the cipher key.
|
||||
*/
|
||||
extern void Twofish_prepare_key(
|
||||
Twofish_Byte key[],
|
||||
int key_len,
|
||||
Twofish_key * xkey
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Encrypt a single block of data.
|
||||
*
|
||||
* This function encrypts a single block of 16 bytes of data.
|
||||
* If you want to encrypt a larger or variable-length message,
|
||||
* you will have to use a cipher mode, such as CBC or CTR.
|
||||
* These are outside the scope of this implementation.
|
||||
*
|
||||
* The xkey structure is not modified by this routine, and can be
|
||||
* used for further encryption and decryption operations.
|
||||
*
|
||||
* Arguments:
|
||||
* xkey pointer to Twofish_key, internal form of the key
|
||||
* produces by Twofish_prepare_key()
|
||||
* p Plaintext to be encrypted
|
||||
* c Place to store the ciphertext
|
||||
*/
|
||||
extern void Twofish_encrypt(
|
||||
Twofish_key * xkey,
|
||||
Twofish_Byte p[16],
|
||||
Twofish_Byte c[16]
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Decrypt a single block of data.
|
||||
*
|
||||
* This function decrypts a single block of 16 bytes of data.
|
||||
* If you want to decrypt a larger or variable-length message,
|
||||
* you will have to use a cipher mode, such as CBC or CTR.
|
||||
* These are outside the scope of this implementation.
|
||||
*
|
||||
* The xkey structure is not modified by this routine, and can be
|
||||
* used for further encryption and decryption operations.
|
||||
*
|
||||
* Arguments:
|
||||
* xkey pointer to Twofish_key, internal form of the key
|
||||
* produces by Twofish_prepare_key()
|
||||
* c Ciphertext to be decrypted
|
||||
* p Place to store the plaintext
|
||||
*/
|
||||
extern void Twofish_decrypt(
|
||||
Twofish_key * xkey,
|
||||
Twofish_Byte c[16],
|
||||
Twofish_Byte p[16]
|
||||
);
|
||||
@@ -1,562 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by Ronny Van Keer,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include "KeccakF-1600-interface.h"
|
||||
|
||||
#ifndef _Keccak1600_32_rvk_h_
|
||||
#define _Keccak1600_32_rvk_h_
|
||||
|
||||
static const UINT32 KeccakF1600RoundConstants_int2[2*24] =
|
||||
{
|
||||
0x00000001UL, 0x00000000UL,
|
||||
0x00000000UL, 0x00000089UL,
|
||||
0x00000000UL, 0x8000008bUL,
|
||||
0x00000000UL, 0x80008080UL,
|
||||
0x00000001UL, 0x0000008bUL,
|
||||
0x00000001UL, 0x00008000UL,
|
||||
0x00000001UL, 0x80008088UL,
|
||||
0x00000001UL, 0x80000082UL,
|
||||
0x00000000UL, 0x0000000bUL,
|
||||
0x00000000UL, 0x0000000aUL,
|
||||
0x00000001UL, 0x00008082UL,
|
||||
0x00000000UL, 0x00008003UL,
|
||||
0x00000001UL, 0x0000808bUL,
|
||||
0x00000001UL, 0x8000000bUL,
|
||||
0x00000001UL, 0x8000008aUL,
|
||||
0x00000001UL, 0x80000081UL,
|
||||
0x00000000UL, 0x80000081UL,
|
||||
0x00000000UL, 0x80000008UL,
|
||||
0x00000000UL, 0x00000083UL,
|
||||
0x00000000UL, 0x80008003UL,
|
||||
0x00000001UL, 0x80008088UL,
|
||||
0x00000000UL, 0x80000088UL,
|
||||
0x00000001UL, 0x00008000UL,
|
||||
0x00000000UL, 0x80008082UL
|
||||
};
|
||||
|
||||
#undef rounds
|
||||
|
||||
#define rounds \
|
||||
{ \
|
||||
UINT32 Da0, De0, Di0, Do0, Du0; \
|
||||
UINT32 Da1, De1, Di1, Do1, Du1; \
|
||||
UINT32 Ba, Be, Bi, Bo, Bu; \
|
||||
UINT32 Aba0, Abe0, Abi0, Abo0, Abu0; \
|
||||
UINT32 Aba1, Abe1, Abi1, Abo1, Abu1; \
|
||||
UINT32 Aga0, Age0, Agi0, Ago0, Agu0; \
|
||||
UINT32 Aga1, Age1, Agi1, Ago1, Agu1; \
|
||||
UINT32 Aka0, Ake0, Aki0, Ako0, Aku0; \
|
||||
UINT32 Aka1, Ake1, Aki1, Ako1, Aku1; \
|
||||
UINT32 Ama0, Ame0, Ami0, Amo0, Amu0; \
|
||||
UINT32 Ama1, Ame1, Ami1, Amo1, Amu1; \
|
||||
UINT32 Asa0, Ase0, Asi0, Aso0, Asu0; \
|
||||
UINT32 Asa1, Ase1, Asi1, Aso1, Asu1; \
|
||||
UINT32 Cw, Cx, Cy, Cz; \
|
||||
UINT32 Eba0, Ebe0, Ebi0, Ebo0, Ebu0; \
|
||||
UINT32 Eba1, Ebe1, Ebi1, Ebo1, Ebu1; \
|
||||
UINT32 Ega0, Ege0, Egi0, Ego0, Egu0; \
|
||||
UINT32 Ega1, Ege1, Egi1, Ego1, Egu1; \
|
||||
UINT32 Eka0, Eke0, Eki0, Eko0, Eku0; \
|
||||
UINT32 Eka1, Eke1, Eki1, Eko1, Eku1; \
|
||||
UINT32 Ema0, Eme0, Emi0, Emo0, Emu0; \
|
||||
UINT32 Ema1, Eme1, Emi1, Emo1, Emu1; \
|
||||
UINT32 Esa0, Ese0, Esi0, Eso0, Esu0; \
|
||||
UINT32 Esa1, Ese1, Esi1, Eso1, Esu1; \
|
||||
const UINT32 * pRoundConstants = KeccakF1600RoundConstants_int2; \
|
||||
UINT32 i; \
|
||||
\
|
||||
copyFromState(A, state) \
|
||||
\
|
||||
for( i = 12; i != 0; --i ) { \
|
||||
Cx = Abu0^Agu0^Aku0^Amu0^Asu0; \
|
||||
Du1 = Abe1^Age1^Ake1^Ame1^Ase1; \
|
||||
Da0 = Cx^ROL32(Du1, 1); \
|
||||
Cz = Abu1^Agu1^Aku1^Amu1^Asu1; \
|
||||
Du0 = Abe0^Age0^Ake0^Ame0^Ase0; \
|
||||
Da1 = Cz^Du0; \
|
||||
\
|
||||
Cw = Abi0^Agi0^Aki0^Ami0^Asi0; \
|
||||
Do0 = Cw^ROL32(Cz, 1); \
|
||||
Cy = Abi1^Agi1^Aki1^Ami1^Asi1; \
|
||||
Do1 = Cy^Cx; \
|
||||
\
|
||||
Cx = Aba0^Aga0^Aka0^Ama0^Asa0; \
|
||||
De0 = Cx^ROL32(Cy, 1); \
|
||||
Cz = Aba1^Aga1^Aka1^Ama1^Asa1; \
|
||||
De1 = Cz^Cw; \
|
||||
\
|
||||
Cy = Abo1^Ago1^Ako1^Amo1^Aso1; \
|
||||
Di0 = Du0^ROL32(Cy, 1); \
|
||||
Cw = Abo0^Ago0^Ako0^Amo0^Aso0; \
|
||||
Di1 = Du1^Cw; \
|
||||
\
|
||||
Du0 = Cw^ROL32(Cz, 1); \
|
||||
Du1 = Cy^Cx; \
|
||||
\
|
||||
Aba0 ^= Da0; \
|
||||
Ba = Aba0; \
|
||||
Age0 ^= De0; \
|
||||
Be = ROL32(Age0, 22); \
|
||||
Aki1 ^= Di1; \
|
||||
Bi = ROL32(Aki1, 22); \
|
||||
Amo1 ^= Do1; \
|
||||
Bo = ROL32(Amo1, 11); \
|
||||
Asu0 ^= Du0; \
|
||||
Bu = ROL32(Asu0, 7); \
|
||||
Eba0 = Ba ^((~Be)& Bi ) ^ *(pRoundConstants++); \
|
||||
Ebe0 = Be ^((~Bi)& Bo ); \
|
||||
Ebi0 = Bi ^((~Bo)& Bu ); \
|
||||
Ebo0 = Bo ^((~Bu)& Ba ); \
|
||||
Ebu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abo0 ^= Do0; \
|
||||
Ba = ROL32(Abo0, 14); \
|
||||
Agu0 ^= Du0; \
|
||||
Be = ROL32(Agu0, 10); \
|
||||
Aka1 ^= Da1; \
|
||||
Bi = ROL32(Aka1, 2); \
|
||||
Ame1 ^= De1; \
|
||||
Bo = ROL32(Ame1, 23); \
|
||||
Asi1 ^= Di1; \
|
||||
Bu = ROL32(Asi1, 31); \
|
||||
Ega0 = Ba ^((~Be)& Bi ); \
|
||||
Ege0 = Be ^((~Bi)& Bo ); \
|
||||
Egi0 = Bi ^((~Bo)& Bu ); \
|
||||
Ego0 = Bo ^((~Bu)& Ba ); \
|
||||
Egu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abe1 ^= De1; \
|
||||
Ba = ROL32(Abe1, 1); \
|
||||
Agi0 ^= Di0; \
|
||||
Be = ROL32(Agi0, 3); \
|
||||
Ako1 ^= Do1; \
|
||||
Bi = ROL32(Ako1, 13); \
|
||||
Amu0 ^= Du0; \
|
||||
Bo = ROL32(Amu0, 4); \
|
||||
Asa0 ^= Da0; \
|
||||
Bu = ROL32(Asa0, 9); \
|
||||
Eka0 = Ba ^((~Be)& Bi ); \
|
||||
Eke0 = Be ^((~Bi)& Bo ); \
|
||||
Eki0 = Bi ^((~Bo)& Bu ); \
|
||||
Eko0 = Bo ^((~Bu)& Ba ); \
|
||||
Eku0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abu1 ^= Du1; \
|
||||
Ba = ROL32(Abu1, 14); \
|
||||
Aga0 ^= Da0; \
|
||||
Be = ROL32(Aga0, 18); \
|
||||
Ake0 ^= De0; \
|
||||
Bi = ROL32(Ake0, 5); \
|
||||
Ami1 ^= Di1; \
|
||||
Bo = ROL32(Ami1, 8); \
|
||||
Aso0 ^= Do0; \
|
||||
Bu = ROL32(Aso0, 28); \
|
||||
Ema0 = Ba ^((~Be)& Bi ); \
|
||||
Eme0 = Be ^((~Bi)& Bo ); \
|
||||
Emi0 = Bi ^((~Bo)& Bu ); \
|
||||
Emo0 = Bo ^((~Bu)& Ba ); \
|
||||
Emu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abi0 ^= Di0; \
|
||||
Ba = ROL32(Abi0, 31); \
|
||||
Ago1 ^= Do1; \
|
||||
Be = ROL32(Ago1, 28); \
|
||||
Aku1 ^= Du1; \
|
||||
Bi = ROL32(Aku1, 20); \
|
||||
Ama1 ^= Da1; \
|
||||
Bo = ROL32(Ama1, 21); \
|
||||
Ase0 ^= De0; \
|
||||
Bu = ROL32(Ase0, 1); \
|
||||
Esa0 = Ba ^((~Be)& Bi ); \
|
||||
Ese0 = Be ^((~Bi)& Bo ); \
|
||||
Esi0 = Bi ^((~Bo)& Bu ); \
|
||||
Eso0 = Bo ^((~Bu)& Ba ); \
|
||||
Esu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Aba1 ^= Da1; \
|
||||
Ba = Aba1; \
|
||||
Age1 ^= De1; \
|
||||
Be = ROL32(Age1, 22); \
|
||||
Aki0 ^= Di0; \
|
||||
Bi = ROL32(Aki0, 21); \
|
||||
Amo0 ^= Do0; \
|
||||
Bo = ROL32(Amo0, 10); \
|
||||
Asu1 ^= Du1; \
|
||||
Bu = ROL32(Asu1, 7); \
|
||||
Eba1 = Ba ^((~Be)& Bi ); \
|
||||
Eba1 ^= *(pRoundConstants++); \
|
||||
Ebe1 = Be ^((~Bi)& Bo ); \
|
||||
Ebi1 = Bi ^((~Bo)& Bu ); \
|
||||
Ebo1 = Bo ^((~Bu)& Ba ); \
|
||||
Ebu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abo1 ^= Do1; \
|
||||
Ba = ROL32(Abo1, 14); \
|
||||
Agu1 ^= Du1; \
|
||||
Be = ROL32(Agu1, 10); \
|
||||
Aka0 ^= Da0; \
|
||||
Bi = ROL32(Aka0, 1); \
|
||||
Ame0 ^= De0; \
|
||||
Bo = ROL32(Ame0, 22); \
|
||||
Asi0 ^= Di0; \
|
||||
Bu = ROL32(Asi0, 30); \
|
||||
Ega1 = Ba ^((~Be)& Bi ); \
|
||||
Ege1 = Be ^((~Bi)& Bo ); \
|
||||
Egi1 = Bi ^((~Bo)& Bu ); \
|
||||
Ego1 = Bo ^((~Bu)& Ba ); \
|
||||
Egu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abe0 ^= De0; \
|
||||
Ba = Abe0; \
|
||||
Agi1 ^= Di1; \
|
||||
Be = ROL32(Agi1, 3); \
|
||||
Ako0 ^= Do0; \
|
||||
Bi = ROL32(Ako0, 12); \
|
||||
Amu1 ^= Du1; \
|
||||
Bo = ROL32(Amu1, 4); \
|
||||
Asa1 ^= Da1; \
|
||||
Bu = ROL32(Asa1, 9); \
|
||||
Eka1 = Ba ^((~Be)& Bi ); \
|
||||
Eke1 = Be ^((~Bi)& Bo ); \
|
||||
Eki1 = Bi ^((~Bo)& Bu ); \
|
||||
Eko1 = Bo ^((~Bu)& Ba ); \
|
||||
Eku1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abu0 ^= Du0; \
|
||||
Ba = ROL32(Abu0, 13); \
|
||||
Aga1 ^= Da1; \
|
||||
Be = ROL32(Aga1, 18); \
|
||||
Ake1 ^= De1; \
|
||||
Bi = ROL32(Ake1, 5); \
|
||||
Ami0 ^= Di0; \
|
||||
Bo = ROL32(Ami0, 7); \
|
||||
Aso1 ^= Do1; \
|
||||
Bu = ROL32(Aso1, 28); \
|
||||
Ema1 = Ba ^((~Be)& Bi ); \
|
||||
Eme1 = Be ^((~Bi)& Bo ); \
|
||||
Emi1 = Bi ^((~Bo)& Bu ); \
|
||||
Emo1 = Bo ^((~Bu)& Ba ); \
|
||||
Emu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Abi1 ^= Di1; \
|
||||
Ba = ROL32(Abi1, 31); \
|
||||
Ago0 ^= Do0; \
|
||||
Be = ROL32(Ago0, 27); \
|
||||
Aku0 ^= Du0; \
|
||||
Bi = ROL32(Aku0, 19); \
|
||||
Ama0 ^= Da0; \
|
||||
Bo = ROL32(Ama0, 20); \
|
||||
Ase1 ^= De1; \
|
||||
Bu = ROL32(Ase1, 1); \
|
||||
Esa1 = Ba ^((~Be)& Bi ); \
|
||||
Ese1 = Be ^((~Bi)& Bo ); \
|
||||
Esi1 = Bi ^((~Bo)& Bu ); \
|
||||
Eso1 = Bo ^((~Bu)& Ba ); \
|
||||
Esu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Cx = Ebu0^Egu0^Eku0^Emu0^Esu0; \
|
||||
Du1 = Ebe1^Ege1^Eke1^Eme1^Ese1; \
|
||||
Da0 = Cx^ROL32(Du1, 1); \
|
||||
Cz = Ebu1^Egu1^Eku1^Emu1^Esu1; \
|
||||
Du0 = Ebe0^Ege0^Eke0^Eme0^Ese0; \
|
||||
Da1 = Cz^Du0; \
|
||||
\
|
||||
Cw = Ebi0^Egi0^Eki0^Emi0^Esi0; \
|
||||
Do0 = Cw^ROL32(Cz, 1); \
|
||||
Cy = Ebi1^Egi1^Eki1^Emi1^Esi1; \
|
||||
Do1 = Cy^Cx; \
|
||||
\
|
||||
Cx = Eba0^Ega0^Eka0^Ema0^Esa0; \
|
||||
De0 = Cx^ROL32(Cy, 1); \
|
||||
Cz = Eba1^Ega1^Eka1^Ema1^Esa1; \
|
||||
De1 = Cz^Cw; \
|
||||
\
|
||||
Cy = Ebo1^Ego1^Eko1^Emo1^Eso1; \
|
||||
Di0 = Du0^ROL32(Cy, 1); \
|
||||
Cw = Ebo0^Ego0^Eko0^Emo0^Eso0; \
|
||||
Di1 = Du1^Cw; \
|
||||
\
|
||||
Du0 = Cw^ROL32(Cz, 1); \
|
||||
Du1 = Cy^Cx; \
|
||||
\
|
||||
Eba0 ^= Da0; \
|
||||
Ba = Eba0; \
|
||||
Ege0 ^= De0; \
|
||||
Be = ROL32(Ege0, 22); \
|
||||
Eki1 ^= Di1; \
|
||||
Bi = ROL32(Eki1, 22); \
|
||||
Emo1 ^= Do1; \
|
||||
Bo = ROL32(Emo1, 11); \
|
||||
Esu0 ^= Du0; \
|
||||
Bu = ROL32(Esu0, 7); \
|
||||
Aba0 = Ba ^((~Be)& Bi ); \
|
||||
Aba0 ^= *(pRoundConstants++); \
|
||||
Abe0 = Be ^((~Bi)& Bo ); \
|
||||
Abi0 = Bi ^((~Bo)& Bu ); \
|
||||
Abo0 = Bo ^((~Bu)& Ba ); \
|
||||
Abu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebo0 ^= Do0; \
|
||||
Ba = ROL32(Ebo0, 14); \
|
||||
Egu0 ^= Du0; \
|
||||
Be = ROL32(Egu0, 10); \
|
||||
Eka1 ^= Da1; \
|
||||
Bi = ROL32(Eka1, 2); \
|
||||
Eme1 ^= De1; \
|
||||
Bo = ROL32(Eme1, 23); \
|
||||
Esi1 ^= Di1; \
|
||||
Bu = ROL32(Esi1, 31); \
|
||||
Aga0 = Ba ^((~Be)& Bi ); \
|
||||
Age0 = Be ^((~Bi)& Bo ); \
|
||||
Agi0 = Bi ^((~Bo)& Bu ); \
|
||||
Ago0 = Bo ^((~Bu)& Ba ); \
|
||||
Agu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebe1 ^= De1; \
|
||||
Ba = ROL32(Ebe1, 1); \
|
||||
Egi0 ^= Di0; \
|
||||
Be = ROL32(Egi0, 3); \
|
||||
Eko1 ^= Do1; \
|
||||
Bi = ROL32(Eko1, 13); \
|
||||
Emu0 ^= Du0; \
|
||||
Bo = ROL32(Emu0, 4); \
|
||||
Esa0 ^= Da0; \
|
||||
Bu = ROL32(Esa0, 9); \
|
||||
Aka0 = Ba ^((~Be)& Bi ); \
|
||||
Ake0 = Be ^((~Bi)& Bo ); \
|
||||
Aki0 = Bi ^((~Bo)& Bu ); \
|
||||
Ako0 = Bo ^((~Bu)& Ba ); \
|
||||
Aku0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebu1 ^= Du1; \
|
||||
Ba = ROL32(Ebu1, 14); \
|
||||
Ega0 ^= Da0; \
|
||||
Be = ROL32(Ega0, 18); \
|
||||
Eke0 ^= De0; \
|
||||
Bi = ROL32(Eke0, 5); \
|
||||
Emi1 ^= Di1; \
|
||||
Bo = ROL32(Emi1, 8); \
|
||||
Eso0 ^= Do0; \
|
||||
Bu = ROL32(Eso0, 28); \
|
||||
Ama0 = Ba ^((~Be)& Bi ); \
|
||||
Ame0 = Be ^((~Bi)& Bo ); \
|
||||
Ami0 = Bi ^((~Bo)& Bu ); \
|
||||
Amo0 = Bo ^((~Bu)& Ba ); \
|
||||
Amu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebi0 ^= Di0; \
|
||||
Ba = ROL32(Ebi0, 31); \
|
||||
Ego1 ^= Do1; \
|
||||
Be = ROL32(Ego1, 28); \
|
||||
Eku1 ^= Du1; \
|
||||
Bi = ROL32(Eku1, 20); \
|
||||
Ema1 ^= Da1; \
|
||||
Bo = ROL32(Ema1, 21); \
|
||||
Ese0 ^= De0; \
|
||||
Bu = ROL32(Ese0, 1); \
|
||||
Asa0 = Ba ^((~Be)& Bi ); \
|
||||
Ase0 = Be ^((~Bi)& Bo ); \
|
||||
Asi0 = Bi ^((~Bo)& Bu ); \
|
||||
Aso0 = Bo ^((~Bu)& Ba ); \
|
||||
Asu0 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Eba1 ^= Da1; \
|
||||
Ba = Eba1; \
|
||||
Ege1 ^= De1; \
|
||||
Be = ROL32(Ege1, 22); \
|
||||
Eki0 ^= Di0; \
|
||||
Bi = ROL32(Eki0, 21); \
|
||||
Emo0 ^= Do0; \
|
||||
Bo = ROL32(Emo0, 10); \
|
||||
Esu1 ^= Du1; \
|
||||
Bu = ROL32(Esu1, 7); \
|
||||
Aba1 = Ba ^((~Be)& Bi ); \
|
||||
Aba1 ^= *(pRoundConstants++); \
|
||||
Abe1 = Be ^((~Bi)& Bo ); \
|
||||
Abi1 = Bi ^((~Bo)& Bu ); \
|
||||
Abo1 = Bo ^((~Bu)& Ba ); \
|
||||
Abu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebo1 ^= Do1; \
|
||||
Ba = ROL32(Ebo1, 14); \
|
||||
Egu1 ^= Du1; \
|
||||
Be = ROL32(Egu1, 10); \
|
||||
Eka0 ^= Da0; \
|
||||
Bi = ROL32(Eka0, 1); \
|
||||
Eme0 ^= De0; \
|
||||
Bo = ROL32(Eme0, 22); \
|
||||
Esi0 ^= Di0; \
|
||||
Bu = ROL32(Esi0, 30); \
|
||||
Aga1 = Ba ^((~Be)& Bi ); \
|
||||
Age1 = Be ^((~Bi)& Bo ); \
|
||||
Agi1 = Bi ^((~Bo)& Bu ); \
|
||||
Ago1 = Bo ^((~Bu)& Ba ); \
|
||||
Agu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebe0 ^= De0; \
|
||||
Ba = Ebe0; \
|
||||
Egi1 ^= Di1; \
|
||||
Be = ROL32(Egi1, 3); \
|
||||
Eko0 ^= Do0; \
|
||||
Bi = ROL32(Eko0, 12); \
|
||||
Emu1 ^= Du1; \
|
||||
Bo = ROL32(Emu1, 4); \
|
||||
Esa1 ^= Da1; \
|
||||
Bu = ROL32(Esa1, 9); \
|
||||
Aka1 = Ba ^((~Be)& Bi ); \
|
||||
Ake1 = Be ^((~Bi)& Bo ); \
|
||||
Aki1 = Bi ^((~Bo)& Bu ); \
|
||||
Ako1 = Bo ^((~Bu)& Ba ); \
|
||||
Aku1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebu0 ^= Du0; \
|
||||
Ba = ROL32(Ebu0, 13); \
|
||||
Ega1 ^= Da1; \
|
||||
Be = ROL32(Ega1, 18); \
|
||||
Eke1 ^= De1; \
|
||||
Bi = ROL32(Eke1, 5); \
|
||||
Emi0 ^= Di0; \
|
||||
Bo = ROL32(Emi0, 7); \
|
||||
Eso1 ^= Do1; \
|
||||
Bu = ROL32(Eso1, 28); \
|
||||
Ama1 = Ba ^((~Be)& Bi ); \
|
||||
Ame1 = Be ^((~Bi)& Bo ); \
|
||||
Ami1 = Bi ^((~Bo)& Bu ); \
|
||||
Amo1 = Bo ^((~Bu)& Ba ); \
|
||||
Amu1 = Bu ^((~Ba)& Be ); \
|
||||
\
|
||||
Ebi1 ^= Di1; \
|
||||
Ba = ROL32(Ebi1, 31); \
|
||||
Ego0 ^= Do0; \
|
||||
Be = ROL32(Ego0, 27); \
|
||||
Eku0 ^= Du0; \
|
||||
Bi = ROL32(Eku0, 19); \
|
||||
Ema0 ^= Da0; \
|
||||
Bo = ROL32(Ema0, 20); \
|
||||
Ese1 ^= De1; \
|
||||
Bu = ROL32(Ese1, 1); \
|
||||
Asa1 = Ba ^((~Be)& Bi ); \
|
||||
Ase1 = Be ^((~Bi)& Bo ); \
|
||||
Asi1 = Bi ^((~Bo)& Bu ); \
|
||||
Aso1 = Bo ^((~Bu)& Ba ); \
|
||||
Asu1 = Bu ^((~Ba)& Be ); \
|
||||
} \
|
||||
copyToState(state, A) \
|
||||
}
|
||||
|
||||
#define copyFromState(X, state) \
|
||||
X##ba0 = state[ 0]; \
|
||||
X##ba1 = state[ 1]; \
|
||||
X##be0 = state[ 2]; \
|
||||
X##be1 = state[ 3]; \
|
||||
X##bi0 = state[ 4]; \
|
||||
X##bi1 = state[ 5]; \
|
||||
X##bo0 = state[ 6]; \
|
||||
X##bo1 = state[ 7]; \
|
||||
X##bu0 = state[ 8]; \
|
||||
X##bu1 = state[ 9]; \
|
||||
X##ga0 = state[10]; \
|
||||
X##ga1 = state[11]; \
|
||||
X##ge0 = state[12]; \
|
||||
X##ge1 = state[13]; \
|
||||
X##gi0 = state[14]; \
|
||||
X##gi1 = state[15]; \
|
||||
X##go0 = state[16]; \
|
||||
X##go1 = state[17]; \
|
||||
X##gu0 = state[18]; \
|
||||
X##gu1 = state[19]; \
|
||||
X##ka0 = state[20]; \
|
||||
X##ka1 = state[21]; \
|
||||
X##ke0 = state[22]; \
|
||||
X##ke1 = state[23]; \
|
||||
X##ki0 = state[24]; \
|
||||
X##ki1 = state[25]; \
|
||||
X##ko0 = state[26]; \
|
||||
X##ko1 = state[27]; \
|
||||
X##ku0 = state[28]; \
|
||||
X##ku1 = state[29]; \
|
||||
X##ma0 = state[30]; \
|
||||
X##ma1 = state[31]; \
|
||||
X##me0 = state[32]; \
|
||||
X##me1 = state[33]; \
|
||||
X##mi0 = state[34]; \
|
||||
X##mi1 = state[35]; \
|
||||
X##mo0 = state[36]; \
|
||||
X##mo1 = state[37]; \
|
||||
X##mu0 = state[38]; \
|
||||
X##mu1 = state[39]; \
|
||||
X##sa0 = state[40]; \
|
||||
X##sa1 = state[41]; \
|
||||
X##se0 = state[42]; \
|
||||
X##se1 = state[43]; \
|
||||
X##si0 = state[44]; \
|
||||
X##si1 = state[45]; \
|
||||
X##so0 = state[46]; \
|
||||
X##so1 = state[47]; \
|
||||
X##su0 = state[48]; \
|
||||
X##su1 = state[49]; \
|
||||
|
||||
#define copyToState(state, X) \
|
||||
state[ 0] = X##ba0; \
|
||||
state[ 1] = X##ba1; \
|
||||
state[ 2] = X##be0; \
|
||||
state[ 3] = X##be1; \
|
||||
state[ 4] = X##bi0; \
|
||||
state[ 5] = X##bi1; \
|
||||
state[ 6] = X##bo0; \
|
||||
state[ 7] = X##bo1; \
|
||||
state[ 8] = X##bu0; \
|
||||
state[ 9] = X##bu1; \
|
||||
state[10] = X##ga0; \
|
||||
state[11] = X##ga1; \
|
||||
state[12] = X##ge0; \
|
||||
state[13] = X##ge1; \
|
||||
state[14] = X##gi0; \
|
||||
state[15] = X##gi1; \
|
||||
state[16] = X##go0; \
|
||||
state[17] = X##go1; \
|
||||
state[18] = X##gu0; \
|
||||
state[19] = X##gu1; \
|
||||
state[20] = X##ka0; \
|
||||
state[21] = X##ka1; \
|
||||
state[22] = X##ke0; \
|
||||
state[23] = X##ke1; \
|
||||
state[24] = X##ki0; \
|
||||
state[25] = X##ki1; \
|
||||
state[26] = X##ko0; \
|
||||
state[27] = X##ko1; \
|
||||
state[28] = X##ku0; \
|
||||
state[29] = X##ku1; \
|
||||
state[30] = X##ma0; \
|
||||
state[31] = X##ma1; \
|
||||
state[32] = X##me0; \
|
||||
state[33] = X##me1; \
|
||||
state[34] = X##mi0; \
|
||||
state[35] = X##mi1; \
|
||||
state[36] = X##mo0; \
|
||||
state[37] = X##mo1; \
|
||||
state[38] = X##mu0; \
|
||||
state[39] = X##mu1; \
|
||||
state[40] = X##sa0; \
|
||||
state[41] = X##sa1; \
|
||||
state[42] = X##se0; \
|
||||
state[43] = X##se1; \
|
||||
state[44] = X##si0; \
|
||||
state[45] = X##si1; \
|
||||
state[46] = X##so0; \
|
||||
state[47] = X##so1; \
|
||||
state[48] = X##su0; \
|
||||
state[49] = X##su1; \
|
||||
|
||||
|
||||
#endif
|
||||
-1191
File diff suppressed because it is too large.
Load diff
-1140
File diff suppressed because it is too large.
Load diff
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#ifdef UseSchedule
|
||||
#if (UseSchedule == 1)
|
||||
#include "KeccakF-1600-32-s1.h"
|
||||
#elif (UseSchedule == 2)
|
||||
#include "KeccakF-1600-32-s2.h"
|
||||
#elif (UseSchedule == 3)
|
||||
#include "KeccakF-1600-32-rvk.h"
|
||||
#else
|
||||
#error "This schedule is not supported."
|
||||
#endif
|
||||
#else
|
||||
#include "KeccakF-1600-32-s1.h"
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
#define ProvideFast576
|
||||
#define ProvideFast832
|
||||
#define ProvideFast1024
|
||||
#define ProvideFast1088
|
||||
#define ProvideFast1152
|
||||
#define ProvideFast1344
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#ifndef _KeccakPermutationInterface_h_
|
||||
#define _KeccakPermutationInterface_h_
|
||||
|
||||
typedef unsigned char UINT8;
|
||||
typedef unsigned short UINT16;
|
||||
typedef unsigned int UINT32;
|
||||
typedef uint64_t UINT64;
|
||||
|
||||
#include "KeccakF-1600-int-set.h"
|
||||
|
||||
void KeccakInitialize( void );
|
||||
void KeccakInitializeState(unsigned char *state);
|
||||
void KeccakPermutation(unsigned char *state);
|
||||
#ifdef ProvideFast576
|
||||
void KeccakAbsorb576bits(unsigned char *state, const unsigned char *data);
|
||||
#endif
|
||||
#ifdef ProvideFast832
|
||||
void KeccakAbsorb832bits(unsigned char *state, const unsigned char *data);
|
||||
#endif
|
||||
#ifdef ProvideFast1024
|
||||
void KeccakAbsorb1024bits(unsigned char *state, const unsigned char *data);
|
||||
#endif
|
||||
#ifdef ProvideFast1088
|
||||
void KeccakAbsorb1088bits(unsigned char *state, const unsigned char *data);
|
||||
#endif
|
||||
#ifdef ProvideFast1152
|
||||
void KeccakAbsorb1152bits(unsigned char *state, const unsigned char *data);
|
||||
#endif
|
||||
#ifdef ProvideFast1344
|
||||
void KeccakAbsorb1344bits(unsigned char *state, const unsigned char *data);
|
||||
#endif
|
||||
void KeccakAbsorb(unsigned char *state, const unsigned char *data, unsigned int laneCount);
|
||||
#ifdef ProvideFast1024
|
||||
void KeccakExtract1024bits(const unsigned char *state, unsigned char *data);
|
||||
#endif
|
||||
void KeccakExtract(const unsigned char *state, unsigned char *data, unsigned int laneCount);
|
||||
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
#define Unrolling 2
|
||||
#define UseSchedule 3
|
||||
/*
|
||||
#define UseBebigokimisa
|
||||
#define UseInterleaveTables
|
||||
*/
|
||||
@@ -1,519 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "brg_endian.h"
|
||||
#include "KeccakF-1600-opt32-settings.h"
|
||||
#include "KeccakF-1600-interface.h"
|
||||
|
||||
#ifdef UseInterleaveTables
|
||||
static int interleaveTablesBuilt = 0;
|
||||
static UINT16 interleaveTable[65536];
|
||||
static UINT16 deinterleaveTable[65536];
|
||||
|
||||
static void buildInterleaveTables()
|
||||
{
|
||||
UINT32 i, j;
|
||||
UINT16 x;
|
||||
|
||||
if (!interleaveTablesBuilt) {
|
||||
for(i=0; i<65536; i++) {
|
||||
x = 0;
|
||||
for(j=0; j<16; j++) {
|
||||
if (i & (1 << j))
|
||||
x |= (1 << (j/2 + 8*(j%2)));
|
||||
}
|
||||
interleaveTable[i] = x;
|
||||
deinterleaveTable[x] = (UINT16)i;
|
||||
}
|
||||
interleaveTablesBuilt = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN)
|
||||
|
||||
#define xor2bytesIntoInterleavedWords(even, odd, source, j) \
|
||||
i##j = interleaveTable[((const UINT16*)source)[j]]; \
|
||||
((UINT8*)even)[j] ^= i##j & 0xFF; \
|
||||
((UINT8*)odd)[j] ^= i##j >> 8;
|
||||
|
||||
#define setInterleavedWordsInto2bytes(dest, even, odd, j) \
|
||||
d##j = deinterleaveTable[((even >> (j*8)) & 0xFF) ^ (((odd >> (j*8)) & 0xFF) << 8)]; \
|
||||
((UINT16*)dest)[j] = d##j;
|
||||
|
||||
#else /* (PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN) */
|
||||
|
||||
#define xor2bytesIntoInterleavedWords(even, odd, source, j) \
|
||||
i##j = interleaveTable[source[2*j] ^ ((UINT16)source[2*j+1] << 8)]; \
|
||||
*even ^= (i##j & 0xFF) << (j*8); \
|
||||
*odd ^= ((i##j >> 8) & 0xFF) << (j*8);
|
||||
|
||||
#define setInterleavedWordsInto2bytes(dest, even, odd, j) \
|
||||
d##j = deinterleaveTable[((even >> (j*8)) & 0xFF) ^ (((odd >> (j*8)) & 0xFF) << 8)]; \
|
||||
dest[2*j] = d##j & 0xFF; \
|
||||
dest[2*j+1] = d##j >> 8;
|
||||
|
||||
#endif /* Endianness */
|
||||
|
||||
static void xor8bytesIntoInterleavedWords(UINT32 *even, UINT32 *odd, const UINT8* source)
|
||||
{
|
||||
UINT16 i0, i1, i2, i3;
|
||||
|
||||
xor2bytesIntoInterleavedWords(even, odd, source, 0)
|
||||
xor2bytesIntoInterleavedWords(even, odd, source, 1)
|
||||
xor2bytesIntoInterleavedWords(even, odd, source, 2)
|
||||
xor2bytesIntoInterleavedWords(even, odd, source, 3)
|
||||
}
|
||||
|
||||
#define xorLanesIntoState(laneCount, state, input) \
|
||||
{ \
|
||||
int i; \
|
||||
for(i=0; i<(laneCount); i++) \
|
||||
xor8bytesIntoInterleavedWords(state+i*2, state+i*2+1, input+i*8); \
|
||||
}
|
||||
|
||||
static void setInterleavedWordsInto8bytes(UINT8* dest, UINT32 even, UINT32 odd)
|
||||
{
|
||||
UINT16 d0, d1, d2, d3;
|
||||
|
||||
setInterleavedWordsInto2bytes(dest, even, odd, 0)
|
||||
setInterleavedWordsInto2bytes(dest, even, odd, 1)
|
||||
setInterleavedWordsInto2bytes(dest, even, odd, 2)
|
||||
setInterleavedWordsInto2bytes(dest, even, odd, 3)
|
||||
}
|
||||
|
||||
#define extractLanes(laneCount, state, data) \
|
||||
{ \
|
||||
int i; \
|
||||
for(i=0; i<(laneCount); i++) \
|
||||
setInterleavedWordsInto8bytes(data+i*8, ((UINT32*)state)[i*2], ((UINT32*)state)[i*2+1]); \
|
||||
}
|
||||
|
||||
#else /* No interleaving tables */
|
||||
|
||||
#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN)
|
||||
|
||||
/* Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */
|
||||
#define xorInterleavedLE(rateInLanes, state, input) \
|
||||
{ \
|
||||
const UINT32 * pI = (const UINT32 *)input; \
|
||||
UINT32 * pS = state; \
|
||||
UINT32 t, x0, x1; \
|
||||
int i; \
|
||||
for (i = (rateInLanes)-1; i >= 0; --i) \
|
||||
{ \
|
||||
x0 = *(pI++); \
|
||||
t = (x0 ^ (x0 >> 1)) & 0x22222222UL; x0 = x0 ^ t ^ (t << 1); \
|
||||
t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0CUL; x0 = x0 ^ t ^ (t << 2); \
|
||||
t = (x0 ^ (x0 >> 4)) & 0x00F000F0UL; x0 = x0 ^ t ^ (t << 4); \
|
||||
t = (x0 ^ (x0 >> 8)) & 0x0000FF00UL; x0 = x0 ^ t ^ (t << 8); \
|
||||
x1 = *(pI++); \
|
||||
t = (x1 ^ (x1 >> 1)) & 0x22222222UL; x1 = x1 ^ t ^ (t << 1); \
|
||||
t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0CUL; x1 = x1 ^ t ^ (t << 2); \
|
||||
t = (x1 ^ (x1 >> 4)) & 0x00F000F0UL; x1 = x1 ^ t ^ (t << 4); \
|
||||
t = (x1 ^ (x1 >> 8)) & 0x0000FF00UL; x1 = x1 ^ t ^ (t << 8); \
|
||||
*(pS++) ^= (UINT16)x0 | (x1 << 16); \
|
||||
*(pS++) ^= (x0 >> 16) | (x1 & 0xFFFF0000); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define xorLanesIntoState(laneCount, state, input) \
|
||||
xorInterleavedLE(laneCount, state, input)
|
||||
|
||||
#else /* (PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN) */
|
||||
|
||||
/* Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */
|
||||
UINT64 toInterleaving(UINT64 x)
|
||||
{
|
||||
UINT64 t;
|
||||
|
||||
t = (x ^ (x >> 1)) & 0x2222222222222222ULL; x = x ^ t ^ (t << 1);
|
||||
t = (x ^ (x >> 2)) & 0x0C0C0C0C0C0C0C0CULL; x = x ^ t ^ (t << 2);
|
||||
t = (x ^ (x >> 4)) & 0x00F000F000F000F0ULL; x = x ^ t ^ (t << 4);
|
||||
t = (x ^ (x >> 8)) & 0x0000FF000000FF00ULL; x = x ^ t ^ (t << 8);
|
||||
t = (x ^ (x >> 16)) & 0x00000000FFFF0000ULL; x = x ^ t ^ (t << 16);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static void xor8bytesIntoInterleavedWords(UINT32* evenAndOdd, const UINT8* source)
|
||||
{
|
||||
/* This can be optimized */
|
||||
UINT64 sourceWord =
|
||||
(UINT64)source[0]
|
||||
^ (((UINT64)source[1]) << 8)
|
||||
^ (((UINT64)source[2]) << 16)
|
||||
^ (((UINT64)source[3]) << 24)
|
||||
^ (((UINT64)source[4]) << 32)
|
||||
^ (((UINT64)source[5]) << 40)
|
||||
^ (((UINT64)source[6]) << 48)
|
||||
^ (((UINT64)source[7]) << 56);
|
||||
UINT64 evenAndOddWord = toInterleaving(sourceWord);
|
||||
evenAndOdd[0] ^= (UINT32)evenAndOddWord;
|
||||
evenAndOdd[1] ^= (UINT32)(evenAndOddWord >> 32);
|
||||
}
|
||||
|
||||
#define xorLanesIntoState(laneCount, state, input) \
|
||||
{ \
|
||||
int i; \
|
||||
for(i=0; i<(laneCount); i++) \
|
||||
xor8bytesIntoInterleavedWords(state+i*2, input+i*8); \
|
||||
}
|
||||
|
||||
#endif /* Endianness */
|
||||
|
||||
/* Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */
|
||||
UINT64 fromInterleaving(UINT64 x)
|
||||
{
|
||||
UINT64 t;
|
||||
|
||||
t = (x ^ (x >> 16)) & 0x00000000FFFF0000ULL; x = x ^ t ^ (t << 16);
|
||||
t = (x ^ (x >> 8)) & 0x0000FF000000FF00ULL; x = x ^ t ^ (t << 8);
|
||||
t = (x ^ (x >> 4)) & 0x00F000F000F000F0ULL; x = x ^ t ^ (t << 4);
|
||||
t = (x ^ (x >> 2)) & 0x0C0C0C0C0C0C0C0CULL; x = x ^ t ^ (t << 2);
|
||||
t = (x ^ (x >> 1)) & 0x2222222222222222ULL; x = x ^ t ^ (t << 1);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static void setInterleavedWordsInto8bytes(UINT8* dest, UINT32* evenAndOdd)
|
||||
{
|
||||
#if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN)
|
||||
((UINT64*)dest)[0] = fromInterleaving(*(UINT64*)evenAndOdd);
|
||||
#else /* (PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN) */
|
||||
/* This can be optimized */
|
||||
UINT64 evenAndOddWord = (UINT64)evenAndOdd[0] ^ ((UINT64)evenAndOdd[1] << 32);
|
||||
UINT64 destWord = fromInterleaving(evenAndOddWord);
|
||||
dest[0] = destWord & 0xFF;
|
||||
dest[1] = (destWord >> 8) & 0xFF;
|
||||
dest[2] = (destWord >> 16) & 0xFF;
|
||||
dest[3] = (destWord >> 24) & 0xFF;
|
||||
dest[4] = (destWord >> 32) & 0xFF;
|
||||
dest[5] = (destWord >> 40) & 0xFF;
|
||||
dest[6] = (destWord >> 48) & 0xFF;
|
||||
dest[7] = (destWord >> 56) & 0xFF;
|
||||
#endif /* Endianness */
|
||||
}
|
||||
|
||||
#define extractLanes(laneCount, state, data) \
|
||||
{ \
|
||||
int i; \
|
||||
for(i=0; i<(laneCount); i++) \
|
||||
setInterleavedWordsInto8bytes(data+i*8, (UINT32*)state+i*2); \
|
||||
}
|
||||
|
||||
#endif /* With or without interleaving tables */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define ROL32(a, offset) _rotl(a, offset)
|
||||
#elif (defined (__arm__) && defined(__ARMCC_VERSION))
|
||||
#define ROL32(a, offset) __ror(a, 32-(offset))
|
||||
#else
|
||||
#define ROL32(a, offset) ((((UINT32)a) << (offset)) ^ (((UINT32)a) >> (32-(offset))))
|
||||
#endif
|
||||
|
||||
#include "KeccakF-1600-unrolling.h"
|
||||
#include "KeccakF-1600-32.h"
|
||||
|
||||
#if (UseSchedule == 3)
|
||||
|
||||
#ifdef UseBebigokimisa
|
||||
#error "No lane complementing with schedule 3."
|
||||
#endif
|
||||
|
||||
#if (Unrolling != 2)
|
||||
#error "Only unrolling 2 is supported by schedule 3."
|
||||
#endif
|
||||
|
||||
static void KeccakPermutationOnWords(UINT32 *state)
|
||||
{
|
||||
rounds
|
||||
}
|
||||
|
||||
static void KeccakPermutationOnWordsAfterXoring(UINT32 *state, const UINT8 *input, unsigned int laneCount)
|
||||
{
|
||||
xorLanesIntoState(laneCount, state, input)
|
||||
rounds
|
||||
}
|
||||
|
||||
#ifdef ProvideFast576
|
||||
static void KeccakPermutationOnWordsAfterXoring576bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
xorLanesIntoState(9, state, input)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast832
|
||||
static void KeccakPermutationOnWordsAfterXoring832bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
xorLanesIntoState(13, state, input)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1024
|
||||
static void KeccakPermutationOnWordsAfterXoring1024bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
xorLanesIntoState(16, state, input)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1088
|
||||
static void KeccakPermutationOnWordsAfterXoring1088bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
xorLanesIntoState(17, state, input)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1152
|
||||
static void KeccakPermutationOnWordsAfterXoring1152bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
xorLanesIntoState(18, state, input)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1344
|
||||
static void KeccakPermutationOnWordsAfterXoring1344bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
xorLanesIntoState(21, state, input)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* (Schedule != 3) */
|
||||
|
||||
static void KeccakPermutationOnWords(UINT32 *state)
|
||||
{
|
||||
declareABCDE
|
||||
#if (Unrolling != 24)
|
||||
unsigned int i;
|
||||
#endif
|
||||
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
|
||||
static void KeccakPermutationOnWordsAfterXoring(UINT32 *state, const UINT8 *input, unsigned int laneCount)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(laneCount, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
|
||||
#ifdef ProvideFast576
|
||||
static void KeccakPermutationOnWordsAfterXoring576bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(9, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast832
|
||||
static void KeccakPermutationOnWordsAfterXoring832bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(13, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1024
|
||||
static void KeccakPermutationOnWordsAfterXoring1024bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(16, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1088
|
||||
static void KeccakPermutationOnWordsAfterXoring1088bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(17, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1152
|
||||
static void KeccakPermutationOnWordsAfterXoring1152bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(18, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1344
|
||||
static void KeccakPermutationOnWordsAfterXoring1344bits(UINT32 *state, const UINT8 *input)
|
||||
{
|
||||
declareABCDE
|
||||
unsigned int i;
|
||||
|
||||
xorLanesIntoState(21, state, input)
|
||||
copyFromState(A, state)
|
||||
rounds
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
void KeccakInitialize()
|
||||
{
|
||||
#ifdef UseInterleaveTables
|
||||
buildInterleaveTables();
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeccakInitializeState(unsigned char *state)
|
||||
{
|
||||
memset(state, 0, 200);
|
||||
#ifdef UseBebigokimisa
|
||||
((UINT32*)state)[ 2] = ~(UINT32)0;
|
||||
((UINT32*)state)[ 3] = ~(UINT32)0;
|
||||
((UINT32*)state)[ 4] = ~(UINT32)0;
|
||||
((UINT32*)state)[ 5] = ~(UINT32)0;
|
||||
((UINT32*)state)[16] = ~(UINT32)0;
|
||||
((UINT32*)state)[17] = ~(UINT32)0;
|
||||
((UINT32*)state)[24] = ~(UINT32)0;
|
||||
((UINT32*)state)[25] = ~(UINT32)0;
|
||||
((UINT32*)state)[34] = ~(UINT32)0;
|
||||
((UINT32*)state)[35] = ~(UINT32)0;
|
||||
((UINT32*)state)[40] = ~(UINT32)0;
|
||||
((UINT32*)state)[41] = ~(UINT32)0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeccakPermutation(unsigned char *state)
|
||||
{
|
||||
/* We assume the state is always stored as interleaved 32-bit words */
|
||||
KeccakPermutationOnWords((UINT32*)state);
|
||||
}
|
||||
|
||||
#ifdef ProvideFast576
|
||||
void KeccakAbsorb576bits(unsigned char *state, const unsigned char *data)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring576bits((UINT32*)state, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast832
|
||||
void KeccakAbsorb832bits(unsigned char *state, const unsigned char *data)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring832bits((UINT32*)state, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1024
|
||||
void KeccakAbsorb1024bits(unsigned char *state, const unsigned char *data)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring1024bits((UINT32*)state, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1088
|
||||
void KeccakAbsorb1088bits(unsigned char *state, const unsigned char *data)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring1088bits((UINT32*)state, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1152
|
||||
void KeccakAbsorb1152bits(unsigned char *state, const unsigned char *data)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring1152bits((UINT32*)state, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ProvideFast1344
|
||||
void KeccakAbsorb1344bits(unsigned char *state, const unsigned char *data)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring1344bits((UINT32*)state, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
void KeccakAbsorb(unsigned char *state, const unsigned char *data, unsigned int laneCount)
|
||||
{
|
||||
KeccakPermutationOnWordsAfterXoring((UINT32*)state, data, laneCount);
|
||||
}
|
||||
|
||||
#ifdef ProvideFast1024
|
||||
void KeccakExtract1024bits(const unsigned char *state, unsigned char *data)
|
||||
{
|
||||
extractLanes(16, state, data)
|
||||
#ifdef UseBebigokimisa
|
||||
((UINT32*)data)[ 2] = ~((UINT32*)data)[ 2];
|
||||
((UINT32*)data)[ 3] = ~((UINT32*)data)[ 3];
|
||||
((UINT32*)data)[ 4] = ~((UINT32*)data)[ 4];
|
||||
((UINT32*)data)[ 5] = ~((UINT32*)data)[ 5];
|
||||
((UINT32*)data)[16] = ~((UINT32*)data)[16];
|
||||
((UINT32*)data)[17] = ~((UINT32*)data)[17];
|
||||
((UINT32*)data)[24] = ~((UINT32*)data)[24];
|
||||
((UINT32*)data)[25] = ~((UINT32*)data)[25];
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void KeccakExtract(const unsigned char *state, unsigned char *data, unsigned int laneCount)
|
||||
{
|
||||
extractLanes(laneCount, state, data)
|
||||
#ifdef UseBebigokimisa
|
||||
if (laneCount > 1) {
|
||||
((UINT32*)data)[ 2] = ~((UINT32*)data)[ 2];
|
||||
((UINT32*)data)[ 3] = ~((UINT32*)data)[ 3];
|
||||
if (laneCount > 2) {
|
||||
((UINT32*)data)[ 4] = ~((UINT32*)data)[ 4];
|
||||
((UINT32*)data)[ 5] = ~((UINT32*)data)[ 5];
|
||||
if (laneCount > 8) {
|
||||
((UINT32*)data)[16] = ~((UINT32*)data)[16];
|
||||
((UINT32*)data)[17] = ~((UINT32*)data)[17];
|
||||
if (laneCount > 12) {
|
||||
((UINT32*)data)[24] = ~((UINT32*)data)[24];
|
||||
((UINT32*)data)[25] = ~((UINT32*)data)[25];
|
||||
if (laneCount > 17) {
|
||||
((UINT32*)data)[34] = ~((UINT32*)data)[34];
|
||||
((UINT32*)data)[35] = ~((UINT32*)data)[35];
|
||||
if (laneCount > 20) {
|
||||
((UINT32*)data)[40] = ~((UINT32*)data)[40];
|
||||
((UINT32*)data)[41] = ~((UINT32*)data)[41];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
#ifndef _Keccak1600_unrolling_h_
|
||||
#define _Keccak1600_unrolling_h_
|
||||
|
||||
#if (Unrolling == 24)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
thetaRhoPiChiIotaPrepareTheta( 0, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 1, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 2, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 3, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 4, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 5, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 6, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 7, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 8, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta( 9, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(10, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(11, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(12, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(13, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(14, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(15, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(16, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(17, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(18, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(19, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(20, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(21, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(22, A, E) \
|
||||
thetaRhoPiChiIota(23, E, A) \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 12)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i+=12) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 1, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 2, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 3, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 4, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 5, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 6, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 7, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 8, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+ 9, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+10, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+11, E, A) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 8)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i+=8) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+1, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+2, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+3, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+4, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+5, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+6, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+7, E, A) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 6)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i+=6) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+1, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+2, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+3, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+4, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+5, E, A) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 4)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i+=4) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+1, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+2, A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+3, E, A) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 3)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i+=3) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+1, E, A) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+2, A, E) \
|
||||
copyStateVariables(A, E) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 2)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i+=2) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
thetaRhoPiChiIotaPrepareTheta(i+1, E, A) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#elif (Unrolling == 1)
|
||||
#define rounds \
|
||||
prepareTheta \
|
||||
for(i=0; i<24; i++) { \
|
||||
thetaRhoPiChiIotaPrepareTheta(i , A, E) \
|
||||
copyStateVariables(A, E) \
|
||||
} \
|
||||
copyToState(state, A)
|
||||
#else
|
||||
#error "Unrolling is not correctly specified!"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "KeccakNISTInterface.h"
|
||||
#include "KeccakF-1600-interface.h"
|
||||
|
||||
HashReturn Init(hashState *state, int hashbitlen)
|
||||
{
|
||||
switch(hashbitlen) {
|
||||
case 0: /* Default parameters, arbitrary length output */
|
||||
InitSponge((spongeState*)state, 1024, 576);
|
||||
break;
|
||||
case 224:
|
||||
InitSponge((spongeState*)state, 1152, 448);
|
||||
break;
|
||||
case 256:
|
||||
InitSponge((spongeState*)state, 1088, 512);
|
||||
break;
|
||||
case 384:
|
||||
InitSponge((spongeState*)state, 832, 768);
|
||||
break;
|
||||
case 512:
|
||||
InitSponge((spongeState*)state, 576, 1024);
|
||||
break;
|
||||
default:
|
||||
return BAD_HASHLEN;
|
||||
}
|
||||
state->fixedOutputLength = hashbitlen;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen)
|
||||
{
|
||||
if ((databitlen % 8) == 0)
|
||||
return Absorb((spongeState*)state, data, databitlen);
|
||||
else {
|
||||
HashReturn ret = Absorb((spongeState*)state, data, databitlen - (databitlen % 8));
|
||||
if (ret == SUCCESS) {
|
||||
unsigned char lastByte;
|
||||
/* Align the last partial byte to the least significant bits */
|
||||
lastByte = data[databitlen/8] >> (8 - (databitlen % 8));
|
||||
return Absorb((spongeState*)state, &lastByte, databitlen % 8);
|
||||
}
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
HashReturn Final(hashState *state, BitSequence *hashval)
|
||||
{
|
||||
return Squeeze(state, hashval, state->fixedOutputLength);
|
||||
}
|
||||
|
||||
/*
|
||||
static HashReturn Hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval)
|
||||
{
|
||||
hashState state;
|
||||
HashReturn result;
|
||||
|
||||
if ((hashbitlen != 224) && (hashbitlen != 256) && (hashbitlen != 384) && (hashbitlen != 512))
|
||||
return BAD_HASHLEN; * Only the four fixed output lengths available through this API *
|
||||
result = Init(&state, hashbitlen);
|
||||
if (result != SUCCESS)
|
||||
return result;
|
||||
result = Update(&state, data, databitlen);
|
||||
if (result != SUCCESS)
|
||||
return result;
|
||||
result = Final(&state, hashval);
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#ifndef _KeccakNISTInterface_h_
|
||||
#define _KeccakNISTInterface_h_
|
||||
|
||||
#include "KeccakSponge.h"
|
||||
|
||||
typedef unsigned char BitSequence;
|
||||
typedef unsigned long long DataLength;
|
||||
typedef enum { SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2 } HashReturn;
|
||||
|
||||
typedef spongeState hashState;
|
||||
|
||||
/**
|
||||
* Function to initialize the state of the Keccak[r, c] sponge function.
|
||||
* The rate r and capacity c values are determined from @a hashbitlen.
|
||||
* @param state Pointer to the state of the sponge function to be initialized.
|
||||
* @param hashbitlen The desired number of output bits,
|
||||
* or 0 for Keccak[] with default parameters
|
||||
* and arbitrarily-long output.
|
||||
* @pre The value of hashbitlen must be one of 0, 224, 256, 384 and 512.
|
||||
* @return SUCCESS if successful, BAD_HASHLEN if the value of hashbitlen is incorrect.
|
||||
*/
|
||||
HashReturn Init(hashState *state, int hashbitlen);
|
||||
/**
|
||||
* Function to give input data for the sponge function to absorb.
|
||||
* @param state Pointer to the state of the sponge function initialized by Init().
|
||||
* @param data Pointer to the input data.
|
||||
* When @a databitLen is not a multiple of 8, the last bits of data must be
|
||||
* in the most significant bits of the last byte.
|
||||
* @param databitLen The number of input bits provided in the input data.
|
||||
* @pre In the previous call to Absorb(), databitLen was a multiple of 8.
|
||||
* @return SUCCESS if successful, FAIL otherwise.
|
||||
*/
|
||||
HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen);
|
||||
/**
|
||||
* Function to squeeze output data from the sponge function.
|
||||
* If @a hashbitlen was not 0 in the call to Init(), the number of output bits is equal to @a hashbitlen.
|
||||
* If @a hashbitlen was 0 in the call to Init(), the output bits must be extracted using the Squeeze() function.
|
||||
* @param state Pointer to the state of the sponge function initialized by Init().
|
||||
* @param hashval Pointer to the buffer where to store the output data.
|
||||
* @return SUCCESS if successful, FAIL otherwise.
|
||||
*/
|
||||
HashReturn Final(hashState *state, BitSequence *hashval);
|
||||
/**
|
||||
* Function to compute a hash using the Keccak[r, c] sponge function.
|
||||
* The rate r and capacity c values are determined from @a hashbitlen.
|
||||
* @param hashbitlen The desired number of output bits.
|
||||
* @param data Pointer to the input data.
|
||||
* When @a databitLen is not a multiple of 8, the last bits of data must be
|
||||
* in the most significant bits of the last byte.
|
||||
* @param databitLen The number of input bits provided in the input data.
|
||||
* @param hashval Pointer to the buffer where to store the output data.
|
||||
* @pre The value of hashbitlen must be one of 224, 256, 384 and 512.
|
||||
* @return SUCCESS if successful, BAD_HASHLEN if the value of hashbitlen is incorrect.
|
||||
*/
|
||||
/*
|
||||
static HashReturn Hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval);
|
||||
*/
|
||||
|
||||
#endif
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "KeccakSponge.h"
|
||||
#include "KeccakF-1600-interface.h"
|
||||
#ifdef KeccakReference
|
||||
#include "displayIntermediateValues.h"
|
||||
#endif
|
||||
|
||||
int InitSponge(spongeState *state, unsigned int rate, unsigned int capacity)
|
||||
{
|
||||
if (rate+capacity != 1600)
|
||||
return 1;
|
||||
if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
|
||||
return 1;
|
||||
KeccakInitialize();
|
||||
state->rate = rate;
|
||||
state->capacity = capacity;
|
||||
state->fixedOutputLength = 0;
|
||||
KeccakInitializeState(state->state);
|
||||
memset(state->dataQueue, 0, KeccakMaximumRateInBytes);
|
||||
state->bitsInQueue = 0;
|
||||
state->squeezing = 0;
|
||||
state->bitsAvailableForSqueezing = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void AbsorbQueue(spongeState *state)
|
||||
{
|
||||
/* state->bitsInQueue is assumed to be equal to state->rate */
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", state->dataQueue, state->rate/8);
|
||||
#endif
|
||||
#ifdef ProvideFast576
|
||||
if (state->rate == 576)
|
||||
KeccakAbsorb576bits(state->state, state->dataQueue);
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast832
|
||||
if (state->rate == 832)
|
||||
KeccakAbsorb832bits(state->state, state->dataQueue);
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1024
|
||||
if (state->rate == 1024)
|
||||
KeccakAbsorb1024bits(state->state, state->dataQueue);
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1088
|
||||
if (state->rate == 1088)
|
||||
KeccakAbsorb1088bits(state->state, state->dataQueue);
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1152
|
||||
if (state->rate == 1152)
|
||||
KeccakAbsorb1152bits(state->state, state->dataQueue);
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1344
|
||||
if (state->rate == 1344)
|
||||
KeccakAbsorb1344bits(state->state, state->dataQueue);
|
||||
else
|
||||
#endif
|
||||
KeccakAbsorb(state->state, state->dataQueue, state->rate/64);
|
||||
state->bitsInQueue = 0;
|
||||
}
|
||||
|
||||
int Absorb(spongeState *state, const unsigned char *data, unsigned long long databitlen)
|
||||
{
|
||||
unsigned long long i, j, wholeBlocks;
|
||||
unsigned int partialBlock, partialByte;
|
||||
const unsigned char *curData;
|
||||
|
||||
if ((state->bitsInQueue % 8) != 0)
|
||||
return 1; /* Only the last call may contain a partial byte */
|
||||
if (state->squeezing)
|
||||
return 1; /* Too late for additional input */
|
||||
|
||||
i = 0;
|
||||
while(i < databitlen) {
|
||||
if ((state->bitsInQueue == 0) && (databitlen >= state->rate) && (i <= (databitlen-state->rate))) {
|
||||
wholeBlocks = (databitlen-i)/state->rate;
|
||||
curData = data+i/8;
|
||||
#ifdef ProvideFast576
|
||||
if (state->rate == 576) {
|
||||
for(j=0; j<wholeBlocks; j++, curData+=576/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb576bits(state->state, curData);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast832
|
||||
if (state->rate == 832) {
|
||||
for(j=0; j<wholeBlocks; j++, curData+=832/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb832bits(state->state, curData);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1024
|
||||
if (state->rate == 1024) {
|
||||
for(j=0; j<wholeBlocks; j++, curData+=1024/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb1024bits(state->state, curData);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1088
|
||||
if (state->rate == 1088) {
|
||||
for(j=0; j<wholeBlocks; j++, curData+=1088/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb1088bits(state->state, curData);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1152
|
||||
if (state->rate == 1152) {
|
||||
for(j=0; j<wholeBlocks; j++, curData+=1152/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb1152bits(state->state, curData);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ProvideFast1344
|
||||
if (state->rate == 1344) {
|
||||
for(j=0; j<wholeBlocks; j++, curData+=1344/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb1344bits(state->state, curData);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
for(j=0; j<wholeBlocks; j++, curData+=state->rate/8) {
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block to be absorbed", curData, state->rate/8);
|
||||
#endif
|
||||
KeccakAbsorb(state->state, curData, state->rate/64);
|
||||
}
|
||||
}
|
||||
i += wholeBlocks*state->rate;
|
||||
}
|
||||
else {
|
||||
partialBlock = (unsigned int)(databitlen - i);
|
||||
if (partialBlock+state->bitsInQueue > state->rate)
|
||||
partialBlock = state->rate-state->bitsInQueue;
|
||||
partialByte = partialBlock % 8;
|
||||
partialBlock -= partialByte;
|
||||
memcpy(state->dataQueue+state->bitsInQueue/8, data+i/8, partialBlock/8);
|
||||
state->bitsInQueue += partialBlock;
|
||||
i += partialBlock;
|
||||
if (state->bitsInQueue == state->rate)
|
||||
AbsorbQueue(state);
|
||||
if (partialByte > 0) {
|
||||
unsigned char mask = (1 << partialByte)-1;
|
||||
state->dataQueue[state->bitsInQueue/8] = data[i/8] & mask;
|
||||
state->bitsInQueue += partialByte;
|
||||
i += partialByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PadAndSwitchToSqueezingPhase(spongeState *state)
|
||||
{
|
||||
/* Note: the bits are numbered from 0=LSB to 7=MSB */
|
||||
if (state->bitsInQueue + 1 == state->rate) {
|
||||
state->dataQueue[state->bitsInQueue/8 ] |= 1 << (state->bitsInQueue % 8);
|
||||
AbsorbQueue(state);
|
||||
memset(state->dataQueue, 0, state->rate/8);
|
||||
}
|
||||
else {
|
||||
memset(state->dataQueue + (state->bitsInQueue+7)/8, 0, state->rate/8 - (state->bitsInQueue+7)/8);
|
||||
state->dataQueue[state->bitsInQueue/8 ] |= 1 << (state->bitsInQueue % 8);
|
||||
}
|
||||
state->dataQueue[(state->rate-1)/8] |= 1 << ((state->rate-1) % 8);
|
||||
AbsorbQueue(state);
|
||||
|
||||
#ifdef KeccakReference
|
||||
displayText(1, "--- Switching to squeezing phase ---");
|
||||
#endif
|
||||
#ifdef ProvideFast1024
|
||||
if (state->rate == 1024) {
|
||||
KeccakExtract1024bits(state->state, state->dataQueue);
|
||||
state->bitsAvailableForSqueezing = 1024;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
KeccakExtract(state->state, state->dataQueue, state->rate/64);
|
||||
state->bitsAvailableForSqueezing = state->rate;
|
||||
}
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block available for squeezing", state->dataQueue, state->bitsAvailableForSqueezing/8);
|
||||
#endif
|
||||
state->squeezing = 1;
|
||||
}
|
||||
|
||||
int Squeeze(spongeState *state, unsigned char *output, unsigned long long outputLength)
|
||||
{
|
||||
unsigned long long i;
|
||||
unsigned int partialBlock;
|
||||
|
||||
if (!state->squeezing)
|
||||
PadAndSwitchToSqueezingPhase(state);
|
||||
if ((outputLength % 8) != 0)
|
||||
return 1; /* Only multiple of 8 bits are allowed, truncation can be done at user level */
|
||||
|
||||
i = 0;
|
||||
while(i < outputLength) {
|
||||
if (state->bitsAvailableForSqueezing == 0) {
|
||||
KeccakPermutation(state->state);
|
||||
#ifdef ProvideFast1024
|
||||
if (state->rate == 1024) {
|
||||
KeccakExtract1024bits(state->state, state->dataQueue);
|
||||
state->bitsAvailableForSqueezing = 1024;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
KeccakExtract(state->state, state->dataQueue, state->rate/64);
|
||||
state->bitsAvailableForSqueezing = state->rate;
|
||||
}
|
||||
#ifdef KeccakReference
|
||||
displayBytes(1, "Block available for squeezing", state->dataQueue, state->bitsAvailableForSqueezing/8);
|
||||
#endif
|
||||
}
|
||||
partialBlock = state->bitsAvailableForSqueezing;
|
||||
if ((unsigned long long)partialBlock > outputLength - i)
|
||||
partialBlock = (unsigned int)(outputLength - i);
|
||||
memcpy(output+i/8, state->dataQueue+(state->rate-state->bitsAvailableForSqueezing)/8, partialBlock/8);
|
||||
state->bitsAvailableForSqueezing -= partialBlock;
|
||||
i += partialBlock;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
The Keccak sponge function, designed by Guido Bertoni, Joan Daemen,
|
||||
Michaël Peeters and Gilles Van Assche. For more information, feedback or
|
||||
questions, please refer to our website: http://keccak.noekeon.org/
|
||||
|
||||
Implementation by the designers,
|
||||
hereby denoted as "the implementer".
|
||||
|
||||
To the extent possible under law, the implementer has waived all copyright
|
||||
and related or neighboring rights to the source code in this file.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#ifndef _KeccakSponge_h_
|
||||
#define _KeccakSponge_h_
|
||||
|
||||
#define KeccakPermutationSize 1600
|
||||
#define KeccakPermutationSizeInBytes (KeccakPermutationSize/8)
|
||||
#define KeccakMaximumRate 1536
|
||||
#define KeccakMaximumRateInBytes (KeccakMaximumRate/8)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#undef ALIGN
|
||||
#define ALIGN __attribute__ ((aligned(32)))
|
||||
#elif defined(_MSC_VER)
|
||||
#define ALIGN __declspec(align(32))
|
||||
#else
|
||||
#define ALIGN
|
||||
#endif
|
||||
|
||||
ALIGN typedef struct spongeStateStruct {
|
||||
ALIGN unsigned char state[KeccakPermutationSizeInBytes];
|
||||
ALIGN unsigned char dataQueue[KeccakMaximumRateInBytes];
|
||||
unsigned int rate;
|
||||
unsigned int capacity;
|
||||
unsigned int bitsInQueue;
|
||||
unsigned int fixedOutputLength;
|
||||
int squeezing;
|
||||
unsigned int bitsAvailableForSqueezing;
|
||||
} spongeState;
|
||||
|
||||
/**
|
||||
* Function to initialize the state of the Keccak[r, c] sponge function.
|
||||
* The sponge function is set to the absorbing phase.
|
||||
* @param state Pointer to the state of the sponge function to be initialized.
|
||||
* @param rate The value of the rate r.
|
||||
* @param capacity The value of the capacity c.
|
||||
* @pre One must have r+c=1600 and the rate a multiple of 64 bits in this implementation.
|
||||
* @return Zero if successful, 1 otherwise.
|
||||
*/
|
||||
int InitSponge(spongeState *state, unsigned int rate, unsigned int capacity);
|
||||
/**
|
||||
* Function to give input data for the sponge function to absorb.
|
||||
* @param state Pointer to the state of the sponge function initialized by InitSponge().
|
||||
* @param data Pointer to the input data.
|
||||
* When @a databitLen is not a multiple of 8, the last bits of data must be
|
||||
* in the least significant bits of the last byte.
|
||||
* @param databitLen The number of input bits provided in the input data.
|
||||
* @pre In the previous call to Absorb(), databitLen was a multiple of 8.
|
||||
* @pre The sponge function must be in the absorbing phase,
|
||||
* i.e., Squeeze() must not have been called before.
|
||||
* @return Zero if successful, 1 otherwise.
|
||||
*/
|
||||
int Absorb(spongeState *state, const unsigned char *data, unsigned long long databitlen);
|
||||
/**
|
||||
* Function to squeeze output data from the sponge function.
|
||||
* If the sponge function was in the absorbing phase, this function
|
||||
* switches it to the squeezing phase.
|
||||
* @param state Pointer to the state of the sponge function initialized by InitSponge().
|
||||
* @param output Pointer to the buffer where to store the output data.
|
||||
* @param outputLength The number of output bits desired.
|
||||
* It must be a multiple of 8.
|
||||
* @return Zero if successful, 1 otherwise.
|
||||
*/
|
||||
int Squeeze(spongeState *state, unsigned char *output, unsigned long long outputLength);
|
||||
|
||||
#endif
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The redistribution and use of this software (with or without changes)
|
||||
is allowed without the payment of fees or royalties provided that:
|
||||
|
||||
1. source code distributions include the above copyright notice, this
|
||||
list of conditions and the following disclaimer;
|
||||
|
||||
2. binary distributions include the above copyright notice, this list
|
||||
of conditions and the following disclaimer in their documentation;
|
||||
|
||||
3. the name of the copyright holder is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 20/12/2007
|
||||
Changes for ARM 9/9/2010
|
||||
*/
|
||||
|
||||
/* modified by justin@basho.com to include Solaris endianness test */
|
||||
|
||||
#ifndef _BRG_ENDIAN_H
|
||||
#define _BRG_ENDIAN_H
|
||||
|
||||
#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */
|
||||
#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */
|
||||
|
||||
#if 0
|
||||
/* Include files where endian defines and byteswap functions may reside */
|
||||
#if defined( __sun )
|
||||
# include <sys/isa_defs.h>
|
||||
#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ ) || \
|
||||
defined (__DragonFly__)
|
||||
# include <sys/types.h>
|
||||
# include <sys/endian.h>
|
||||
#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \
|
||||
defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ )
|
||||
# include <machine/endian.h>
|
||||
#elif defined (__SVR4) && defined (__sun)
|
||||
# ifdef __sparc__
|
||||
# define BYTE_ORDER IS_BIG_ENDIAN
|
||||
# else
|
||||
# define BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
# endif
|
||||
#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ )
|
||||
# if !defined( __MINGW32__ ) && !defined( _AIX )
|
||||
# include <endian.h>
|
||||
# if !defined( __BEOS__ )
|
||||
# include <byteswap.h>
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Now attempt to set the define for platform byte order using any */
|
||||
/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which */
|
||||
/* seem to encompass most endian symbol definitions */
|
||||
|
||||
#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN )
|
||||
# if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
# elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
# endif
|
||||
#elif defined( BIG_ENDIAN )
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
#elif defined( LITTLE_ENDIAN )
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN )
|
||||
# if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
# elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
# endif
|
||||
#elif defined( _BIG_ENDIAN )
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
#elif defined( _LITTLE_ENDIAN )
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN )
|
||||
# if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
# elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
# endif
|
||||
#elif defined( __BIG_ENDIAN )
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
#elif defined( __LITTLE_ENDIAN )
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ )
|
||||
# if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
# elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
# endif
|
||||
#elif defined( __BIG_ENDIAN__ )
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
#elif defined( __LITTLE_ENDIAN__ )
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/* if the platform byte order could not be determined, then try to */
|
||||
/* set this define using common machine defines */
|
||||
#if !defined(PLATFORM_BYTE_ORDER)
|
||||
|
||||
#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \
|
||||
defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \
|
||||
defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \
|
||||
defined( vax ) || defined( vms ) || defined( VMS ) || \
|
||||
defined( __VMS ) || defined( _M_X64 )
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
|
||||
#elif defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \
|
||||
defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \
|
||||
defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \
|
||||
defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \
|
||||
defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \
|
||||
defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM ) || \
|
||||
defined( THINK_C ) || defined( __VMCMS__ ) || defined( _AIX )
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
|
||||
#elif defined(__arm__)
|
||||
# ifdef __BIG_ENDIAN
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
# else
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
# endif
|
||||
#elif 1 /* **** EDIT HERE IF NECESSARY **** */
|
||||
# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
|
||||
#elif 0 /* **** EDIT HERE IF NECESSARY **** */
|
||||
# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
|
||||
#else
|
||||
# error Please edit lines 132 or 134 in brg_endian.h to set the platform byte order
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Generated
-71
@@ -1,71 +0,0 @@
|
||||
NACrypto
|
||||
=========
|
||||
|
||||
You should be using [NAChloride](https://github.com/gabriel/NAChloride) (libsodium/NaCl) instead. This is for advanced crypto only.
|
||||
|
||||
The following use Apple's CommonCrypto framework:
|
||||
|
||||
* HMAC: *SHA1*, *SHA2*
|
||||
* Digest: *SHA2*
|
||||
* AES (256-CTR)
|
||||
|
||||
The following are implemented from included reference C libraries:
|
||||
|
||||
* HMAC: *SHA3/Keccak*
|
||||
* Digest: *SHA3/Keccak*
|
||||
* TwoFish (CTR)
|
||||
|
||||
# Podfile
|
||||
|
||||
```ruby
|
||||
pod "NACrypto"
|
||||
```
|
||||
|
||||
# HMAC (SHA1, SHA2, SHA3)
|
||||
|
||||
```objc
|
||||
NSData *mac1 = [NAHMAC HMACForKey:key data:data algorithm:NAHMACAlgorithmSHA2_512];
|
||||
NSData *mac2 = [NAHMAC HMACForKey:key data:data algorithm:NAHMACAlgorithmSHA3_512];
|
||||
```
|
||||
|
||||
# AES (256-CTR)
|
||||
|
||||
```objc
|
||||
// Nonce should be 16 bytes
|
||||
// Key should be 32 bytes
|
||||
NAAES *AES = [[NAAES alloc] initWithAlgorithm:NAAESAlgorithm256CTR];
|
||||
NSData *encrypted = [AES encrypt:message nonce:nonce key:key error:&error];
|
||||
```
|
||||
|
||||
# TwoFish (CTR)
|
||||
|
||||
```objc
|
||||
// Nonce should be 16 bytes
|
||||
// Key should be 32 bytes
|
||||
NATwoFish *twoFish = [[NATwoFish alloc] init];
|
||||
NSData *encrypted = [twoFish encrypt:message nonce:nonce key:key error:&error];
|
||||
```
|
||||
|
||||
# Digest (SHA2, SHA3)
|
||||
|
||||
```objc
|
||||
NSData *digest1 = [NADigest digestForData:data algorithm:NADigestAlgorithmSHA2_256];
|
||||
NSData *digest2 = [NADigest digestForData:data algorithm:NADigestAlgorithmSHA3_512];
|
||||
|
||||
// Directly use SHA3
|
||||
NSData *sha = [NASHA3 SHA3ForData:data digestBitLength:512];
|
||||
```
|
||||
|
||||
# Keychain Utils
|
||||
|
||||
```objc
|
||||
NSData *key = [NASecRandom randomData:32 error:&error];
|
||||
[NAKeychain addSymmetricKey:key applicationLabel:@"NACrypto" tag:nil label:nil];
|
||||
NSData *keyOut = [NAKeychain symmetricKeyWithApplicationLabel:@"NACrypto"];
|
||||
```
|
||||
|
||||
# NSData Utils
|
||||
```objc
|
||||
NSData *data = [@"deadbeef" na_dataFromHexString];
|
||||
[data na_hexString]; // @"deadbeef";
|
||||
```
|
||||
@@ -99,6 +99,7 @@ static NSString *kNSDateHelperFormatSQLDateWithTimeEN_shashSeparated = @"
|
||||
+ (NSString *) stringForDisplayFromDate:(NSDate *) date;
|
||||
+ (NSString *) stringForDisplayFromDate:(NSDate *) date prefixed:(BOOL) prefixed;
|
||||
+ (NSString *) stringForDisplayFromDate:(NSDate *) date prefixed:(BOOL) prefixed alwaysDisplayTime:(BOOL)displayTime;
|
||||
- (NSString *) monthSymbol;
|
||||
- (NSString *) string;
|
||||
- (NSString *) stringWithFormat:(NSString *) format;
|
||||
- (NSString *) stringWithFormat:(NSString *) format timeZone:(NSTimeZone*) timezone;
|
||||
|
||||
+6
-1
@@ -139,6 +139,11 @@ static NSDateFormatter *_displayFormatter = nil;
|
||||
return -[self timeIntervalSinceDate:anotherDate];
|
||||
}
|
||||
|
||||
- (NSString *) monthSymbol {
|
||||
|
||||
return [[[NSDate sharedDateFormatter] monthSymbols] objectAtIndex:([self month]-1)];
|
||||
}
|
||||
|
||||
-(NSDate *) toLocalTime
|
||||
{
|
||||
NSTimeZone *tz = [NSTimeZone defaultTimeZone];
|
||||
@@ -1190,4 +1195,4 @@ static NSDateFormatter *_displayFormatter = nil;
|
||||
}
|
||||
return @"";
|
||||
}
|
||||
@end
|
||||
@end
|
||||
+5591
-5111
File diff suppressed because it is too large.
Load diff
+5
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_DDDKeychainWrapper : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_DDDKeychainWrapper
|
||||
@end
|
||||
File renamed without changes.
+17
@@ -0,0 +1,17 @@
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#import "DDDKeychainWrapper.h"
|
||||
|
||||
FOUNDATION_EXPORT double DDDKeychainWrapperVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char DDDKeychainWrapperVersionString[];
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
framework module DDDKeychainWrapper {
|
||||
umbrella header "DDDKeychainWrapper-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
Loaded 100 of 452 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user