mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Merge branch 'mkirk/overflow-rebased'
This commit is contained in:
commit
c9fdefd195
|
@ -313,7 +313,7 @@ def process(filepath):
|
|||
|
||||
filename = os.path.basename(filepath)
|
||||
if filename.startswith('.'):
|
||||
return
|
||||
raise "shouldn't call process with dotfile"
|
||||
file_ext = os.path.splitext(filename)[1]
|
||||
if file_ext in ('.swift'):
|
||||
env_copy = os.environ.copy()
|
||||
|
@ -376,7 +376,7 @@ def should_ignore_path(path):
|
|||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def process_if_appropriate(filepath):
|
||||
filename = os.path.basename(filepath)
|
||||
if filename.startswith('.'):
|
||||
|
@ -388,7 +388,33 @@ def process_if_appropriate(filepath):
|
|||
return
|
||||
process(filepath)
|
||||
|
||||
|
||||
|
||||
def check_diff_for_keywords():
|
||||
keywords = ["OWSAssert\(", "OWSFail\(", "ows_add_overflow\(", "ows_sub_overflow\("]
|
||||
matching_expression = "|".join(keywords)
|
||||
command_line = 'git diff --staged | grep --color=always -C 3 -E "%s"' % matching_expression
|
||||
print(command_line)
|
||||
try:
|
||||
output = subprocess.check_output(command_line, shell=True)
|
||||
except subprocess.CalledProcessError, e:
|
||||
# > man grep
|
||||
# EXIT STATUS
|
||||
# The grep utility exits with one of the following values:
|
||||
# 0 One or more lines were selected.
|
||||
# 1 No lines were selected.
|
||||
# >1 An error occurred.
|
||||
if e.returncode == 1:
|
||||
# no keywords in diff output
|
||||
return
|
||||
else:
|
||||
# some other error - bad grep expression?
|
||||
raise e
|
||||
|
||||
if len(output) > 0:
|
||||
print("⚠️ keywords detected in diff:")
|
||||
print(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description='Precommit script.')
|
||||
|
@ -426,3 +452,5 @@ if __name__ == "__main__":
|
|||
|
||||
print 'git clang-format...'
|
||||
print commands.getoutput('git clang-format')
|
||||
|
||||
check_diff_for_keywords()
|
||||
|
|
|
@ -134,7 +134,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
NSData *_Nullable data =
|
||||
[NSKeyedArchiver archivedDataWithRootObject:interaction];
|
||||
OWSAssertDebug(data);
|
||||
interactionSizeTotal += data.length;
|
||||
ows_add_overflow(
|
||||
interactionSizeTotal, data.length, &interactionSizeTotal);
|
||||
}];
|
||||
[transaction enumerateKeysAndObjectsInCollection:[TSAttachment collection]
|
||||
usingBlock:^(NSString *key, id object, BOOL *stop) {
|
||||
|
@ -143,7 +144,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
NSData *_Nullable data =
|
||||
[NSKeyedArchiver archivedDataWithRootObject:attachment];
|
||||
OWSAssertDebug(data);
|
||||
attachmentSizeTotal += data.length;
|
||||
ows_add_overflow(
|
||||
attachmentSizeTotal, data.length, &attachmentSizeTotal);
|
||||
}];
|
||||
}];
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@interface NSArray (FunctionalUtil)
|
||||
|
@ -19,15 +19,6 @@
|
|||
/// Returns an array of all the results of passing items from this array through the given projection function.
|
||||
- (NSArray *)filter:(int (^)(id item))predicate;
|
||||
|
||||
/// Returns the sum of the doubles in this array of doubles.
|
||||
- (double)sumDouble;
|
||||
|
||||
/// Returns the sum of the unsigned integers in this array of unsigned integers.
|
||||
- (NSUInteger)sumNSUInteger;
|
||||
|
||||
/// Returns the sum of the integers in this array of integers.
|
||||
- (NSInteger)sumNSInteger;
|
||||
|
||||
- (NSDictionary *)keyedBy:(id (^)(id))keySelector;
|
||||
- (NSDictionary *)groupBy:(id (^)(id value))keySelector;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FunctionalUtil.h"
|
||||
|
@ -52,27 +52,7 @@
|
|||
}
|
||||
return r;
|
||||
}
|
||||
- (double)sumDouble {
|
||||
double s = 0.0;
|
||||
for (NSNumber *e in self) {
|
||||
s += [e doubleValue];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
- (NSUInteger)sumNSUInteger {
|
||||
NSUInteger s = 0;
|
||||
for (NSNumber *e in self) {
|
||||
s += [e unsignedIntegerValue];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
- (NSInteger)sumNSInteger {
|
||||
NSInteger s = 0;
|
||||
for (NSNumber *e in self) {
|
||||
s += [e integerValue];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSDictionary *)keyedBy:(id (^)(id value))keySelector {
|
||||
OWSAssertDebug(keySelector != nil);
|
||||
|
||||
|
|
|
@ -630,27 +630,30 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
unsigned long long databaseFileSize = 0;
|
||||
for (OWSBackupExportItem *item in self.unsavedDatabaseItems) {
|
||||
databaseFileSize += [OWSFileSystem fileSizeOfPath:item.encryptedItem.filePath].unsignedLongLongValue;
|
||||
unsigned long long fileSize =
|
||||
[OWSFileSystem fileSizeOfPath:item.encryptedItem.filePath].unsignedLongLongValue;
|
||||
ows_add_overflow(databaseFileSize, fileSize, &databaseFileSize);
|
||||
}
|
||||
OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.",
|
||||
@"database items",
|
||||
self.unsavedDatabaseItems.count,
|
||||
databaseFileSize);
|
||||
totalFileSize += databaseFileSize;
|
||||
totalFileCount += self.unsavedDatabaseItems.count;
|
||||
ows_add_overflow(totalFileSize, databaseFileSize, &totalFileSize);
|
||||
ows_add_overflow(totalFileCount, self.unsavedDatabaseItems.count, &totalFileCount);
|
||||
}
|
||||
{
|
||||
unsigned long long attachmentFileSize = 0;
|
||||
for (OWSAttachmentExport *attachmentExport in self.unsavedAttachmentExports) {
|
||||
attachmentFileSize +=
|
||||
unsigned long long fileSize =
|
||||
[OWSFileSystem fileSizeOfPath:attachmentExport.attachmentFilePath].unsignedLongLongValue;
|
||||
ows_add_overflow(attachmentFileSize, fileSize, &attachmentFileSize);
|
||||
}
|
||||
OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.",
|
||||
@"attachment items",
|
||||
self.unsavedAttachmentExports.count,
|
||||
attachmentFileSize);
|
||||
totalFileSize += attachmentFileSize;
|
||||
totalFileCount += self.unsavedAttachmentExports.count;
|
||||
ows_add_overflow(totalFileSize, attachmentFileSize, &totalFileSize);
|
||||
ows_add_overflow(totalFileCount, self.unsavedAttachmentExports.count, &totalFileSize);
|
||||
}
|
||||
OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", @"all items", totalFileCount, totalFileSize);
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ extern const NSUInteger kAES256_KeyByteLength;
|
|||
@interface Cryptography : NSObject
|
||||
|
||||
typedef NS_ENUM(NSInteger, TSMACType) {
|
||||
TSHMACSHA1Truncated10Bytes = 1,
|
||||
TSHMACSHA256Truncated10Bytes = 2,
|
||||
TSHMACSHA256AttachementType = 3
|
||||
};
|
||||
|
|
|
@ -26,6 +26,9 @@ static const NSUInteger kAESGCM256_IVLength = 12;
|
|||
// length of authentication tag for AES256-GCM
|
||||
static const NSUInteger kAESGCM256_TagLength = 16;
|
||||
|
||||
// length of key used for websocket envelope authentication
|
||||
static const NSUInteger kHMAC256_EnvelopeKeyLength = 20;
|
||||
|
||||
const NSUInteger kAES256_KeyByteLength = 32;
|
||||
|
||||
@implementation OWSAES256Key
|
||||
|
@ -230,42 +233,13 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
return [ourHmacData copy];
|
||||
}
|
||||
|
||||
+ (nullable NSData *)computeSHA1HMAC:(NSData *)data withHMACKey:(NSData *)HMACKey
|
||||
{
|
||||
if (data.length >= SIZE_MAX) {
|
||||
OWSFailDebug(@"data is too long.");
|
||||
return nil;
|
||||
}
|
||||
size_t dataLength = (size_t)data.length;
|
||||
if (HMACKey.length >= SIZE_MAX) {
|
||||
OWSFailDebug(@"HMAC key is too long.");
|
||||
return nil;
|
||||
}
|
||||
size_t hmacKeyLength = (size_t)HMACKey.length;
|
||||
|
||||
NSMutableData *_Nullable ourHmacData = [[NSMutableData alloc] initWithLength:CC_SHA1_DIGEST_LENGTH];
|
||||
if (!ourHmacData) {
|
||||
OWSFailDebug(@"could not allocate buffer.");
|
||||
return nil;
|
||||
}
|
||||
CCHmac(kCCHmacAlgSHA1, [HMACKey bytes], hmacKeyLength, [data bytes], dataLength, ourHmacData.mutableBytes);
|
||||
return [ourHmacData copy];
|
||||
}
|
||||
|
||||
+ (nullable NSData *)truncatedSHA1HMAC:(NSData *)dataToHMAC
|
||||
withHMACKey:(NSData *)HMACKey
|
||||
truncation:(NSUInteger)truncation
|
||||
{
|
||||
OWSAssertDebug(truncation <= CC_SHA1_DIGEST_LENGTH);
|
||||
|
||||
return [[Cryptography computeSHA1HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, truncation)];
|
||||
}
|
||||
|
||||
+ (nullable NSData *)truncatedSHA256HMAC:(NSData *)dataToHMAC
|
||||
withHMACKey:(NSData *)HMACKey
|
||||
truncation:(NSUInteger)truncation
|
||||
{
|
||||
OWSAssertDebug(truncation <= CC_SHA256_DIGEST_LENGTH);
|
||||
OWSAssert(truncation <= CC_SHA256_DIGEST_LENGTH);
|
||||
OWSAssert(dataToHMAC);
|
||||
OWSAssert(HMACKey);
|
||||
|
||||
return
|
||||
[[Cryptography computeSHA256HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, truncation)];
|
||||
|
@ -287,29 +261,60 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
matchingHMAC:(NSData *)hmac
|
||||
digest:(nullable NSData *)digest
|
||||
{
|
||||
if (dataToDecrypt.length >= (SIZE_MAX - kCCBlockSizeAES128)) {
|
||||
OWSFailDebug(@"data is too long.");
|
||||
OWSAssert(dataToDecrypt);
|
||||
OWSAssert(key);
|
||||
if (key.length != kCCKeySizeAES256) {
|
||||
OWSFailDebug(@"key had wrong size.");
|
||||
return nil;
|
||||
}
|
||||
OWSAssert(iv);
|
||||
if (iv.length != kCCBlockSizeAES128) {
|
||||
OWSFailDebug(@"iv had wrong size.");
|
||||
return nil;
|
||||
}
|
||||
OWSAssert(hmacKey);
|
||||
OWSAssert(hmac);
|
||||
|
||||
size_t bufferSize;
|
||||
BOOL didOverflow = __builtin_add_overflow(dataToDecrypt.length, kCCBlockSizeAES128, &bufferSize);
|
||||
if (didOverflow) {
|
||||
OWSFailDebug(@"bufferSize was too large.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Verify hmac of: version? || iv || encrypted data
|
||||
|
||||
NSUInteger dataToAuthLength = 0;
|
||||
if (__builtin_add_overflow(dataToDecrypt.length, iv.length, &dataToAuthLength)) {
|
||||
OWSFailDebug(@"dataToAuth was too large.");
|
||||
return nil;
|
||||
}
|
||||
if (version != nil && __builtin_add_overflow(dataToAuthLength, version.length, &dataToAuthLength)) {
|
||||
OWSFailDebug(@"dataToAuth was too large.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *dataToAuth = [NSMutableData data];
|
||||
if (version != nil) {
|
||||
[dataToAuth appendData:version];
|
||||
}
|
||||
|
||||
[dataToAuth appendData:iv];
|
||||
[dataToAuth appendData:dataToDecrypt];
|
||||
|
||||
NSData *_Nullable ourHmacData;
|
||||
|
||||
if (hmacType == TSHMACSHA1Truncated10Bytes) {
|
||||
ourHmacData = [Cryptography truncatedSHA1HMAC:dataToAuth withHMACKey:hmacKey truncation:10];
|
||||
} else if (hmacType == TSHMACSHA256Truncated10Bytes) {
|
||||
if (hmacType == TSHMACSHA256Truncated10Bytes) {
|
||||
// used to authenticate envelope from websocket
|
||||
OWSAssert(hmacKey.length == kHMAC256_EnvelopeKeyLength);
|
||||
ourHmacData = [Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:10];
|
||||
OWSAssert(ourHmacData.length == 10);
|
||||
} else if (hmacType == TSHMACSHA256AttachementType) {
|
||||
OWSAssert(hmacKey.length == HMAC256_KEY_LENGTH);
|
||||
ourHmacData =
|
||||
[Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:HMAC256_OUTPUT_LENGTH];
|
||||
OWSAssert(ourHmacData.length == HMAC256_OUTPUT_LENGTH);
|
||||
} else {
|
||||
OWSFail(@"unknown HMAC scheme: %ld", (long)hmacType);
|
||||
}
|
||||
|
||||
if (hmac == nil || ![ourHmacData ows_constantTimeIsEqualToData:hmac]) {
|
||||
|
@ -333,7 +338,6 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
}
|
||||
|
||||
// decrypt
|
||||
size_t bufferSize = [dataToDecrypt length] + kCCBlockSizeAES128;
|
||||
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize];
|
||||
if (!bufferData) {
|
||||
OWSLogError(@"Failed to allocate buffer.");
|
||||
|
@ -372,7 +376,9 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
size_t ivLength = 16;
|
||||
size_t macLength = 10;
|
||||
size_t nonCiphertextLength = versionLength + ivLength + macLength;
|
||||
size_t ciphertextLength = payload.length - nonCiphertextLength;
|
||||
|
||||
size_t ciphertextLength;
|
||||
ows_sub_overflow(payload.length, nonCiphertextLength, &ciphertextLength);
|
||||
|
||||
if (payload.length < nonCiphertextLength) {
|
||||
OWSFailDebug(@"Invalid payload");
|
||||
|
@ -389,12 +395,12 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
NSData *ivData = [payload subdataWithRange:NSMakeRange(cursor, ivLength)];
|
||||
cursor += ivLength;
|
||||
NSData *ciphertextData = [payload subdataWithRange:NSMakeRange(cursor, ciphertextLength)];
|
||||
cursor += ciphertextLength;
|
||||
ows_add_overflow(cursor, ciphertextLength, &cursor);
|
||||
NSData *macData = [payload subdataWithRange:NSMakeRange(cursor, macLength)];
|
||||
|
||||
NSData *signalingKey = [NSData dataFromBase64String:signalingKeyString];
|
||||
NSData *signalingKeyAESKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(0, 32)];
|
||||
NSData *signalingKeyHMACKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(32, 20)];
|
||||
NSData *signalingKeyHMACKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(32, kHMAC256_EnvelopeKeyLength)];
|
||||
return [Cryptography decryptCBCMode:ciphertextData
|
||||
key:signalingKeyAESKeyMaterial
|
||||
IV:ivData
|
||||
|
@ -434,11 +440,14 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
|
||||
// dataToDecrypt: IV || Ciphertext || truncated MAC(IV||Ciphertext)
|
||||
NSData *iv = [dataToDecrypt subdataWithRange:NSMakeRange(0, AES_CBC_IV_LENGTH)];
|
||||
NSData *encryptedAttachment = [dataToDecrypt
|
||||
subdataWithRange:NSMakeRange(AES_CBC_IV_LENGTH,
|
||||
[dataToDecrypt length] - AES_CBC_IV_LENGTH - HMAC256_OUTPUT_LENGTH)];
|
||||
NSData *hmac = [dataToDecrypt
|
||||
subdataWithRange:NSMakeRange([dataToDecrypt length] - HMAC256_OUTPUT_LENGTH, HMAC256_OUTPUT_LENGTH)];
|
||||
|
||||
NSUInteger cipherTextLength;
|
||||
ows_sub_overflow(dataToDecrypt.length, (AES_CBC_IV_LENGTH + HMAC256_OUTPUT_LENGTH), &cipherTextLength);
|
||||
NSData *encryptedAttachment = [dataToDecrypt subdataWithRange:NSMakeRange(AES_CBC_IV_LENGTH, cipherTextLength)];
|
||||
|
||||
NSUInteger hmacOffset;
|
||||
ows_sub_overflow(dataToDecrypt.length, HMAC256_OUTPUT_LENGTH, &hmacOffset);
|
||||
NSData *hmac = [dataToDecrypt subdataWithRange:NSMakeRange(hmacOffset, HMAC256_OUTPUT_LENGTH)];
|
||||
|
||||
NSData *_Nullable paddedPlainText = [Cryptography decryptCBCMode:encryptedAttachment
|
||||
key:encryptionKey
|
||||
|
@ -469,7 +478,9 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
OWSLogInfo(@"decrypted unpadded attachment.");
|
||||
return [paddedPlainText copy];
|
||||
} else {
|
||||
unsigned long paddingSize = paddedPlainText.length - unpaddedSize;
|
||||
unsigned long paddingSize;
|
||||
ows_sub_overflow(paddedPlainText.length, unpaddedSize, &paddingSize);
|
||||
|
||||
OWSLogInfo(@"decrypted padded attachment with unpaddedSize: %lu, paddingSize: %lu",
|
||||
(unsigned long)unpaddedSize,
|
||||
paddingSize);
|
||||
|
@ -517,7 +528,8 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
paddedAttachmentData.length = desiredSize;
|
||||
|
||||
// Encrypt
|
||||
size_t bufferSize = [paddedAttachmentData length] + kCCBlockSizeAES128;
|
||||
size_t bufferSize;
|
||||
ows_add_overflow(paddedAttachmentData.length, kCCBlockSizeAES128, &bufferSize);
|
||||
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize];
|
||||
if (!bufferData) {
|
||||
OWSLogError(@"Failed to allocate buffer.");
|
||||
|
@ -790,14 +802,22 @@ const NSUInteger kAES256_KeyByteLength = 32;
|
|||
|
||||
+ (nullable NSData *)decryptAESGCMWithProfileData:(NSData *)encryptedData key:(OWSAES256Key *)key
|
||||
{
|
||||
OWSAssertDebug(encryptedData.length > kAESGCM256_IVLength + kAESGCM256_TagLength);
|
||||
NSUInteger cipherTextLength = encryptedData.length - kAESGCM256_IVLength - kAESGCM256_TagLength;
|
||||
NSUInteger cipherTextLength;
|
||||
BOOL didOverflow
|
||||
= __builtin_sub_overflow(encryptedData.length, (kAESGCM256_IVLength + kAESGCM256_TagLength), &cipherTextLength);
|
||||
if (didOverflow) {
|
||||
OWSFailDebug(@"unexpectedly short encryptedData.length: %lu", (unsigned long)encryptedData.length);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// encryptedData layout: initializationVector || ciphertext || authTag
|
||||
NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM256_IVLength)];
|
||||
NSData *ciphertext = [encryptedData subdataWithRange:NSMakeRange(kAESGCM256_IVLength, cipherTextLength)];
|
||||
NSData *authTag =
|
||||
[encryptedData subdataWithRange:NSMakeRange(kAESGCM256_IVLength + cipherTextLength, kAESGCM256_TagLength)];
|
||||
|
||||
NSUInteger tagOffset;
|
||||
ows_add_overflow(kAESGCM256_IVLength, cipherTextLength, &tagOffset);
|
||||
|
||||
NSData *authTag = [encryptedData subdataWithRange:NSMakeRange(tagOffset, kAESGCM256_TagLength)];
|
||||
|
||||
return [self decryptAESGCMWithInitializationVector:initializationVector
|
||||
ciphertext:ciphertext
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@interface NSArray (FunctionalUtil)
|
||||
|
@ -19,15 +19,6 @@
|
|||
/// Returns an array of all the results of passing items from this array through the given projection function.
|
||||
- (NSArray *)filter:(int (^)(id item))predicate;
|
||||
|
||||
/// Returns the sum of the doubles in this array of doubles.
|
||||
- (double)sumDouble;
|
||||
|
||||
/// Returns the sum of the unsigned integers in this array of unsigned integers.
|
||||
- (NSUInteger)sumNSUInteger;
|
||||
|
||||
/// Returns the sum of the integers in this array of integers.
|
||||
- (NSInteger)sumNSInteger;
|
||||
|
||||
- (NSDictionary *)keyedBy:(id (^)(id))keySelector;
|
||||
- (NSDictionary *)groupBy:(id (^)(id value))keySelector;
|
||||
|
||||
|
|
|
@ -75,27 +75,7 @@
|
|||
}
|
||||
return r;
|
||||
}
|
||||
- (double)sumDouble {
|
||||
double s = 0.0;
|
||||
for (NSNumber *e in self) {
|
||||
s += [e doubleValue];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
- (NSUInteger)sumNSUInteger {
|
||||
NSUInteger s = 0;
|
||||
for (NSNumber *e in self) {
|
||||
s += [e unsignedIntegerValue];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
- (NSInteger)sumNSInteger {
|
||||
NSInteger s = 0;
|
||||
for (NSNumber *e in self) {
|
||||
s += [e integerValue];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSDictionary *)keyedBy:(id (^)(id value))keySelector {
|
||||
tskit_require(keySelector != nil);
|
||||
|
||||
|
|
Loading…
Reference in a new issue