Add 'server/' from commit '800090542d91beae40bc81fc41b67ba61c47da77'
git-subtree-dir: server git-subtree-mainline:6a4054c15agit-subtree-split:800090542d
This commit is contained in:
149
server/kordophone/Bridge/MBIMAuthToken.m
Normal file
149
server/kordophone/Bridge/MBIMAuthToken.m
Normal file
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// MBIMAuthToken.m
|
||||
// MBIMAuthToken
|
||||
//
|
||||
// Created by James Magahern on 7/6/21.
|
||||
// Copyright © 2021 James Magahern. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MBIMAuthToken.h"
|
||||
#import <CommonCrypto/CommonHMAC.h>
|
||||
|
||||
#define HOUR 3600
|
||||
#define DAY (24*HOUR)
|
||||
|
||||
static const NSTimeInterval ExpirationTime = 15 * DAY;
|
||||
static const char *SecretKey = "709E7CD8-4983-4D5F-B7BF-8B1C6341D2DB";
|
||||
|
||||
static NSString *const kUsernamePayloadKey = @"user";
|
||||
static NSString *const kIssuerPayloadKey = @"iss";
|
||||
static NSString *const kExpirationDatePayloadKey = @"exp";
|
||||
|
||||
static NSString *const kIssuerPayloadValue = @"kordophone";
|
||||
|
||||
@interface MBIMAuthToken ()
|
||||
@property (nonatomic, copy) NSString *username;
|
||||
@property (nonatomic, copy) NSString *jwtToken;
|
||||
@property (nonatomic, copy) NSDate *expirationDate;
|
||||
|
||||
// JWT Payload data
|
||||
@property (nonatomic, copy) NSString *headerString;
|
||||
@property (nonatomic, copy) NSString *payloadString;
|
||||
@property (nonatomic, copy) NSData *signatureData;
|
||||
@end
|
||||
|
||||
@implementation MBIMAuthToken
|
||||
|
||||
- (instancetype)initWithUsername:(NSString *)username
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.username = username;
|
||||
self.expirationDate = [NSDate dateWithTimeIntervalSinceNow:ExpirationTime];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTokenString:(NSString *)tokenString
|
||||
{
|
||||
NSArray<NSString *> *components = [tokenString componentsSeparatedByString:@"."];
|
||||
if (components.count != 3) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *header = components[0];
|
||||
NSString *payload = components[1];
|
||||
NSString *signature = components[2];
|
||||
|
||||
NSData *payloadData = [[NSData alloc] initWithBase64EncodedString:payload options:0];
|
||||
NSDictionary *decodedPayload = [NSJSONSerialization JSONObjectWithData:payloadData options:0 error:nil];
|
||||
if (!decodedPayload) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![decodedPayload[kIssuerPayloadKey] isEqualToString:@"kordophone"]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_headerString = header;
|
||||
_payloadString = payload;
|
||||
_signatureData = [[NSData alloc] initWithBase64EncodedString:signature options:0];
|
||||
|
||||
_username = decodedPayload[kUsernamePayloadKey];
|
||||
|
||||
NSTimeInterval expirationDate = [decodedPayload[kExpirationDatePayloadKey] floatValue];
|
||||
_expirationDate = [NSDate dateWithTimeIntervalSince1970:expirationDate];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return (_username.hash ^ _expirationDate.hash);
|
||||
}
|
||||
|
||||
- (NSString *)jwtToken
|
||||
{
|
||||
if (!_jwtToken) {
|
||||
NSDictionary *header = @{
|
||||
@"alg" : @"HS256",
|
||||
@"typ" : @"jwt"
|
||||
};
|
||||
|
||||
NSData *headerData = [NSJSONSerialization dataWithJSONObject:header options:0 error:nil];
|
||||
NSString *headerStr = [headerData base64EncodedStringWithOptions:0];
|
||||
|
||||
NSInteger expirationDate = [_expirationDate timeIntervalSince1970];
|
||||
NSDictionary *payload = @{
|
||||
kUsernamePayloadKey : _username,
|
||||
kIssuerPayloadKey : kIssuerPayloadValue,
|
||||
kExpirationDatePayloadKey : [NSString stringWithFormat:@"%ld", expirationDate]
|
||||
};
|
||||
NSData *payloadData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
|
||||
NSString *payloadStr = [payloadData base64EncodedStringWithOptions:0];
|
||||
|
||||
NSString *jwtDataStr = [NSString stringWithFormat:@"%@.%@", headerStr, payloadStr];
|
||||
NSData *jwtData = [jwtDataStr dataUsingEncoding:NSASCIIStringEncoding];
|
||||
|
||||
unsigned char signature[CC_SHA256_DIGEST_LENGTH];
|
||||
CCHmac(kCCHmacAlgSHA256, SecretKey, sizeof(SecretKey), jwtData.bytes, jwtData.length, signature);
|
||||
|
||||
NSData *signatureData = [NSData dataWithBytes:signature length:CC_SHA256_DIGEST_LENGTH];
|
||||
NSString *signatureStr = [signatureData base64EncodedStringWithOptions:0];
|
||||
|
||||
_jwtToken = [NSString stringWithFormat:@"%@.%@", jwtDataStr, signatureStr];
|
||||
}
|
||||
|
||||
return _jwtToken;
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
// Verify expiration date
|
||||
BOOL expirationDateValid = [_expirationDate timeIntervalSinceNow] > 0;
|
||||
if (!expirationDateValid) {
|
||||
MBIMLogInfo(@"Auth token expired.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Verify signature
|
||||
NSString *verificationDataStr = [NSString stringWithFormat:@"%@.%@", _headerString, _payloadString];
|
||||
NSData *verificationData = [verificationDataStr dataUsingEncoding:NSASCIIStringEncoding];
|
||||
|
||||
unsigned char computedSignature[CC_SHA256_DIGEST_LENGTH];
|
||||
CCHmac(kCCHmacAlgSHA256, SecretKey, sizeof(SecretKey), verificationData.bytes, verificationData.length, computedSignature);
|
||||
|
||||
NSData *computedSignatureData = [NSData dataWithBytes:computedSignature length:CC_SHA256_DIGEST_LENGTH];
|
||||
if (![computedSignatureData isEqualToData:_signatureData]) {
|
||||
MBIMLogInfo(@"Auth token signature verification failed.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user