yourkarma/JWT

View on GitHub
Sources/JWT/Algorithms/ESFamily/JWTAlgorithmAsymmetricBase.m

Summary

Maintainability
Test Coverage
//
//  JWTAlgorithmAsymmetricBase.m
//  Base64
//
//  Created by Lobanov Dmitry on 12.03.2018.
//

#import "JWTAlgorithmAsymmetricBase.h"
#import "JWTBase64Coder.h"
#import "JWTCryptoSecurity.h"
#import "JWTCryptoKeyExtractor.h"
#import "JWTCryptoKey.h"
#import "JWTAlgorithmFactory.h"
#import <Availability.h>

#ifndef IPHONE_SDK_VERSION_GREATER_THAN_10
#define IPHONE_SDK_VERSION_GREATER_THAN_10 (__IPHONE_OS_VERSION_MAX_ALLOWED && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000)
#endif

#ifndef MAC_OS_SDK_VERSION_GREATER_THAN_10_12
#define MAC_OS_SDK_VERSION_GREATER_THAN_10_12 (__MAC_OS_X_VERSION_MAX_ALLOWED && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)
#endif

// Abilities
#ifndef JWT_CRYPTO_MODERN_API_IS_ALLOWED
#define JWT_CRYPTO_MODERN_API_IS_ALLOWED ((IPHONE_SDK_VERSION_GREATER_THAN_10) || (MAC_OS_SDK_VERSION_GREATER_THAN_10_12))
#endif

// #ifndef IPHONE_OS_VERSION_GREATER_THAN_10
// #define IPHONE_OS_VERSION_GREATER_THAN_10 (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000)
// #endif

// #ifndef MAC_OS_VERSION_GREATER_THAN_10
// #define MAC_OS_VERSION_GREATER_THAN_10 (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
// #endif

// // Requirements
// #ifndef JWT_CRYPTO_MODERN_API_IS_REQUIRED
// #define JWT_CRYPTO_MODERN_API_IS_REQUIRED ((IPHONE_OS_VERSION_GREATER_THAN_10) || (MAC_OS_VERSION_GREATER_THAN_10))
// #endif

// TODO:
// Rewrite errors presentation.
NSString *const JWTAlgorithmAsymmetricFamilyErrorDomain = @"io.jwt.jwa.asymmetric";

@implementation JWTAlgorithmAsymmetricFamilyErrorDescription
// Subclass
+ (NSString *)errorDomain {
    return JWTAlgorithmAsymmetricFamilyErrorDomain;
}
+ (NSInteger)defaultErrorCode {
    return JWTAlgorithmAsymmetricFamilyErrorUnexpected;
}
+ (NSInteger)externalErrorCode {
    return JWTAlgorithmAsymmetricFamilyErrorInternalSecurityAPI;
}
+ (NSDictionary *)codesAndUserDescriptions {
    static NSDictionary *dictionary = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dictionary = @{
                       @(JWTAlgorithmAsymmetricFamilyErrorAlgorithmIsNotSupported) : @"Algorithm is not supported!",
                       @(JWTAlgorithmAsymmetricFamilyErrorInternalSecurityAPI) : @"algorithm internal security framework error!",
                       @(JWTAlgorithmAsymmetricFamilyErrorUnexpected) : @"Asymmetric algorithm unexpected error!"
                       };
    });
    return dictionary;
}
+ (NSDictionary *)codesAndDescriptions {
    static NSDictionary *dictionary = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dictionary = @{
                       @(JWTAlgorithmAsymmetricFamilyErrorAlgorithmIsNotSupported) : @"JWTAlgorithmAsymmetricFamilyErrorAlgorithmIsNotSupported",
                       @(JWTAlgorithmAsymmetricFamilyErrorInternalSecurityAPI) : @"JWTAlgorithmAsymmetricFamilyErrorInternalSecurityAPI",
                       @(JWTAlgorithmAsymmetricFamilyErrorUnexpected) : @"JWTAlgorithmAsymmetricFamilyErrorUnexpected"
                       };
    });
    return dictionary;
}
@end

