Compare commits

..

40 Commits

Author SHA1 Message Date
Matthew Chen
4fc1c10e98 Merge branch 'charlesmchen/fixBrokenTests' 2019-11-07 17:17:57 -03:00
Matthew Chen
53b34e07e4 Fix SMKServerCertificateTest.testBadSignature. 2019-11-07 13:27:07 -03:00
Michael Kirk
877c48f4bc Merge branch 'mkirk/linking' 2019-10-17 10:12:41 -06:00
Michael Kirk
8b6e70b18c move helpers from SMK 2019-10-17 10:05:01 -06:00
Michael Kirk
ad808db2fa expose failable designated init for ECKeyPair 2019-10-17 10:05:01 -06:00
Nora Trapp
3d09b9480f Merge branch 'nt/deployment-target' 2019-09-23 16:48:41 -07:00
Nora Trapp
e8671e71bd Update deployment target to iOS 10 2019-09-23 10:49:18 -07:00
Michael Kirk
9b5981d7e3 Merge branch 'mkirk/nullability-warnings' 2019-06-19 10:02:01 -06:00
Michael Kirk
c8384ed4a1 address compiler warnings 2019-06-18 15:17:35 -06:00
Michael Kirk
9e5663baf4 Merge branch 'mkirk/exception-wrapping' 2018-10-30 11:13:45 -06:00
Michael Kirk
3c9725d65c Don't throw objc exceptions from Swift 2018-10-30 11:13:34 -06:00
Matthew Chen
14effe344b Merge branch 'charlesmchen/assertKeyPairInSign' 2018-10-10 17:39:21 -04:00
Matthew Chen
6926c1bea8 Assert key pair in sign. 2018-10-10 17:07:32 -04:00
Matthew Chen
9d0f5e68d5 Merge branch 'charlesmchen/sck' 2018-09-28 08:48:51 -04:00
Matthew Chen
6ab1ebcd25 Add placeholder Swift test. 2018-09-28 08:48:41 -04:00
Matthew Chen
c04f9bc017 Move Randomness to SignalCoreKit. 2018-09-28 08:48:41 -04:00
Matthew Chen
134e14edf6 Update podspec. 2018-09-28 08:48:41 -04:00
Matthew Chen
c54600abb1 Fix asserts & logging. 2018-09-28 08:48:41 -04:00
Matthew Chen
f9c774ef72 Merge branch 'charlesmchen/asserts' 2018-09-28 08:47:02 -04:00
Matthew Chen
9e25cdbca2 Refine asserts. 2018-09-28 08:46:11 -04:00
Matthew Chen
6b2c7342d8 Merge branch 'charlesmchen/convenience' 2018-09-28 08:45:28 -04:00
Matthew Chen
1da78f7317 Add convenience method. 2018-09-28 08:43:06 -04:00
Matthew Chen
a4f46bd621 Merge branch 'charlesmchen/curveTests' 2018-09-21 09:39:46 -04:00
Matthew Chen
47748b9bd1 Expose tests in podspec. 2018-09-21 09:39:25 -04:00
Matthew Chen
ff5407362f Merge branch 'charlesmchen/memoryLeak' 2018-09-20 08:56:17 -04:00
Matthew Chen
bdfd99737a Respond to CR. 2018-09-19 15:09:11 -04:00
Matthew Chen
e4efb719c8 Fix memory leak. 2018-09-19 11:02:03 -04:00
Michael Kirk
14bfa06578 Merge branch 'mkirk/overflow' 2018-09-07 13:17:01 -06:00
Michael Kirk
83311455d7 add overflow macros 2018-08-30 09:47:29 -06:00
Michael Kirk
203dca7156 Make OWSAssert terminate in production, introduce OWSAssertDebug which will not
terminate in production.

