Compare commits
40 Commits
mkirk/fram
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fc1c10e98 | ||
|
|
53b34e07e4 | ||
|
|
877c48f4bc | ||
|
|
8b6e70b18c | ||
|
|
ad808db2fa | ||
|
|
3d09b9480f | ||
|
|
e8671e71bd | ||
|
|
9b5981d7e3 | ||
|
|
c8384ed4a1 | ||
|
|
9e5663baf4 | ||
|
|
3c9725d65c | ||
|
|
14effe344b | ||
|
|
6926c1bea8 | ||
|
|
9d0f5e68d5 | ||
|
|
6ab1ebcd25 | ||
|
|
c04f9bc017 | ||
|
|
134e14edf6 | ||
|
|
c54600abb1 | ||
|
|
f9c774ef72 | ||
|
|
9e25cdbca2 | ||
|
|
6b2c7342d8 | ||
|
|
1da78f7317 | ||
|
|
a4f46bd621 | ||
|
|
47748b9bd1 | ||
|
|
ff5407362f | ||
|
|
bdfd99737a | ||
|
|
e4efb719c8 | ||
|
|
14bfa06578 | ||
|
|
83311455d7 | ||
|
|
203dca7156 | ||
|
|
95debe15f5 | ||
|
|
c7507a93e1 | ||
|
|
17dbf0ab5a | ||
|
|
fa918f69b9 | ||
|
|
ced1466996 | ||
|
|
a16d135d71 | ||
|
|
516d47b426 | ||
|
|
337cf5dd53 | ||
|
|
fddf9c9d74 | ||
|
|
a6e4496709 |
15
.clang-format
Normal file
15
.clang-format
Normal 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
|
||||
...
|
||||
16
BuildTests/BuildTestsTests/Curve25519KitSwiftTests.swift
Normal file
16
BuildTests/BuildTestsTests/Curve25519KitSwiftTests.swift
Normal 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
0
Classes/Curve25519Kit.h
Normal file
28
Classes/ECKeyPair.swift
Normal file
28
Classes/ECKeyPair.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
35
Classes/ECPrivateKey.swift
Normal file
35
Classes/ECPrivateKey.swift
Normal 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
64
Classes/ECPublicKey.swift
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
14
Classes/Ed25519.swift
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user