typedef NS_ENUM(NSInteger, JWTAlgorithmAsymmetricBase__AlgorithmType) {
    JWTAlgorithmAsymmetricBase__AlgorithmType__RS,
    JWTAlgorithmAsymmetricBase__AlgorithmType__ES,
    JWTAlgorithmAsymmetricBase__AlgorithmType__PS
};
typedef NS_ENUM(NSInteger, JWTAlgorithmAsymmetricBase__AlgorithmNumber) {
    JWTAlgorithmAsymmetricBase__AlgorithmNumber__256,
    JWTAlgorithmAsymmetricBase__AlgorithmNumber__384,
    JWTAlgorithmAsymmetricBase__AlgorithmNumber__512
};
@interface JWTAlgorithmAsymmetricBase ()
@property (copy, nonatomic, readwrite) NSNumber *algorithmType;
@property (copy, nonatomic, readwrite) NSNumber *algorithmNumber;
@property (strong, nonatomic, readonly) id <JWTCryptoKeyExtractorProtocol> keyExtractor;
@property (copy, nonatomic, readwrite) JWTAlgorithmAsymmetricBase *(^setAlgorithmType)(JWTAlgorithmAsymmetricBase__AlgorithmType);
@property (copy, nonatomic, readwrite) JWTAlgorithmAsymmetricBase *(^setAlgorithmNumber)(JWTAlgorithmAsymmetricBase__AlgorithmNumber);
@end

@implementation JWTAlgorithmAsymmetricBase
@synthesize keyExtractorType;
@synthesize signKey;
@synthesize verifyKey;
@synthesize privateKeyCertificatePassphrase;
- (void)setupFluent {
    __weak typeof(self) weakSelf = self;
    self.setAlgorithmType = ^(JWTAlgorithmAsymmetricBase__AlgorithmType type) {
        weakSelf.algorithmType = @(type);
        return weakSelf;
    };
    self.setAlgorithmNumber = ^(JWTAlgorithmAsymmetricBase__AlgorithmNumber number) {
        weakSelf.algorithmNumber = @(number);
        return weakSelf;
    };
}
- (instancetype)init {
    self = [super init];
    if (self) {
        [self setupFluent];
    }
    return self;
}

- (id<JWTCryptoKeyExtractorProtocol>)keyExtractor {
    return [JWTCryptoKeyExtractor createWithType:self.keyExtractorType];
}
@end

@interface JWTAlgorithmAsymmetricBase (SignAndVerify)
#pragma mark - Private ( Override-part depends on platform )
- (BOOL)verifyData:(NSData *)plainData signature:(NSData *)signature key:(SecKeyRef)publicKey error:(NSError *__autoreleasing*)error;
- (NSData *)signData:(NSData *)plainData key:(SecKeyRef)privateKey error:(NSError *__autoreleasing*)error;
@end

@implementation JWTAlgorithmAsymmetricBase (SignAndVerify)
#pragma mark - Private ( Override-part depends on platform )
- (BOOL)verifyData:(NSData *)plainData signature:(NSData *)signature key:(SecKeyRef)publicKey error:(NSError *__autoreleasing*)error {
    return NO;
}

- (NSData *)signData:(NSData *)plainData key:(SecKeyRef)privateKey error:(NSError *__autoreleasing*)error {
    return nil;
}
@end

@implementation JWTAlgorithmAsymmetricBase (JWTAsymmetricKeysAlgorithm)
@dynamic privateKeyCertificatePassphrase;
- (NSString *)name {
    return @"AssymetricBase";
}

- (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing *)error {
    return nil;
}

- (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing *)error {
    return NO;
}

#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
    // create new.
    id <JWTRSAlgorithm> algorithm = (id<JWTRSAlgorithm>)[JWTAlgorithmFactory algorithmByName:[self name]];
    algorithm.privateKeyCertificatePassphrase = self.privateKeyCertificatePassphrase;
    algorithm.keyExtractorType = self.keyExtractorType;
    algorithm.signKey = self.signKey;
    algorithm.verifyKey = self.verifyKey;
    return algorithm;
}
@end

