pEpObjCAdapter/PEPPassphraseCache.m
author Dirk Zimmermann <dz@pep.security>
Mon, 29 Jun 2020 16:39:43 +0200
branchIOSAD-172
changeset 1531 be2d0d6fcf2f
parent 1530 9947a905a79d
child 1532 455eb191aa48
permissions -rw-r--r--
IOSAD-172 Rename private var
dz@1449
     1
//
dz@1449
     2
//  PEPPassphraseCache.m
dz@1449
     3
//  pEpObjCAdapter
dz@1449
     4
//
dz@1449
     5
//  Created by Dirk Zimmermann on 25.06.20.
dz@1449
     6
//  Copyright © 2020 p≡p. All rights reserved.
dz@1449
     7
//
dz@1449
     8
dz@1449
     9
#import "PEPPassphraseCache.h"
dz@1449
    10
dz@1469
    11
#import "PEPPassphraseCacheInternal.h"
dz@1469
    12
dz@1467
    13
#import "PEPPassphraseCacheEntry.h"
dz@1467
    14
dz@1460
    15
static NSUInteger s_maxNumberOfPassphrases = 20;
dz@1468
    16
static NSTimeInterval s_defaultTimeoutInSeconds = 10 * 60;
dz@1474
    17
static NSTimeInterval s_defaultCheckExpiryInterval = 60;
dz@1457
    18
dz@1454
    19
@interface PEPPassphraseCache ()
dz@1454
    20
dz@1478
    21
/// Timeout of passwords in seconds.
dz@1478
    22
@property (nonatomic) NSTimeInterval timeout;
dz@1478
    23
dz@1456
    24
@property (nonatomic) dispatch_queue_t queue;
dz@1531
    25
@property (nonatomic) NSMutableArray<PEPPassphraseCacheEntry *> *mutablePassphraseEntries;
dz@1476
    26
@property (nonatomic) dispatch_source_t timer;
dz@1456
    27
dz@1454
    28
@end
dz@1454
    29
dz@1449
    30
@implementation PEPPassphraseCache
dz@1449
    31
dz@1481
    32
static PEPPassphraseCache *s_sharedInstance;
dz@1481
    33
dz@1481
    34
+ (void)initialize
dz@1481
    35
{
dz@1481
    36
    static BOOL initialized = NO;
dz@1481
    37
    if (!initialized) {
dz@1481
    38
        initialized = YES;
dz@1481
    39
        s_sharedInstance = [[PEPPassphraseCache alloc] init];
dz@1481
    40
    }
dz@1481
    41
}
dz@1481
    42
dz@1482
    43
+ (instancetype)sharedInstance
dz@1482
    44
{
dz@1482
    45
    return s_sharedInstance;
dz@1482
    46
}
dz@1482
    47
dz@1449
    48
/// Internal constructor (for now).
dz@1474
    49
- (instancetype)initWithPassphraseTimeout:(NSTimeInterval)timeout
dz@1474
    50
                      checkExpiryInterval:(NSTimeInterval)checkExpiryInterval
dz@1449
    51
{
dz@1449
    52
    self = [super init];
dz@1449
    53
    if (self) {
dz@1449
    54
        _timeout = timeout;
dz@1456
    55
        _queue = dispatch_queue_create("PEPPassphraseCache Queue", DISPATCH_QUEUE_SERIAL);
dz@1531
    56
        _mutablePassphraseEntries = [NSMutableArray arrayWithCapacity:s_maxNumberOfPassphrases];
dz@1475
    57
dz@1475
    58
        // we have a strong reference to the timer, but the timer doesn't have one to us
dz@1475
    59
        typeof(self) __weak weakSelf = self;
dz@1476
    60
dz@1476
    61
        _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
dz@1476
    62
        dispatch_source_set_timer(_timer,
dz@1476
    63
                                  DISPATCH_TIME_NOW,
dz@1476
    64
                                  checkExpiryInterval * NSEC_PER_SEC,
dz@1476
    65
                                  checkExpiryInterval / 10 * NSEC_PER_SEC);
dz@1476
    66
        dispatch_source_set_event_handler(_timer, ^{
dz@1475
    67
            [weakSelf removeStaleEntries];
dz@1476
    68
        });
dz@1476
    69
        dispatch_resume(_timer);
dz@1449
    70
    }
dz@1449
    71
    return self;
dz@1449
    72
}
dz@1449
    73
dz@1449
    74
/// Public constructor with default values.
dz@1449
    75
