// _____ _ // |_ _| |_ _ _ ___ ___ _ __ __ _ // | | | ' \| '_/ -_) -_) ' \/ _` |_ // |_| |_||_|_| \___\___|_|_|_\__,_(_) // // Threema iOS Client // Copyright (c) 2015-2020 Threema GmbH // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License, version 3, // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #import "RandomSeedViewController.h" #import "BundleUtil.h" #import "MotionEntropyCollector.h" #import "NaClCrypto.h" #import #import "NSString+Hex.h" #import "UIDefines.h" #import "RectUtil.h" #import "LicenseStore.h" #import "ThreemaFramework/ThreemaFramework-swift.h" #define NUM_POSITIONS_REQUIRED 200 #define NUM_LINES 16 #define NUM_BYTES 16 #define CHARACTER_SPACING 8.0 #define LINE_SPACING 6.0 @interface RandomSeedViewController () @property MotionEntropyCollector *motionEntropyCollector; @property BOOL doneCollecting; @property BOOL startedCollectiong; @property NSString *randomString; @property NSMutableAttributedString *attributedString; @property NSMutableArray *labelMatrix; @property CGFloat accessabilityLastProgress; @end @implementation RandomSeedViewController - (void)viewDidLoad { [super viewDidLoad]; [self setup]; } - (void)adaptToSmallScreen { [super adaptToSmallScreen]; CGFloat yOffset = -20.0; _titleLabel.frame = [RectUtil offsetRect:_titleLabel.frame byX:0.0 byY:yOffset]; yOffset -= 24.0; _actionLabel.frame = [RectUtil offsetRect:_actionLabel.frame byX:0.0 byY:yOffset]; _progressView.frame = [RectUtil offsetRect:_progressView.frame byX:0.0 byY:yOffset]; _randomDataView.frame = [RectUtil offsetRect:_randomDataView.frame byX:0.0 byY:yOffset]; _fingerView.frame = [RectUtil offsetRect:_fingerView.frame byX:0.0 byY:yOffset]; yOffset = 48.0; self.moreView.frame = [RectUtil offsetRect:self.moreView.frame byX:0.0 byY:yOffset]; } - (void)setup { _randomDataBackground.layer.cornerRadius = 5.0; _progressView.tintColor = [Colors mainThemeDark]; if ([LicenseStore requiresLicenseKey]) { _titleLabel.text = [BundleUtil localizedStringForKey:@"welcome_work"]; } else { _titleLabel.text = [BundleUtil localizedStringForKey:@"welcome"]; } _actionLabel.text = [BundleUtil localizedStringForKey:@"move_your_finger"]; self.moreView.mainView = self.mainContentView; self.moreView.moreButtonTitle = [BundleUtil localizedStringForKey:@"more_information"]; self.moreView.moreMessageText = [BundleUtil localizedStringForKey:@"more_information_random_seed"]; _motionEntropyCollector = [[MotionEntropyCollector alloc] init]; _randomDataView.delegate = self; _randomDataView.isAccessibilityElement = YES; [_randomDataView setAccessibilityHint:[BundleUtil localizedStringForKey:@"move_your_finger"]]; _randomDataView.accessibilityIdentifier = @"randomDataView"; [self setupRandomMatrix]; [self progressUpdate]; [_motionEntropyCollector start]; self.view.backgroundColor = [UIColor clearColor]; _accessabilityLastProgress = 0.0; _fingerView.image = [StyleKit finger]; } - (void)setupRandomMatrix { CGFloat size = _randomDataView.frame.size.width / NUM_BYTES; _labelMatrix = [NSMutableArray arrayWithCapacity:NUM_LINES*NUM_BYTES]; NSData *random = [[NaClCrypto sharedCrypto] randomBytes:(NUM_LINES*NUM_BYTES)]; NSString *randomString = [NSString stringWithHexData:random]; randomString = [randomString uppercaseString]; for (int n = 0; n < NUM_LINES; n++) { for (int i = 0; i < NUM_BYTES; i++) { CGRect rect = CGRectMake(i*size, n*size, size, size); UILabel *label = [self charLabelAt:rect]; label.text = [randomString substringWithRange:NSMakeRange((i+1)*(n+1), 1)]; label.isAccessibilityElement = NO; [_randomDataView addSubview:label]; [_labelMatrix addObject:label]; } } } - (UILabel *)charLabelAt:(CGRect)rect { UILabel *label = [[UILabel alloc] initWithFrame:rect]; label.font = [UIFont systemFontOfSize:12]; label.textColor = THREEMA_COLOR_LIGHT_GREY; label.textAlignment = NSTextAlignmentCenter; label.lineBreakMode = NSLineBreakByClipping; label.backgroundColor = [UIColor clearColor]; return label; } - (void)dealloc { [_motionEntropyCollector stop]; } - (void)didMoveFingerInView:(MoveFingerView *)view { if (_startedCollectiong == NO) { _startedCollectiong = YES; [UIView animateWithDuration:0.3 animations:^{ _fingerView.alpha = 0.0; } completion:^(BOOL finished) { _fingerView.hidden = YES; }]; } [self progressUpdate]; if (_randomDataView.numberOfPositionsRecorded >= NUM_POSITIONS_REQUIRED && !_doneCollecting) { /* done! */ _doneCollecting = YES; NSData *seed = [self getSeed]; if ([_delegate respondsToSelector: @selector(generatedRandomSeed:)]) { [_delegate generatedRandomSeed:seed]; } } } - (void)progressUpdate { CGFloat progress = (float)_randomDataView.numberOfPositionsRecorded / NUM_POSITIONS_REQUIRED; _progressView.progress = progress; if (progress >= 1.0) { for (UILabel *label in _labelMatrix) { label.textColor = [Colors mainThemeDark]; } dispatch_async(dispatch_get_main_queue(), ^{ UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, [BundleUtil localizedStringForKey:@"done"]); }); } else if (progress > 0) { int posIncrement = 1.0/progress + arc4random() % 5; for (int i=0; i < _labelMatrix.count; i += posIncrement) { // some randomness if the color should change int random = arc4random(); if (random % 10 > 8) { UILabel *label = _labelMatrix[i]; label.textColor = [Colors mainThemeDark]; } } if (progress - _accessabilityLastProgress >= 0.1) { dispatch_async(dispatch_get_main_queue(), ^{ UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, _progressView.accessibilityValue); }); _accessabilityLastProgress = progress; } } } - (NSData *)getSeed { NSData *motionSeed = [_motionEntropyCollector stop]; NSData *fingerSeed = _randomDataView.digest; /* mix the two seeds */ CC_SHA256_CTX ctx; CC_SHA256_Init(&ctx); if (motionSeed.length > 0) CC_SHA256_Update(&ctx, motionSeed.bytes, (CC_LONG)motionSeed.length); if (fingerSeed.length > 0) CC_SHA256_Update(&ctx, fingerSeed.bytes, (CC_LONG)fingerSeed.length); unsigned char digest[CC_SHA256_DIGEST_LENGTH]; CC_SHA256_Final(digest, &ctx); NSData *seed = [NSData dataWithBytes:digest length:sizeof(digest)]; return seed; } @end