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