- (instancetype)init
dz@1449
    76
{
dz@1474
    77
    return [self initWithPassphraseTimeout:s_defaultTimeoutInSeconds
dz@1474
    78
                       checkExpiryInterval:s_defaultCheckExpiryInterval];
dz@1449
    79
}
dz@1449
    80
dz@1449
    81
- (void)addPassphrase:(NSString *)passphrase
dz@1449
    82
{
dz@1467
    83
    PEPPassphraseCacheEntry *entry = [[PEPPassphraseCacheEntry alloc]
dz@1467
    84
                                      initWithPassphrase:passphrase];
dz@1459
    85
    dispatch_sync(self.queue, ^{
dz@1531
    86
        [self.mutablePassphraseEntries addObject:entry];
dz@1531
    87
        if (self.mutablePassphraseEntries.count > s_maxNumberOfPassphrases) {
dz@1531
    88
            [self.mutablePassphraseEntries removeObjectAtIndex:0];
dz@1459
    89
        }
dz@1459
    90
    });
dz@1449
    91
}
dz@1449
    92
dz@1449
    93
- (NSArray *)passphrases
dz@1449
    94
{
dz@1457
    95
    NSMutableArray *resultingPassphrases = [NSMutableArray
dz@1460
    96
                                            arrayWithCapacity:s_maxNumberOfPassphrases + 1];
dz@1457
    97
    [resultingPassphrases addObject:@""];
dz@1457
    98
    dispatch_sync(self.queue, ^{
dz@1531
    99
        for (PEPPassphraseCacheEntry *entry in self.mutablePassphraseEntries) {
dz@1467
   100
            [resultingPassphrases addObject:entry.passphrase];
dz@1457
   101
        }
dz@1457
   102
    });
dz@1457
   103
    return [NSArray arrayWithArray:resultingPassphrases];
dz@1449
   104
}
dz@1449
   105
dz@1468
   106
/// Remove password entries that have timed out.
dz@1477
   107
/// - Note: Assumes it gets called on `queue`.
dz@1468
   108
- (void)removeStaleEntries
dz@1468
   109
{
dz@1477
   110
    NSMutableArray *resultingPassphrases = [NSMutableArray
dz@1477
   111
                                            arrayWithCapacity:s_maxNumberOfPassphrases];
dz@1468
   112
dz@1531
   113
    for (PEPPassphraseCacheEntry *entry in self.mutablePassphraseEntries) {
dz@1530
   114
        if (![self isExpiredPassphraseEntry:entry]) {
dz@1477
   115
            [resultingPassphrases addObject:entry];
dz@1468
   116
        }
dz@1477
   117
    }
dz@1468
   118
dz@1531
   119
    [self.mutablePassphraseEntries removeAllObjects];
dz@1531
   120
    [self.mutablePassphraseEntries addObjectsFromArray:resultingPassphrases];
dz@1468
   121
}
dz@1468
   122
dz@1523
   123
- (void)resetTimeoutForPassphrase:(NSString *)passphrase
dz@1523
   124
{
dz@1525
   125
    if ([passphrase isEqualToString:@""]) {
dz@1525
   126
        // ignore the empty passphrase, it's always there
dz@1525
   127
        return;
dz@1525
   128
    }
dz@1525
   129
dz@1523
   130
    dispatch_sync(self.queue, ^{
dz@1531
   131
        for (PEPPassphraseCacheEntry *entry in self.mutablePassphraseEntries) {
dz@1523
   132
            if ([entry.passphrase isEqualToString:passphrase]) {
dz@1523
   133
                entry.dateAdded = [NSDate date];
dz@1523
   134
            }
dz@1523
   135
        }
dz@1523
   136
    });
dz@1523
   137
}
dz@1523
   138
dz@1529
   139
- (BOOL)isExpiredPassphraseEntry:(PEPPassphraseCacheEntry *)passphraseEntry
dz@1529
   140
{
dz@1529
   141
    NSDate *now = [NSDate date];
dz@1529
   142
    NSDate *minimum = [now dateByAddingTimeInterval:-self.timeout];
dz@1529
   143
    NSTimeInterval minimumTimeInterval = [minimum timeIntervalSinceReferenceDate];
dz@1529
   144
dz@1529
   145
    if ([passphraseEntry.dateAdded timeIntervalSinceReferenceDate] < minimumTimeInterval) {
dz@1529
   146
        return YES;
dz@1529
   147
    }
dz@1529
   148
dz@1529
   149
    return NO;
dz@1529
   150
}
dz@1529
   151
dz@1449
   152
@end