No change in behavior here since these two methods aren't yet used in CurveKit.
2018-08-30 09:47:29 -06:00
Matthew Chen
95debe15f5 Merge branch 'charlesmchen/loggingAndAsserts' 2018-08-29 16:10:44 -04:00
Matthew Chen
c7507a93e1 Respond to CR. 2018-08-28 14:57:17 -04:00
Matthew Chen
17dbf0ab5a Respond to CR. Make headers private, change prefix. 2018-08-28 14:08:06 -04:00
Matthew Chen
fa918f69b9 Logging and asserts. 2018-08-28 12:26:21 -04:00
Matthew Chen
ced1466996 Merge branch 'charlesmchen/cleanupFormatting' 2018-08-02 16:09:06 -04:00
Matthew Chen
a16d135d71 Respond to CR. 2018-08-02 16:08:39 -04:00
Matthew Chen
516d47b426 Clean up formatting. 2018-07-27 10:36:28 -04:00
Matthew Chen
337cf5dd53 Clean up formatting. 2018-07-27 10:33:26 -04:00
Matthew Chen
fddf9c9d74 Clean up formatting. 2018-07-27 10:25:28 -04:00
Michael Kirk
a6e4496709 Merge branch 'mkirk/framework-friendly' 2017-11-29 14:36:26 -08:00
16 changed files with 482 additions and 193 deletions

15
.clang-format Normal file
View File

@ -0,0 +1,15 @@
---
BasedOnStyle: WebKit
AllowShortFunctionsOnASingleLine: false
BinPackArguments: false
BinPackParameters: false
ColumnLimit: 120
IndentCaseLabels: true
MaxEmptyLinesToKeep: 2
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PointerBindsToType: false
SpacesBeforeTrailingComments: 1
TabWidth: 8
UseTab: Never
...

View File

@ -0,0 +1,16 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import XCTest
// Cocoapods-generated test targets (like this one)
// fail to link if:
//
// * They only contain Obj-C tests.
// * They depend on pods that use Swift.
//
// The work around is to add (this) empty swift file
// to our test target.
//
// See: https://github.com/CocoaPods/CocoaPods/issues/7170

View File