@interface JWTAlgorithmAsymmetricBase__Prior10 : JWTAlgorithmAsymmetricBase @end
@implementation JWTAlgorithmAsymmetricBase__Prior10 @end

#import <Security/Security.h>
#if JWT_CRYPTO_MODERN_API_IS_ALLOWED
SecKeyAlgorithm chooseAlgorithm(NSNumber *type, NSNumber *number) {
    if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
        switch ((JWTAlgorithmAsymmetricBase__AlgorithmNumber)number.integerValue) {
            case JWTAlgorithmAsymmetricBase__AlgorithmNumber__256:
                switch ((JWTAlgorithmAsymmetricBase__AlgorithmType)type.integerValue) {
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__RS: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256;
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__ES: return kSecKeyAlgorithmECDSASignatureMessageX962SHA256;
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__PS: {
                        if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
                            return kSecKeyAlgorithmRSASignatureMessagePSSSHA256;
                        }
                        break;
                    }
                }
            case JWTAlgorithmAsymmetricBase__AlgorithmNumber__384:
                switch ((JWTAlgorithmAsymmetricBase__AlgorithmType)type.integerValue) {
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__RS: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384;
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__ES: return kSecKeyAlgorithmECDSASignatureMessageX962SHA384;
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__PS: {
                        if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
                            return kSecKeyAlgorithmRSASignatureMessagePSSSHA384;
                        }
                        break;
                    }
                }
            case JWTAlgorithmAsymmetricBase__AlgorithmNumber__512:
                switch ((JWTAlgorithmAsymmetricBase__AlgorithmType)type.integerValue) {
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__RS: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512;
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__ES: return kSecKeyAlgorithmECDSASignatureMessageX962SHA512;
                    case JWTAlgorithmAsymmetricBase__AlgorithmType__PS: {
                        if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
                            return kSecKeyAlgorithmRSASignatureMessagePSSSHA512;
                        }
                        break;
                    }
                }
        }
        return NULL;
    } else {
        return NULL;
    }
}

#ifdef NS_AVAILABLE
NS_AVAILABLE(10_12, 10_0)
#endif
@interface JWTAlgorithmAsymmetricBase__After10
: JWTAlgorithmAsymmetricBase
@property (assign, nonatomic, readonly) SecKeyAlgorithm algorithm;
@end
@implementation JWTAlgorithmAsymmetricBase__After10
- (SecKeyAlgorithm)chooseAlgorithmByType:(NSNumber *)type number:(NSNumber *)number {
    return chooseAlgorithm(type, number);
}

- (SecKeyAlgorithm)algorithm {
    return [self chooseAlgorithmByType:self.algorithmType number:self.algorithmNumber];
}
@end

@implementation JWTAlgorithmAsymmetricBase__After10 (SignAndVerify)
- (NSData *)signData:(NSData *)plainData key:(SecKeyRef)privateKey error:(NSError *__autoreleasing *)error {
    if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
        CFErrorRef theError = NULL;
        NSData *result = (NSData *)CFBridgingRelease(SecKeyCreateSignature(privateKey, self.algorithm, (__bridge CFDataRef)plainData, &theError));
        if (error && theError) {
            *error = (__bridge NSError *)(theError);
        }
        return result;
    } else {
        if (error) {
            *error = [JWTAlgorithmAsymmetricFamilyErrorDescription errorWithCode:JWTAlgorithmAsymmetricFamilyErrorAlgorithmIsNotSupported];
        }
        return nil;
    }
}
- (BOOL)verifyData:(NSData *)plainData signature:(NSData *)signature key:(SecKeyRef)publicKey error:(NSError *__autoreleasing *)error {
    if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
        CFErrorRef theError = NULL;
        BOOL result = SecKeyVerifySignature(publicKey, self.algorithm, (__bridge CFDataRef)plainData, (__bridge CFDataRef)signature, &theError);
        if (error && theError) {
            *error = (__bridge NSError *)(theError);
        }
        return result;
    } else {
        if (error) {
            *error = [JWTAlgorithmAsymmetricFamilyErrorDescription errorWithCode:JWTAlgorithmAsymmetricFamilyErrorAlgorithmIsNotSupported];
        }
        return NO;
    }
}
@end

