Compare commits
7 Commits
main
...
feature/Si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0af175812b | ||
|
|
459674cfb4 | ||
|
|
87bfaf0748 | ||
|
|
40d77fd50f | ||
|
|
7fef1e384b | ||
|
|
7653748f2f | ||
|
|
edcb438b1d |
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@ -9,11 +9,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#define ECCKeyLength 32
|
||||
#define ECCSignatureLength 64
|
||||
|
||||
extern NSErrorDomain const Curve25519KitErrorDomain;
|
||||
typedef NS_ERROR_ENUM(Curve25519KitErrorDomain, Curve25519KitError){
|
||||
Curve25519KitError_InvalidKeySize = 1
|
||||
};
|
||||
|
||||
@interface ECKeyPair : NSObject <NSSecureCoding>
|
||||
|
||||
@property (atomic, readonly) NSData *publicKey;
|
||||
@ -29,6 +24,12 @@ typedef NS_ERROR_ENUM(Curve25519KitErrorDomain, Curve25519KitError){
|
||||
privateKeyData:(NSData *)privateKeyData
|
||||
error:(NSError **)error NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* A transitionary interface, only to be called from within the implementation of ECKeyPair's class cluster.
|
||||
*/
|
||||
- (instancetype)initFromClassClusterSubclassOnly NS_DESIGNATED_INITIALIZER;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Curve25519.h"
|
||||
@ -9,65 +9,38 @@
|
||||
|
||||
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 */
|
||||
@interface ECKeyPair (ImplementedInSwift)
|
||||
+ (Class)concreteSubclass;
|
||||
- (nullable NSData *)sign:(NSData *)data error:(NSError **)error;
|
||||
@end
|
||||
|
||||
@implementation ECKeyPair
|
||||
|
||||
@dynamic publicKey;
|
||||
@dynamic privateKey;
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
+ (Class)classForKeyedUnarchiver
|
||||
{
|
||||
[coder encodeBytes:self.publicKey.bytes length:ECCKeyLength forKey:TSECKeyPairPublicKey];
|
||||
[coder encodeBytes:self.privateKey.bytes length:ECCKeyLength forKey:TSECKeyPairPrivateKey];
|
||||
return [self concreteSubclass];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
||||
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder
|
||||
{
|
||||
NSUInteger returnedLength = 0;
|
||||
const uint8_t *returnedBuffer = NULL;
|
||||
// FIXME: This should never be called thanks to +classForKeyedUnarchiver above,
|
||||
// but there's a test in SignalServiceKit that calls it directly.
|
||||
return [[[ECKeyPair concreteSubclass] alloc] initWithCoder:coder];
|
||||
}
|
||||
|
||||
// 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;
|
||||
- (void)encodeWithCoder:(nonnull NSCoder *)coder
|
||||
{
|
||||
OWSFail(@"Implemented by subclass; should never be called directly");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,44 +51,20 @@ extern int curve25519_sign(unsigned char *signature_out, /* 64 bytes */
|
||||
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;
|
||||
}
|
||||
_publicKey = publicKeyData;
|
||||
_privateKey = privateKeyData;
|
||||
}
|
||||
return [[[ECKeyPair concreteSubclass] alloc] initWithPublicKeyData:publicKeyData
|
||||
privateKeyData:privateKeyData
|
||||
error:error];
|
||||
}
|
||||
|
||||
- (instancetype)initFromClassClusterSubclassOnly
|
||||
{
|
||||
self = [super init];
|
||||
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);
|
||||
|
||||
return keyPair;
|
||||
return [[self concreteSubclass] generateKeyPair];
|
||||
}
|
||||
|
||||
- (NSData *)throws_sign:(NSData *)data
|
||||
@ -124,20 +73,13 @@ extern int curve25519_sign(unsigned char *signature_out, /* 64 bytes */
|
||||
OWSRaiseException(NSInvalidArgumentException, @"Missing data.");
|
||||
}
|
||||
|
||||
NSMutableData *signatureData = [NSMutableData dataWithLength:ECCSignatureLength];
|
||||
NSError *error;
|
||||
NSData *signatureData = [self sign:data error:&error];
|
||||
if (!signatureData) {
|
||||
OWSFail(@"Could not allocate buffer");
|
||||
OWSRaiseException(NSInternalInconsistencyException, @"Message couldn't be signed: %@", error);
|
||||
}
|
||||
|
||||
NSData *randomBytes = [Randomness generateRandomBytes:64];
|
||||
|
||||
if (curve25519_sign(
|
||||
signatureData.mutableBytes, self.privateKey.bytes, [data bytes], [data length], [randomBytes bytes])
|
||||
== -1) {
|
||||
OWSRaiseException(NSInternalInconsistencyException, @"Message couldn't be signed.");
|
||||
}
|
||||
|
||||
return [signatureData copy];
|
||||
return signatureData;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -1,28 +1,120 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalCoreKit
|
||||
import LibSignalClient
|
||||
|
||||
// Work around Swift's lack of factory initializers.
|
||||
// See https://bugs.swift.org/browse/SR-5255.
|
||||
public protocol ECKeyPairFromIdentityKeyPair {}
|
||||
public extension ECKeyPairFromIdentityKeyPair {
|
||||
init(_ keyPair: IdentityKeyPair) {
|
||||
self = ECKeyPairImpl(keyPair) as! Self
|
||||
}
|
||||
}
|
||||
extension ECKeyPair: ECKeyPairFromIdentityKeyPair {}
|
||||
|
||||
// TODO: Eventually we should define ECKeyPair entirely in Swift as a wrapper around IdentityKeyPair,
|
||||
// but doing that right now would break clients that are importing Curve25519.h and nothing else.
|
||||
// For now, just provide the API we'd like to have in the future via its subclass.
|
||||
extension ECKeyPair {
|
||||
public var identityKeyPair: IdentityKeyPair {
|
||||
(self as! ECKeyPairImpl).storedKeyPair
|
||||
}
|
||||
|
||||
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)
|
||||
public func ecPublicKey() throws -> ECPublicKey {
|
||||
return ECPublicKey(self.identityKeyPair.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")
|
||||
}
|
||||
public func ecPrivateKey() throws -> ECPrivateKey {
|
||||
return ECPrivateKey(self.identityKeyPair.privateKey)
|
||||
}
|
||||
|
||||
return try ECPrivateKey(keyData: privateKey)
|
||||
@objc private class var concreteSubclass: ECKeyPair.Type {
|
||||
return ECKeyPairImpl.self
|
||||
}
|
||||
}
|
||||
|
||||
/// A transitionary class. Do not use directly; continue using ECKeyPair instead.
|
||||
private class ECKeyPairImpl: ECKeyPair {
|
||||
private static let TSECKeyPairPublicKey = "TSECKeyPairPublicKey"
|
||||
private static let TSECKeyPairPrivateKey = "TSECKeyPairPrivateKey"
|
||||
|
||||
let storedKeyPair: IdentityKeyPair
|
||||
|
||||
init(_ keyPair: IdentityKeyPair) {
|
||||
storedKeyPair = keyPair
|
||||
super.init(fromClassClusterSubclassOnly: ())
|
||||
}
|
||||
|
||||
override convenience init(publicKeyData: Data, privateKeyData: Data) throws {
|
||||
// Go through ECPublicKey to handle the public key data without a type byte.
|
||||
let publicKey = try ECPublicKey(keyData: publicKeyData).key
|
||||
let privateKey = try PrivateKey(privateKeyData)
|
||||
|
||||
self.init(IdentityKeyPair(publicKey: publicKey, privateKey: privateKey))
|
||||
}
|
||||
|
||||
required convenience init?(coder: NSCoder) {
|
||||
var returnedLength = 0
|
||||
|
||||
let publicKeyBuffer = coder.decodeBytes(forKey: Self.TSECKeyPairPublicKey, returnedLength: &returnedLength)
|
||||
guard returnedLength == ECCKeyLength else {
|
||||
owsFailDebug("failure: wrong length for public key.")
|
||||
return nil
|
||||
}
|
||||
let publicKeyData = Data(bytes: publicKeyBuffer!, count: returnedLength)
|
||||
|
||||
let privateKeyBuffer = coder.decodeBytes(forKey: Self.TSECKeyPairPrivateKey, returnedLength: &returnedLength)
|
||||
guard returnedLength == ECCKeyLength else {
|
||||
owsFailDebug("failure: wrong length for private key.")
|
||||
return nil
|
||||
}
|
||||
let privateKeyData = Data(bytes: privateKeyBuffer!, count: returnedLength)
|
||||
|
||||
do {
|
||||
try self.init(publicKeyData: publicKeyData, privateKeyData: privateKeyData)
|
||||
} catch {
|
||||
owsFailDebug("error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func encode(with coder: NSCoder) {
|
||||
// Go through ECPublicKey to drop the type byte.
|
||||
self.identityKeyPair.publicKey.keyBytes.withUnsafeBufferPointer {
|
||||
coder.encodeBytes($0.baseAddress, length: $0.count, forKey: Self.TSECKeyPairPublicKey)
|
||||
}
|
||||
self.identityKeyPair.privateKey.serialize().withUnsafeBufferPointer {
|
||||
coder.encodeBytes($0.baseAddress, length: $0.count, forKey: Self.TSECKeyPairPrivateKey)
|
||||
}
|
||||
}
|
||||
|
||||
override class var supportsSecureCoding: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override var classForCoder: AnyClass {
|
||||
return ECKeyPair.self
|
||||
}
|
||||
|
||||
@objc private class func generateKeyPair() -> ECKeyPair {
|
||||
return ECKeyPairImpl(IdentityKeyPair.generate())
|
||||
}
|
||||
|
||||
@objc private func sign(_ data: Data) throws -> Data {
|
||||
return Data(identityKeyPair.privateKey.generateSignature(message: data))
|
||||
}
|
||||
|
||||
override var publicKey: Data {
|
||||
return Data(identityKeyPair.publicKey.keyBytes)
|
||||
}
|
||||
|
||||
override var privateKey: Data {
|
||||
return Data(identityKeyPair.privateKey.serialize())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,36 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalCoreKit
|
||||
import LibSignalClient
|
||||
|
||||
// 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 {
|
||||
public let key: PrivateKey
|
||||
|
||||
@objc
|
||||
public let keyData: Data
|
||||
public var keyData: Data {
|
||||
Data(key.serialize())
|
||||
}
|
||||
|
||||
@objc
|
||||
public init(keyData: Data) throws {
|
||||
guard keyData.count == ECCKeyLength else {
|
||||
throw OWSAssertionError("\(ECPrivateKey.logTag) key has invalid length")
|
||||
}
|
||||
self.key = try PrivateKey(keyData)
|
||||
}
|
||||
|
||||
self.keyData = keyData
|
||||
public init(_ key: PrivateKey) {
|
||||
self.key = key
|
||||
}
|
||||
|
||||
open override func isEqual(_ object: Any?) -> Bool {
|
||||
if let object = object as? ECPrivateKey {
|
||||
return keyData == object.keyData
|
||||
} else {
|
||||
guard let object = object as? ECPrivateKey else {
|
||||
return false
|
||||
}
|
||||
// FIXME: compare private keys directly?
|
||||
return keyData == object.keyData
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalCoreKit
|
||||
|
||||
public enum ECKeyError: Error {
|
||||
case assertionError(description: String)
|
||||
}
|
||||
import LibSignalClient
|
||||
|
||||
// See:
|
||||
// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java
|
||||
@ -16,46 +13,37 @@ public enum ECKeyError: Error {
|
||||
@objc
|
||||
public static let keyTypeDJB: UInt8 = 0x05
|
||||
|
||||
public let key: PublicKey
|
||||
|
||||
@objc
|
||||
public let keyData: Data
|
||||
public var keyData: Data {
|
||||
return Data(key.keyBytes)
|
||||
}
|
||||
|
||||
@objc
|
||||
public init(keyData: Data) throws {
|
||||
guard keyData.count == ECCKeyLength else {
|
||||
throw ECKeyError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length")
|
||||
}
|
||||
self.key = try PublicKey([ECPublicKey.keyTypeDJB] + keyData)
|
||||
}
|
||||
|
||||
self.keyData = keyData
|
||||
public init(_ key: PublicKey) {
|
||||
self.key = key
|
||||
}
|
||||
|
||||
// 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
|
||||
self.key = try PublicKey(serializedKeyData)
|
||||
}
|
||||
|
||||
@objc public var serialized: Data {
|
||||
return Data([ECPublicKey.keyTypeDJB] + keyData)
|
||||
return Data(key.serialize())
|
||||
}
|
||||
|
||||
open override func isEqual(_ object: Any?) -> Bool {
|
||||
if let object = object as? ECPublicKey {
|
||||
return keyData == object.keyData
|
||||
} else {
|
||||
guard let object = object as? ECPublicKey else {
|
||||
return false
|
||||
}
|
||||
return key == object.key
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
|
||||
@ -18,10 +18,11 @@ Pod::Spec.new do |spec|
|
||||
spec.framework = 'Security'
|
||||
spec.public_header_files = "Classes/*.h"
|
||||
spec.requires_arc = true
|
||||
spec.ios.deployment_target = "10.0"
|
||||
spec.ios.deployment_target = "12.2"
|
||||
|
||||
spec.dependency 'CocoaLumberjack'
|
||||
spec.dependency 'SignalCoreKit'
|
||||
spec.dependency 'LibSignalClient', '>= 0.15.0'
|
||||
|
||||
spec.test_spec 'Tests' do |test_spec|
|
||||
test_spec.source_files = 'BuildTests/BuildTestsTests/**/*.{h,m,swift}'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user