@ -1,15 +1,11 @@
//
// BuildTestsTests.m
// BuildTestsTests
//
// Created by Frederic Jacobs on 22/07/14.
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "Curve25519.h"
#import "Randomness.h"
#import "Ed25519.h"
#import <SignalCoreKit/Randomness.h>
#import <XCTest/XCTest.h>
@interface SigningTests : XCTestCase
@ -35,14 +31,13 @@
ECKeyPair *key = [Curve25519 generateKeyPair];
NSData *data = [Randomness generateRandomBytes:i];
NSData *signature = [Ed25519 sign:data withKeyPair:key];
if (![Ed25519 verifySignature:signature publicKey:[key publicKey] data:data]) {
NSData *signature = [Ed25519 throws_sign:data withKeyPair:key];
if (![Ed25519 throws_verifySignature:signature publicKey:[key publicKey] data:data]) {
XCTAssert(false, @"Failed to verify signature while performing testing");
return;
}
}
}
}
@ -53,10 +48,10 @@
ECKeyPair *key = [Curve25519 generateKeyPair];
NSData *data = [Randomness generateRandomBytes:32];
NSData *signature = [Ed25519 sign:data withKeyPair:key];
if (![Ed25519 verifySignature:signature publicKey:[key publicKey] data:data]) {
NSData *signature = [Ed25519 throws_sign:data withKeyPair:key];
if (![Ed25519 throws_verifySignature:signature publicKey:[key publicKey] data:data]) {
XCTAssert(false, @"Verifying a signed 32-byte identity key failed");
return;
}

View File

@ -1,26 +1,42 @@
//
// Curve25519.h
//
// Created by Frederic Jacobs on 22/07/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#define ECCKeyLength 32
#define ECCSignatureLength 64
@interface ECKeyPair : NSObject <NSSecureCoding> {
uint8_t publicKey [ECCKeyLength];
uint8_t privateKey[ECCKeyLength];
}
extern NSErrorDomain const Curve25519KitErrorDomain;
typedef NS_ERROR_ENUM(Curve25519KitErrorDomain, Curve25519KitError){
Curve25519KitError_InvalidKeySize = 1
};
-(NSData*) publicKey;
@interface ECKeyPair : NSObject <NSSecureCoding>
@property (atomic, readonly) NSData *publicKey;
@property (atomic, readonly) NSData *privateKey;
- (instancetype)init NS_UNAVAILABLE;
/**
* Build a keypair from existing key data.
* If you need a *new* keypair, user `Curve25519.generateKeyPair` instead.
*/
- (nullable instancetype)initWithPublicKeyData:(NSData *)publicKeyData
privateKeyData:(NSData *)privateKeyData
error:(NSError **)error NS_DESIGNATED_INITIALIZER;
@end
#pragma mark -
@interface Curve25519 : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Generate a 32-byte shared secret from a public key and a key pair using curve25519.
*
@ -29,15 +45,21 @@
*
* @return 32-byte shared secret derived from ECDH with curve25519 public key and key pair.
*/
+ (NSData *)throws_generateSharedSecretFromPublicKey:(NSData *)theirPublicKey andKeyPair:(ECKeyPair *)keyPair NS_SWIFT_UNAVAILABLE("throws objc expections");
+ (NSData*)generateSharedSecretFromPublicKey:(NSData*)theirPublicKey andKeyPair:(ECKeyPair*)keyPair;
+ (NSData *)throws_generateSharedSecretFromPublicKey:(NSData *)publicKey privateKey:(NSData *)privateKey NS_SWIFT_UNAVAILABLE("throws objc expections");
+ (nullable NSData *)generateSharedSecretFromPublicKey:(NSData *)publicKey
privateKey:(NSData *)privateKey
error:(NSError **)outError;
/**
* Generate a curve25519 key pair
*
* @return curve25519 key pair.
*/
+ (ECKeyPair*)generateKeyPair;
+ (ECKeyPair *)generateKeyPair;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,124 +1,198 @@
//
// Curve25519.m
// BuildTests
//
// Created by Frederic Jacobs on 22/07/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
#import "Curve25519.h"
#import "Randomness.h"
#import <SignalCoreKit/OWSAsserts.h>
#import <SignalCoreKit/Randomness.h>
#import <SignalCoreKit/SCKExceptionWrapper.h>
NSString * const TSECKeyPairPublicKey = @"TSECKeyPairPublicKey";
NSString * const TSECKeyPairPrivateKey = @"TSECKeyPairPrivateKey";
NSString * const TSECKeyPairPreKeyId = @"TSECKeyPairPreKeyId";
NS_ASSUME_NONNULL_BEGIN
NSErrorDomain const Curve25519KitErrorDomain = @"Curve25519KitErrorDomain";
NSString *const TSECKeyPairPublicKey = @"TSECKeyPairPublicKey";
NSString *const TSECKeyPairPrivateKey = @"TSECKeyPairPrivateKey";
NSString *const TSECKeyPairPreKeyId = @"TSECKeyPairPreKeyId";
extern void curve25519_donna(unsigned char *output, const unsigned char *a, const unsigned char *b);
extern int curve25519_sign(unsigned char* signature_out, /* 64 bytes */
const unsigned char* curve25519_privkey, /* 32 bytes */
const unsigned char* msg, const unsigned long msg_len,
const unsigned char* random); /* 64 bytes */
extern int curve25519_sign(unsigned char *signature_out, /* 64 bytes */
const unsigned char *curve25519_privkey, /* 32 bytes */
const unsigned char *msg,
const unsigned long msg_len,
const unsigned char *random); /* 64 bytes */
@implementation ECKeyPair
+ (BOOL)supportsSecureCoding{
+ (BOOL)supportsSecureCoding
{
return YES;
}
-(void)encodeWithCoder:(NSCoder *)coder {
[coder encodeBytes:self->publicKey length:ECCKeyLength forKey:TSECKeyPairPublicKey];
[coder encodeBytes:self->privateKey length:ECCKeyLength forKey:TSECKeyPairPrivateKey];
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeBytes:self.publicKey.bytes length:ECCKeyLength forKey:TSECKeyPairPublicKey];
[coder encodeBytes:self.privateKey.bytes length:ECCKeyLength forKey:TSECKeyPairPrivateKey];
}
-(id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
NSUInteger returnedLength = 0;
const uint8_t *returnedBuffer = NULL;
// De-serialize public key
returnedBuffer = [coder decodeBytesForKey:TSECKeyPairPublicKey returnedLength:&returnedLength];
if (returnedLength != ECCKeyLength) {
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
NSUInteger returnedLength = 0;
const uint8_t *returnedBuffer = NULL;
// De-serialize public key
returnedBuffer = [coder decodeBytesForKey:TSECKeyPairPublicKey returnedLength:&returnedLength];
if (returnedLength != ECCKeyLength) {
OWSFailDebug(@"failure: wrong length for public key.");
return nil;
}
NSData *publicKeyData = [NSData dataWithBytes:returnedBuffer length:returnedLength];
// De-serialize private key
returnedBuffer = [coder decodeBytesForKey:TSECKeyPairPrivateKey returnedLength:&returnedLength];
if (returnedLength != ECCKeyLength) {
OWSFailDebug(@"failure: wrong length for private key.");
return nil;
}
NSData *privateKeyData = [NSData dataWithBytes:returnedBuffer length:returnedLength];
NSError *error;
ECKeyPair *keyPair = [self initWithPublicKeyData:publicKeyData
privateKeyData:privateKeyData
error:&error];
if (error != nil) {
OWSFailDebug(@"error: %@", error);
return nil;
}
return keyPair;
}
/**
* Build a keypair from existing key data.
* If you need a *new* keypair, user `generateKeyPair` instead.
*/
- (nullable instancetype)initWithPublicKeyData:(NSData *)publicKeyData
privateKeyData:(NSData *)privateKeyData
error:(NSError **)error
{
if (self = [super init]) {
if (publicKeyData.length != ECCKeyLength || privateKeyData.length != ECCKeyLength) {
*error = [NSError errorWithDomain:Curve25519KitErrorDomain
code:Curve25519KitError_InvalidKeySize
userInfo:nil];
return nil;
}
memcpy(self->publicKey, returnedBuffer, ECCKeyLength);
// De-serialize private key
returnedBuffer = [coder decodeBytesForKey:TSECKeyPairPrivateKey returnedLength:&returnedLength];
if (returnedLength != ECCKeyLength) {
return nil;
}
memcpy(self->privateKey, returnedBuffer, ECCKeyLength);
_publicKey = publicKeyData;
_privateKey = privateKeyData;
}
return self;
}
+ (ECKeyPair *)generateKeyPair
{
// Generate key pair as described in
// https://code.google.com/p/curve25519-donna/
NSMutableData *privateKey = [[Randomness generateRandomBytes:ECCKeyLength] mutableCopy];
uint8_t *privateKeyBytes = privateKey.mutableBytes;
privateKeyBytes[0] &= 248;
privateKeyBytes[31] &= 127;
privateKeyBytes[31] |= 64;
static const uint8_t basepoint[ECCKeyLength] = { 9 };
NSMutableData *publicKey = [NSMutableData dataWithLength:ECCKeyLength];
if (!publicKey) {
OWSFail(@"Could not allocate buffer");
}
curve25519_donna(publicKey.mutableBytes, privateKey.mutableBytes, basepoint);
ECKeyPair *keyPair = [[ECKeyPair alloc] initWithPublicKeyData:[publicKey copy]
privateKeyData:[privateKey copy]
error:nil];
OWSAssert(keyPair != nil);
+(ECKeyPair*)generateKeyPair{
ECKeyPair* keyPair =[[ECKeyPair alloc] init];
// Generate key pair as described in https://code.google.com/p/curve25519-donna/
memcpy(keyPair->privateKey, [[Randomness generateRandomBytes:32] bytes], 32);
keyPair->privateKey[0] &= 248;
keyPair->privateKey[31] &= 127;
keyPair->privateKey[31] |= 64;
static const uint8_t basepoint[ECCKeyLength] = {9};
curve25519_donna(keyPair->publicKey, keyPair->privateKey, basepoint);
return keyPair;
}
-(NSData*) publicKey {
return [NSData dataWithBytes:self->publicKey length:32];
}
- (NSData *)throws_sign:(NSData *)data
{
if (!data) {
OWSRaiseException(NSInvalidArgumentException, @"Missing data.");
}
NSMutableData *signatureData = [NSMutableData dataWithLength:ECCSignatureLength];
if (!signatureData) {
OWSFail(@"Could not allocate buffer");
}
-(NSData*) sign:(NSData*)data{
Byte signatureBuffer[ECCSignatureLength];
NSData *randomBytes = [Randomness generateRandomBytes:64];
if(curve25519_sign(signatureBuffer, self->privateKey, [data bytes], [data length], [randomBytes bytes]) == -1 ){
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Message couldn't be signed." userInfo:nil];
}
NSData *signature = [NSData dataWithBytes:signatureBuffer length:ECCSignatureLength];
return signature;
}
-(NSData*) generateSharedSecretFromPublicKey:(NSData*)theirPublicKey {
unsigned char *sharedSecret = NULL;
if ([theirPublicKey length] != 32) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The supplied public key does not contain 32 bytes" userInfo:nil];
if (curve25519_sign(
signatureData.mutableBytes, self.privateKey.bytes, [data bytes], [data length], [randomBytes bytes])
== -1) {
OWSRaiseException(NSInternalInconsistencyException, @"Message couldn't be signed.");
}
sharedSecret = malloc(32);
if (sharedSecret == NULL) {
free(sharedSecret);
return nil;
}
curve25519_donna(sharedSecret,self->privateKey, [theirPublicKey bytes]);
NSData *sharedSecretData = [NSData dataWithBytes:sharedSecret length:32];
free(sharedSecret);
return sharedSecretData;
return [signatureData copy];
}
@end
#pragma mark -
@implementation Curve25519
+(ECKeyPair*)generateKeyPair{
+ (ECKeyPair *)generateKeyPair
{
return [ECKeyPair generateKeyPair];
}
+(NSData*)generateSharedSecretFromPublicKey:(NSData *)theirPublicKey andKeyPair:(ECKeyPair *)keyPair{
return [keyPair generateSharedSecretFromPublicKey:theirPublicKey];
+ (NSData *)throws_generateSharedSecretFromPublicKey:(NSData *)theirPublicKey andKeyPair:(ECKeyPair *)keyPair
{
if (!keyPair) {
OWSRaiseException(NSInvalidArgumentException, @"Missing key pair.");
}
return [self throws_generateSharedSecretFromPublicKey:theirPublicKey privateKey:keyPair.privateKey];
}
+ (nullable NSData *)generateSharedSecretFromPublicKey:(NSData *)publicKey
privateKey:(NSData *)privateKey
error:(NSError **)outError
{
@try {
return [self throws_generateSharedSecretFromPublicKey:publicKey privateKey:privateKey];
} @catch (NSException *exception) {
*outError = SCKExceptionWrapperErrorMake(exception);
return nil;
}
}
+ (NSData *)throws_generateSharedSecretFromPublicKey:(NSData *)publicKey privateKey:(NSData *)privateKey
{
if (publicKey.length != ECCKeyLength) {
OWSRaiseException(
NSInvalidArgumentException, @"Public key has unexpected length: %lu", (unsigned long)publicKey.length);
}
if (privateKey.length != ECCKeyLength) {
OWSRaiseException(
NSInvalidArgumentException, @"Private key has unexpected length: %lu", (unsigned long)privateKey.length);
}
NSMutableData *sharedSecretData = [NSMutableData dataWithLength:32];
if (!sharedSecretData) {
OWSFail(@"Could not allocate buffer");
}
curve25519_donna(sharedSecretData.mutableBytes, privateKey.bytes, publicKey.bytes);
return [sharedSecretData copy];
}
@end
NS_ASSUME_NONNULL_END

0
Classes/Curve25519Kit.h Normal file
View File

28
Classes/ECKeyPair.swift Normal file
View File

@ -0,0 +1,28 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalCoreKit
public extension ECKeyPair {
// TODO: Rename to publicKey(), rename existing publicKey() method to publicKeyData().
func ecPublicKey() throws -> ECPublicKey {
guard publicKey.count == ECCKeyLength else {
throw OWSAssertionError("\(logTag) public key has invalid length")
}
// NOTE: we don't use ECPublicKey(serializedKeyData:) since the
// key data should not have a type byte.
return try ECPublicKey(keyData: publicKey)
}
// TODO: Rename to privateKey(), rename existing privateKey() method to privateKeyData().
func ecPrivateKey() throws -> ECPrivateKey {
guard privateKey.count == ECCKeyLength else {
throw OWSAssertionError("\(logTag) private key has invalid length")
}
return try ECPrivateKey(keyData: privateKey)
}
}

View File

@ -0,0 +1,35 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalCoreKit
// See:
// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/ECPrivateKey.java
@objc public class ECPrivateKey: NSObject {
@objc
public let keyData: Data
@objc
public init(keyData: Data) throws {
guard keyData.count == ECCKeyLength else {
throw OWSAssertionError("\(ECPrivateKey.logTag) key has invalid length")
}
self.keyData = keyData
}
open override func isEqual(_ object: Any?) -> Bool {
if let object = object as? ECPrivateKey {
return keyData == object.keyData
} else {
return false
}
}
public override var hash: Int {
return keyData.hashValue
}
}

64
Classes/ECPublicKey.swift Normal file
View File

@ -0,0 +1,64 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalCoreKit
public enum ECKeyError: Error {
case assertionError(description: String)
}
// See:
// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java
@objc public class ECPublicKey: NSObject {
@objc
public static let keyTypeDJB: UInt8 = 0x05
@objc
public let keyData: Data
@objc
public init(keyData: Data) throws {
guard keyData.count == ECCKeyLength else {
throw ECKeyError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length")
}
self.keyData = keyData
}
// https://github.com/signalapp/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java#L30
@objc
public init(serializedKeyData: Data) throws {
let parser = OWSDataParser(data: serializedKeyData)
let typeByte = try parser.nextByte(name: "type byte")
guard typeByte == ECPublicKey.keyTypeDJB else {
throw ECKeyError.assertionError(description: "\(ECPublicKey.logTag) key data has invalid type byte")
}
let keyData = try parser.remainder(name: "key data")
guard keyData.count == ECCKeyLength else {
throw ECKeyError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length")
}
self.keyData = keyData
}
@objc public var serialized: Data {
return Data([ECPublicKey.keyTypeDJB] + keyData)
}
open override func isEqual(_ object: Any?) -> Bool {
if let object = object as? ECPublicKey {
return keyData == object.keyData
} else {
return false
}
}
public override var hash: Int {
return keyData.hashValue
}
}

View File

@ -1,12 +1,11 @@
//
// Ed25519.h
//
// Created by Frederic Jacobs on 22/07/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class ECKeyPair;
@interface Ed25519 : NSObject
@ -19,19 +18,43 @@
*
* @return The ed25519 64-bytes signature.
*/
+(NSData*)sign:(NSData*)data withKeyPair:(ECKeyPair*)keyPair;
+ (NSData *)throws_sign:(NSData *)data withKeyPair:(ECKeyPair *)keyPair NS_SWIFT_UNAVAILABLE("throws objc exceptions");
+ (nullable NSData *)sign:(NSData *)data withKeyPair:(ECKeyPair *)keyPair error:(NSError **)outError;
/**
* Verify ed25519 signature with 32-bytes Curve25519 key pair. Throws an NSInvalid
*
* @param signature ed25519 64-byte signature.
* @param pubKey public key of the signer.
* @param publicKey public key of the signer.
* @param data data to be checked against the signature.
*
* @return Returns TRUE if the signature is valid, false if it's not.
*/
+ (BOOL)throws_verifySignature:(NSData *)signature
publicKey:(NSData *)publicKey
data:(NSData *)data NS_SWIFT_UNAVAILABLE("throws objc exceptions");
+(BOOL)verifySignature:(NSData*)signature publicKey:(NSData*)pubKey data:(NSData*)data;
/**
* Verify ed25519 signature with 32-bytes Curve25519 key pair. Throws an NSInvalid
*
* @param signature ed25519 64-byte signature.
* @param publicKey public key of the signer.
* @param data data to be checked against the signature.
* @param didVerify whether or not the signature was verified.
*
* @return Returns YES if no error was encountered
* Returns NO if an error was encountered while verifying signature.
*
* NOTE: In line with convention's required for Swift interop, the return value does *not* indicate
* whether or not the signature was verified - check `didVerify` for that. The return value only
* indicates whether an error was encountered.
*/
+ (BOOL)verifySignature:(NSData *)signature
publicKey:(NSData *)publicKey
data:(NSData *)data
didVerify:(BOOL *)didVerify
error:(NSError **)outError NS_REFINED_FOR_SWIFT;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,50 +1,92 @@
//
// Ed25519.m
// BuildTests
//
// Created by Frederic Jacobs on 22/07/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "Ed25519.h"
#import "Curve25519.h"
#import <SignalCoreKit/OWSAsserts.h>
#import <SignalCoreKit/SCKExceptionWrapper.h>
NS_ASSUME_NONNULL_BEGIN
extern int curve25519_verify(const unsigned char *signature, /* 64 bytes */
const unsigned char *curve25519_pubkey, /* 32 bytes */
const unsigned char *msg,
const unsigned long msg_len);
@interface ECKeyPair ()
-(NSData*) sign:(NSData*)data;
- (NSData *)throws_sign:(NSData *)data;
@end
extern int curve25519_verify(const unsigned char* signature, /* 64 bytes */
const unsigned char* curve25519_pubkey, /* 32 bytes */
const unsigned char* msg, const unsigned long msg_len);
#pragma mark -
@implementation Ed25519
+(NSData*)sign:(NSData*)data withKeyPair:(ECKeyPair*)keyPair{
if ([data length] < 1) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Data needs to be at least one byte" userInfo:nil];
+ (nullable NSData *)sign:(NSData *)data withKeyPair:(ECKeyPair *)keyPair error:(NSError **)outError
{
@try {
return [self throws_sign:data withKeyPair:keyPair];
} @catch (NSException *exception) {
*outError = SCKExceptionWrapperErrorMake(exception);
return nil;
}
return [keyPair sign:data];
}
+(BOOL)verifySignature:(NSData*)signature publicKey:(NSData*)pubKey data:(NSData*)data{
+ (NSData *)throws_sign:(NSData *)data withKeyPair:(ECKeyPair *)keyPair
{
if ([data length] < 1) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Data needs to be at least one byte" userInfo:nil];
OWSRaiseException(NSInvalidArgumentException, @"Data needs to be at least one byte");
}
if (!keyPair) {
OWSRaiseException(NSInvalidArgumentException, @"Missing key pair.");
}
return [keyPair throws_sign:data];
}
+ (BOOL)verifySignature:(NSData *)signature
publicKey:(NSData *)publicKey
data:(NSData *)data
didVerify:(BOOL *)didVerify
error:(NSError **)outError;
{
@try {
*didVerify = [self throws_verifySignature:signature publicKey:publicKey data:data];
// TODO this seems potentially unintuitive for the caller.
// Instead of returning YES, should we remove didVerify and return an error when verification fails? (but no
// exception was thrown)
return YES;
} @catch (NSException *exception) {
*outError = SCKExceptionWrapperErrorMake(exception);
return NO;
}
}
+ (BOOL)throws_verifySignature:(NSData *)signature publicKey:(NSData *)pubKey data:(NSData *)data
{
if ([data length] < 1) {
OWSRaiseException(NSInvalidArgumentException, @"Data needs to be at least one byte");
}
if ([data length] >= ULONG_MAX) {
OWSRaiseException(NSInvalidArgumentException, @"Data is too long.");
}
if ([pubKey length] != ECCKeyLength) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Public Key isn't 32 bytes" userInfo:nil];
OWSRaiseException(
NSInvalidArgumentException, @"Public Key has unexpected length: %lu", (unsigned long)pubKey.length);
}
if ([signature length] != ECCSignatureLength) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Signature isn't 64 bytes" userInfo:nil];
OWSRaiseException(
NSInvalidArgumentException, @"Signature has unexpected length: %lu", (unsigned long)signature.length);
}
BOOL success = (curve25519_verify([signature bytes], [pubKey bytes], [data bytes], [data length]) == 0);
return success;
}
@end
NS_ASSUME_NONNULL_END

14
Classes/Ed25519.swift Normal file
View File

@ -0,0 +1,14 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
public extension Ed25519 {
class func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool {
var didVerify: ObjCBool = false
try __verifySignature(signature, publicKey: publicKey, data: data, didVerify: &didVerify)
return didVerify.boolValue
}
}

View File

@ -1,24 +0,0 @@
//
// Randomness.h
// AxolotlKit
//
// Created by Frederic Jacobs on 21/07/14.
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Randomness : NSObject
/**
* Generates a given number of cryptographically secure bytes using SecRandomCopyBytes.
*
* @param numberBytes The number of bytes to be generated.
*
* @return Random Bytes.
*/
+(NSData*) generateRandomBytes:(int)numberBytes;
@end

View File

@ -1,24 +0,0 @@
//
// Randomness.m
// AxolotlKit
//
// Created by Frederic Jacobs on 21/07/14.
// Copyright (c) 2014 Frederic Jacobs. All rights reserved.
//
#import "Randomness.h"
@implementation Randomness
+(NSData*) generateRandomBytes:(int)numberBytes {
/* used to generate db master key, and to generate signaling key, both at install */
NSMutableData* randomBytes = [NSMutableData dataWithLength:numberBytes];
int err = 0;
err = SecRandomCopyBytes(kSecRandomDefault,numberBytes,[randomBytes mutableBytes]);
if(err != noErr && [randomBytes length] != numberBytes) {
@throw [NSException exceptionWithName:@"random problem" reason:@"problem generating the random " userInfo:nil];
}
return [NSData dataWithData:randomBytes];
}
@end

View File

@ -11,10 +11,19 @@ Pod::Spec.new do |spec|
Curve25519 is a fast and secure curve used for key agreement. Unfortunately, it does not support signing out of the box. This pod translates the point curves to do ed25519 signing with curve25519 keys.
DESC
spec.source = { :git => 'https://github.com/WhisperSystems/Curve25519Kit.git', :tag => "#{spec.version}" }
spec.source_files = 'Classes/*.{h,m}', 'Sources/Curve25519/curve25519-donna.c', 'Sources/ed25519/*.{c,h}', 'Sources/ed25519/additions/*.{c,h}', 'Sources/ed25519/nacl_sha512/*.{c,h}', 'Sources/ed25519/nacl_includes/*.{c,h}'
spec.source = { :git => 'https://github.com/signalapp/Curve25519Kit.git', :tag => "#{spec.version}" }
spec.source_files = 'Classes/*.{h,m,swift}', 'Sources/Curve25519/curve25519-donna.c', 'Sources/ed25519/*.{c,h}', 'Sources/ed25519/additions/*.{c,h}', 'Sources/ed25519/nacl_sha512/*.{c,h}', 'Sources/ed25519/nacl_includes/*.{c,h}', 'Private/*.{h,m}'
spec.public_header_files = 'Classes/**/*.h'
#spec.private_header_files = 'Sources/ed25519/nacl_includes/*.h','Sources/ed25519/additions/*.h', 'Sources/ed25519/nacl_sha512/*.h'
spec.framework = 'Security'
spec.public_header_files = "Classes/*.h"
spec.requires_arc = true
spec.ios.deployment_target = "10.0"
spec.dependency 'CocoaLumberjack'
spec.dependency 'SignalCoreKit'
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'BuildTests/BuildTestsTests/**/*.{h,m,swift}'
end
end

View File

@ -63,7 +63,7 @@ int curve25519_sign(unsigned char* signature_out,
signature_out[63] &= 0x7F; /* bit should be zero already, but just in case */
signature_out[63] |= sign_bit;
//free(sigbuf);
free(sigbuf);
return 0;
}