@implementation JWTAlgorithmAsymmetricBase__After10 (JWTAsymmetricKeysAlgorithm)
#pragma mark - JWTAlgorithm
- (NSDictionary *)verifyKeyExtractorParameters {
    switch ((JWTAlgorithmAsymmetricBase__AlgorithmType)self.algorithmType.integerValue) {
        case JWTAlgorithmAsymmetricBase__AlgorithmType__RS: return @{[JWTCryptoKey parametersKeyBuilder] : [JWTCryptoKeyBuilder new].keyTypeRSA};
        case JWTAlgorithmAsymmetricBase__AlgorithmType__ES: return @{[JWTCryptoKey parametersKeyBuilder] : [JWTCryptoKeyBuilder new].keyTypeEC};
        default: return nil;
    }
}
- (NSDictionary *)signKeyExtractorParameters {
    NSMutableDictionary *mutableDictionary = [[self verifyKeyExtractorParameters] mutableCopy];
    mutableDictionary[[JWTCryptoKeyExtractor parametersKeyCertificatePassphrase]] = self.privateKeyCertificatePassphrase ?: [NSNull null];
    return [mutableDictionary copy];
}

- (BOOL)removeKeyItem:(id<JWTCryptoKeyProtocol>)item error:(NSError *__autoreleasing *)error {
    NSError *theError = nil;
    [JWTCryptoSecurity removeKeyByTag:item.tag error:&theError];
    if (error) {
        *error = theError;
    }
    return theError == nil;
}

- (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing *)error {    
    id<JWTCryptoKeyExtractorProtocol> theExtractor = self.keyExtractor ?: [JWTCryptoKeyExtractor privateKeyInP12];
    NSError *extractKeyError = nil;
    id <JWTCryptoKeyProtocol> keyItem = self.signKey ?: [theExtractor keyFromData:key parameters:[self signKeyExtractorParameters] error:&extractKeyError];
    
    if (extractKeyError || keyItem == nil) {
        // tell about error
        if (extractKeyError && error) {
            *error = extractKeyError;
        }
        NSError *removeError = nil;
        [self removeKeyItem:keyItem error:&removeError];
        return nil;
    }
    else {
        NSError *signError = nil;
        NSData *result = [self signData:hash key:keyItem.key error:&signError];
        if (signError && error) {
            *error = signError;
        }
        return result;
    }
}
- (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing *)error {
    id<JWTCryptoKeyExtractorProtocol> theExtractor = self.keyExtractor ?: [JWTCryptoKeyExtractor publicKeyWithCertificate];
    NSError *extractKeyError = nil;
    id<JWTCryptoKeyProtocol> keyItem = self.verifyKey ?: [theExtractor keyFromData:key parameters:[self verifyKeyExtractorParameters] error:&extractKeyError];
    if (extractKeyError || keyItem == nil) {
        if (extractKeyError && error) {
            *error = extractKeyError;
        }
        NSError *removeError = nil;
        [self removeKeyItem:keyItem error:&removeError];
        return NO;
    }
    else {
        NSError *verifyError = nil;
        BOOL verified = [self verifyData:hash signature:signature key:keyItem.key error:&verifyError];
        if (verifyError && error) {
            *error = verifyError;
        }
        NSError *removeError = nil;
        [self removeKeyItem:keyItem error:&removeError];
        return verified;
    }
}

@end
#endif

