- Upgrade to 1.0

This commit is contained in:
giuseppenuc committed 2016-12-28 18:49:02 +01:00
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
View File
@@ -0,0 +1,47 @@
# DDDKeychainWrapper
[![CI Status](http://img.shields.io/travis/axldyb/DDDKeychainWrapper.svg?style=flat)](https://travis-ci.org/axldyb/DDDKeychainWrapper)
[![Version](https://img.shields.io/cocoapods/v/DDDKeychainWrapper.svg?style=flat)](http://cocoadocs.org/docsets/DDDKeychainWrapper)
[![License](https://img.shields.io/cocoapods/l/DDDKeychainWrapper.svg?style=flat)](http://cocoadocs.org/docsets/DDDKeychainWrapper)
[![Platform](https://img.shields.io/cocoapods/p/DDDKeychainWrapper.svg?style=flat)](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
View File
@@ -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.
-87
View File
@@ -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).
[![Build Status](https://travis-ci.org/dblock/ios-snapshot-test-case-expecta.png)](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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
}
@@ -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
@@ -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
-29
View File
@@ -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
View File
@@ -1,97 +0,0 @@
FBSnapshotTestCase
======================
[![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.svg)](https://travis-ci.org/facebook/ios-snapshot-test-case) [![Cocoa Pod Version](https://cocoapod-badges.herokuapp.com/v/FBSnapshotTestCase/badge.svg)](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`|
![](FBSnapshotTestCaseDemo/Scheme_FB_REFERENCE_IMAGE_DIR.png)
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
View File
@@ -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": [
+14 -32
View File
@@ -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
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+189
View File
@@ -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.
});
```
-25
View File
@@ -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
-72
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
-34
View File
@@ -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
-121
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
-15
View File
@@ -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
-24
View File
@@ -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
View File
@@ -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
View File
@@ -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
-17
View File
@@ -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
-37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
File diff suppressed because it is too large. Load diff
-180
View File
@@ -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
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-26
View File
@@ -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
*/
-519
View File
@@ -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
View File
@@ -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;
}
-77
View File
@@ -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
View File
@@ -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
-71
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_DDDKeychainWrapper : NSObject
@end
@implementation PodsDummy_DDDKeychainWrapper
@end
@@ -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[];
@@ -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