//// MacOS OR iOS is Base
//#if TARGET_OS_MAC && !TARGET_OS_IPHONE // check that mac version is ok.
//@interface JWTAlgorithmRSFamilyMember : JWTAlgorithmRSBaseMac @end
//#else // check that iphone version is ok.
//@interface JWTAlgorithmRSFamilyMember : JWTAlgorithmRSBaseIOS @end
//#endif
// (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0) ||
@interface JWTAlgorithmAsymmetricBase__FamilyMember :
#if JWT_CRYPTO_MODERN_API_IS_ALLOWED
JWTAlgorithmAsymmetricBase__After10
#else
JWTAlgorithmAsymmetricBase__Prior10
#endif
@end

@implementation JWTAlgorithmAsymmetricBase__FamilyMember
- (NSString *)stringForAlgorithmNumber:(JWTAlgorithmAsymmetricBase__AlgorithmNumber)number {
    switch (number) {
        case JWTAlgorithmAsymmetricBase__AlgorithmNumber__256: return @"256";
        case JWTAlgorithmAsymmetricBase__AlgorithmNumber__384: return @"384";
        case JWTAlgorithmAsymmetricBase__AlgorithmNumber__512: return @"512";
        default: return nil;
    }
}

- (NSString *)stringForAlgorithmType:(JWTAlgorithmAsymmetricBase__AlgorithmType)type {
    switch (type) {
        case JWTAlgorithmAsymmetricBase__AlgorithmType__RS: return @"RS";
        case JWTAlgorithmAsymmetricBase__AlgorithmType__ES: return @"ES";
        case JWTAlgorithmAsymmetricBase__AlgorithmType__PS: return @"PS";
        default: return nil;
    }
}

- (NSString *)name {
    return [[self stringForAlgorithmType:(JWTAlgorithmAsymmetricBase__AlgorithmType)self.algorithmType.integerValue] stringByAppendingString:[self stringForAlgorithmNumber:(JWTAlgorithmAsymmetricBase__AlgorithmNumber)self.algorithmNumber.integerValue]];
}
@end

@interface JWTAlgorithmAsymmetricBase__FamilyMember__RS : JWTAlgorithmAsymmetricBase__FamilyMember @end

@implementation JWTAlgorithmAsymmetricBase__FamilyMember__RS
- (NSNumber *)algorithmType {
    return @(JWTAlgorithmAsymmetricBase__AlgorithmType__RS);
}
@end

@interface JWTAlgorithmAsymmetricBase__FamilyMember__ES : JWTAlgorithmAsymmetricBase__FamilyMember @end

@implementation JWTAlgorithmAsymmetricBase__FamilyMember__ES
- (NSNumber *)algorithmType {
    return @(JWTAlgorithmAsymmetricBase__AlgorithmType__ES);
}
@end

@interface JWTAlgorithmAsymmetricBase__FamilyMember__PS : JWTAlgorithmAsymmetricBase__FamilyMember @end

@implementation JWTAlgorithmAsymmetricBase__FamilyMember__PS
- (NSNumber *)algorithmType {
    return @(JWTAlgorithmAsymmetricBase__AlgorithmType__PS);
}
@end

@implementation JWTAlgorithmAsymmetricBase (Create)
+ (instancetype)withRS {
    return [[JWTAlgorithmAsymmetricBase__FamilyMember__RS alloc] init];
}
+ (instancetype)withES {
    return [[JWTAlgorithmAsymmetricBase__FamilyMember__ES alloc] init];
}
+ (instancetype)withPS {
    return [[JWTAlgorithmAsymmetricBase__FamilyMember__PS alloc] init];
}
- (instancetype)with256 {
    return self.setAlgorithmNumber(JWTAlgorithmAsymmetricBase__AlgorithmNumber__256);
}
- (instancetype)with384 {
    return self.setAlgorithmNumber(JWTAlgorithmAsymmetricBase__AlgorithmNumber__384);
}
- (instancetype)with512 {
    return self.setAlgorithmNumber(JWTAlgorithmAsymmetricBase__AlgorithmNumber__512);
}
@end