Skip to content

Commit

Permalink
Merge pull request #4 from proximax-storage/develop
Browse files Browse the repository at this point in the history
0.0.2
  • Loading branch information
ryuta46 committed Oct 22, 2018
2 parents c7fe792 + 801625f commit cd92012
Show file tree
Hide file tree
Showing 14 changed files with 321 additions and 12 deletions.
12 changes: 12 additions & 0 deletions Nem2SdkSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,15 @@
6905731A7A6BC6972598E8C2 /* MosaicId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057FABD7E8E143066FF306 /* MosaicId.swift */; };
69057344AE8173DE1D4AB319 /* NamespaceRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057BBF6248EC257FF89A81 /* NamespaceRepository.swift */; };
6905736C1D5AD18B8443461F /* Decimal+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690577DDD4A8A7D4C0C28BFF /* Decimal+Extension.swift */; };
690574123B07C8045C3BCD46 /* SecureMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690576DA0364BB973840E73D /* SecureMessage.swift */; };
6905741E1CDBEF0B9E6111B4 /* SignedTransactionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057C90F97EEFD8C3366587 /* SignedTransactionTest.swift */; };
6905743441495CA4AA95419B /* TransactionStatusError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057A77D2C9D991A73D1E03 /* TransactionStatusError.swift */; };
6905744C5E59424E1B8E30C5 /* Hashes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057B986E9819C1F7A1D9EC /* Hashes.swift */; };
690574500C2D312CE09E7D11 /* IdGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057112997DDD3A4EF8EBC0 /* IdGenerator.swift */; };
6905746FE8393463A2CC1401 /* AccountHttp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057403EDCE2B19C886B9AE /* AccountHttp.swift */; };
690574853CB166EF4A274535 /* BlockchainHttp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690574D86759166EEF643ACB /* BlockchainHttp.swift */; };
690574BC29D8CE86112D79CC /* PrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6905727D398B6D62B9C61123 /* PrivateKey.swift */; };
690574F4738488EE76B39784 /* MessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6905798FB2623331B71963AB /* MessageType.swift */; };
69057501D968DE546B20BCFC /* CosignatureTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057C8FF4584C4E8042158C /* CosignatureTransaction.swift */; };
6905751D70865C6A49F707F7 /* MosaicSupplyType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690577C24D98A72C4CB7DF9C /* MosaicSupplyType.swift */; };
6905752B85432CD893B0D8B3 /* byte_order.c in Sources */ = {isa = PBXBuildFile; fileRef = 6905747BED623BABFD230DF2 /* byte_order.c */; };
Expand Down Expand Up @@ -188,6 +190,7 @@
69057CB77D0053A4FD639A7F /* NamespaceNameTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690575526BC6A27D1D607A09 /* NamespaceNameTest.swift */; };
69057CCCF623E80286DB26D9 /* MosaicName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057227F40DF62DB4A43BB1 /* MosaicName.swift */; };
69057CDF383F3B7847470D13 /* RegisterNamespaceTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6905763C5412EC92747DF5FA /* RegisterNamespaceTransaction.swift */; };
69057CE85A11889F49CF1584 /* SecureMessageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057C8F91360AA84BE1B408 /* SecureMessageTest.swift */; };
69057CF0A56ECAFB37A4D3AE /* AccountRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69057769EED737D20D4A27CF /* AccountRepository.swift */; };
69057D0F2E49B31CC09595B0 /* ed25519.h in Headers */ = {isa = PBXBuildFile; fileRef = 6905736F848C7AA310EB90A2 /* ed25519.h */; settings = {ATTRIBUTES = (Public, ); }; };
69057D1CFD6F2D5D44DFE8AF /* Numeric+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690570E4D59A5A2086AFAD50 /* Numeric+Extension.swift */; };
Expand Down Expand Up @@ -361,6 +364,7 @@
690576B9A11D8064952EBE39 /* ListenerChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListenerChannel.swift; sourceTree = "<group>"; };
690576C66F7344706C982A81 /* sha512.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha512.c; path = src/sha512.c; sourceTree = "<group>"; };
690576C9F957129A821B12C7 /* TransactionStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionStatus.swift; sourceTree = "<group>"; };
690576DA0364BB973840E73D /* SecureMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureMessage.swift; sourceTree = "<group>"; };
690576F415BDCEE642C47005 /* MosaicInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicInfo.swift; sourceTree = "<group>"; };
69057704C467557E62EEB68E /* TransactionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionType.swift; sourceTree = "<group>"; };
69057769EED737D20D4A27CF /* AccountRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountRepository.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -388,6 +392,7 @@
6905794CBDD98953113DB2C1 /* AddressTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressTest.swift; sourceTree = "<group>"; };
690579680D4BBA9D6D539EE3 /* MosaicSupplyChangeTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicSupplyChangeTransaction.swift; sourceTree = "<group>"; };
6905798BCB6BF7D7B8EE5382 /* SecretLockTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecretLockTransaction.swift; sourceTree = "<group>"; };
6905798FB2623331B71963AB /* MessageType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageType.swift; sourceTree = "<group>"; };
690579AB7AB64C884EE5C970 /* NamespaceName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NamespaceName.swift; sourceTree = "<group>"; };
690579BB951D521F5A6001BD /* MultisigAccountInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultisigAccountInfo.swift; sourceTree = "<group>"; };
690579C22CC886582EC79684 /* MosaicPropertiesTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MosaicPropertiesTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -417,6 +422,7 @@
69057C4294CA4B0D25F48620 /* ParameterizedTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParameterizedTest.swift; sourceTree = "<group>"; };
69057C4E59A05C27DEF1B5BA /* HashType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HashType.swift; sourceTree = "<group>"; };
69057C5C79CEBF378E572E36 /* TransactionTypeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionTypeTest.swift; sourceTree = "<group>"; };
69057C8F91360AA84BE1B408 /* SecureMessageTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureMessageTest.swift; sourceTree = "<group>"; };
69057C8FF4584C4E8042158C /* CosignatureTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CosignatureTransaction.swift; sourceTree = "<group>"; };
69057C90F97EEFD8C3366587 /* SignedTransactionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignedTransactionTest.swift; sourceTree = "<group>"; };
69057C96B2D6864C931AA9E6 /* TransferTransactionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferTransactionTest.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -843,6 +849,8 @@
690573498990C169FB7B406B /* TransactionAnnounceResponse.swift */,
690576C9F957129A821B12C7 /* TransactionStatus.swift */,
69057A77D2C9D991A73D1E03 /* TransactionStatusError.swift */,
6905798FB2623331B71963AB /* MessageType.swift */,
690576DA0364BB973840E73D /* SecureMessage.swift */,
);
path = transaction;
sourceTree = "<group>";
Expand Down Expand Up @@ -904,6 +912,7 @@
69057E566AF82EEF8FE20DEB /* SecretLockTransactionTest.swift */,
69057174468FF2DD571A303C /* SecretProofTransactionTest.swift */,
690576952CF286C600952C7A /* TransactionMappingTest.swift */,
69057C8F91360AA84BE1B408 /* SecureMessageTest.swift */,
);
path = transaction;
sourceTree = "<group>";
Expand Down Expand Up @@ -1256,6 +1265,8 @@
690579BAE0072106A6F98357 /* ListenerSubscribeMessage.swift in Sources */,
6905725E13CC6B0645DF993F /* Listener.swift in Sources */,
6905743441495CA4AA95419B /* TransactionStatusError.swift in Sources */,
690574F4738488EE76B39784 /* MessageType.swift in Sources */,
690574123B07C8045C3BCD46 /* SecureMessage.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -1320,6 +1331,7 @@
69057EC66807922AE0512EEC /* TransactionHttpTest.swift in Sources */,
6905727C1C4672097466B0E7 /* ListenerTest.swift in Sources */,
690575368C10C935E11C43E3 /* E2ETest.swift in Sources */,
69057CE85A11889F49CF1584 /* SecureMessageTest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
2 changes: 2 additions & 0 deletions Nem2SdkSwift/core/Nem2SdkSwiftError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ public enum Nem2SdkSwiftError: Error {
case parseError(String)
/// A serialize error
case serializeError(String)
/// A message encryption error
case messageEncryptionError(String)
}
1 change: 0 additions & 1 deletion Nem2SdkSwift/core/crypto/message_encryption.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#ifndef message_encryption_h
#define message_encryption_h

int create_random_bytes(unsigned char* buff, size_t size);
void create_shared_key(unsigned char* shared_key, const unsigned char* public_key, const unsigned char* private_key, const unsigned char* salt);

#endif /* message_encryption_h */
4 changes: 2 additions & 2 deletions Nem2SdkSwift/sdk/model/transaction/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import Foundation
public class Message {
// MARK: Properties
/// Message type.
public let type: UInt8
public let type: MessageType
/// Returns message payload.
public let payload: [UInt8]

init(type: UInt8, payload: [UInt8]) {
init(type: MessageType, payload: [UInt8]) {
self.type = type
self.payload = payload
}
Expand Down
13 changes: 13 additions & 0 deletions Nem2SdkSwift/sdk/model/transaction/MessageType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2018 ProximaX Limited. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

import Foundation

/// Enum containing message type constants.
public enum MessageType: UInt8 {
/// Plain message
case plain = 0
/// Encrypted message
case secure = 1
}
4 changes: 2 additions & 2 deletions Nem2SdkSwift/sdk/model/transaction/PlainMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class PlainMessage: Message {
*/
public init(text: String) {
self.text = text
super.init(type: 0, payload: Array(text.utf8))
super.init(type: .plain, payload: Array(text.utf8))
}

/**
Expand All @@ -29,7 +29,7 @@ public class PlainMessage: Message {
*/
init(payload: [UInt8]) {
self.text = String(bytes: payload, encoding: .utf8)
super.init(type: 0, payload: payload)
super.init(type: .plain, payload: payload)
}
}

100 changes: 100 additions & 0 deletions Nem2SdkSwift/sdk/model/transaction/SecureMessage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2018 ProximaX Limited. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

import Foundation
import CryptoSwift
import CommonCrypto

public class SecureMessage: Message {
private static let keyLength = 256 / 8
private static let blockSize = 16

// MARK: Properties
/// Plain text message. Nil If loaded payload cannot be decoded as utf-8 string.

/**
* Constructor
*
* - parameter encodedPayload: Encoded(encrypted) payload
*/
public init(encodedPayload: [UInt8]) {
super.init(type: .secure, payload: encodedPayload)
}

/**
* Constructor
*
* - parameter decodedPayload: Decoded(not encrypted) payload.
* - parameter privateKey: Private key.
* - parameter publicKey: Public key.
*/
public init(decodedPayload: [UInt8], privateKey: PrivateKey, publicKey: PublicKey) throws {
let salt = SecureMessage.createRandomBytesOf(SecureMessage.keyLength)
let iv = SecureMessage.createRandomBytesOf(SecureMessage.blockSize)

do {
let keyPair = KeyPair(privateKey: privateKey)
let sharedKey = SecureMessage.calculateSharedKey(keys: keyPair, peerPublicKey: publicKey.bytes, salt: salt)
let aes = try AES(key: sharedKey, blockMode: CBC(iv: iv), padding: .pkcs7)

let encryptedMessage = try aes.encrypt(decodedPayload)

let encodedPayload = salt + iv + encryptedMessage

super.init(type: .secure, payload: encodedPayload)
} catch {
throw Nem2SdkSwiftError.messageEncryptionError("Failed to encrypt message.")
}
}


/**
* Decodes the payload and returns is.
*
* - parameter privateKey: Private key.
* - parameter publicKey: Public key.
*/
public func getDecodedPayload(privateKey: PrivateKey, publicKey: PublicKey) throws -> [UInt8] {
guard payload.count >= SecureMessage.keyLength + SecureMessage.blockSize else {
throw Nem2SdkSwiftError.messageEncryptionError("Payload is too short to decode.")
}

let salt = payload[0..<SecureMessage.keyLength].map { $0 }
let iv = payload[SecureMessage.keyLength..<SecureMessage.keyLength + SecureMessage.blockSize].map { $0 }
let encryptedMessage = Array(payload.dropFirst(SecureMessage.keyLength + SecureMessage.blockSize))

do {
let keyPair = KeyPair(privateKey: privateKey)
let sharedKey = SecureMessage.calculateSharedKey(keys: keyPair, peerPublicKey: publicKey.bytes, salt: salt)


let aes = try AES(key: sharedKey, blockMode: CBC(iv: iv), padding: .pkcs7)

let message = try aes.decrypt(encryptedMessage)

return message
} catch {
throw Nem2SdkSwiftError.messageEncryptionError("Failed to decrypt message.")
}
}



private static func calculateSharedKey(keys: KeyPair, peerPublicKey: [UInt8], salt: [UInt8]) -> [UInt8] {
var sharedKey = [UInt8](repeating: 0, count: SecureMessage.keyLength)
var varPeerPublicKey = peerPublicKey
var varNativePrivateKey = keys.nativePrivateKey
var varSalt = salt

create_shared_key(&sharedKey, &varPeerPublicKey, &varNativePrivateKey, &varSalt)

return sharedKey
}

private static func createRandomBytesOf(_ count: Int) -> [UInt8] {
var randomBytes = [UInt8](repeating: 0, count: count)
let _ = SecRandomCopyBytes(kSecRandomDefault, count, &randomBytes)
return randomBytes
}
}
8 changes: 6 additions & 2 deletions Nem2SdkSwift/sdk/model/transaction/TransactionMapping.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,13 @@ class TransactionMapping {

let message: Message?
if let messageJson = try? transaction.getDictionary("message") {
let messageType: MessageType = try messageJson.getEnum("type")
let payload = try HexEncoder.toBytes(try messageJson.getString("payload"))
// TODO: Encrypted message
message = PlainMessage(payload: payload)

switch messageType {
case .plain: message = PlainMessage(payload: payload)
case .secure: message = SecureMessage(encodedPayload: payload)
}
} else {
message = nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class TransferTransaction: Transaction {
let messageBytes: [UInt8]
if let message = self.message {
messageSize = UInt16(message.payload.count + 1)
messageBytes = [message.type] + message.payload
messageBytes = [message.type.rawValue] + message.payload
} else {
messageSize = 0
messageBytes = []
Expand Down
2 changes: 2 additions & 0 deletions Nem2SdkSwiftTests/ParameterizedTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

import XCTest

// Parameterized Test Template
class ParameterizedTest: XCTestCase {
class func createTestCases() -> [ParameterizedTest] {
Expand Down
13 changes: 13 additions & 0 deletions Nem2SdkSwiftTests/TestUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,17 @@ class TestUtils {
}
}

static func expectMessageEncryptionError(message: String? = nil, _ body: () throws -> Void) {
XCTAssertThrowsError(try body()) { error in
if case Nem2SdkSwiftError.messageEncryptionError(let resultMessage) = error {
if message != nil {
XCTAssertEqual(message, resultMessage)
}
}
else {
XCTFail("Unexpected Error")
}
}
}

}
47 changes: 47 additions & 0 deletions Nem2SdkSwiftTests/sdk/infrastructure/E2ETest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ class E2ETest: XCTestCase {
private let cosignatoryAccount1 = try! Account(privateKeyHexString: TestSettings.cosignatory1PrivateKey, networkType: .mijinTest)
private let cosignatoryAccount2 = try! Account(privateKeyHexString: TestSettings.cosignatory2PrivateKey, networkType: .mijinTest)

// nem2-cli transaction namespace -r -n "test-root-namespace" -d 1000
private let namespaceId = try! NamespaceId(fullName: "test-root-namespace") // This namespace is created in functional testing
private let namespaceName = "test-root-namespace"

// nem2-cli transaction mosaic -m "test-mosaic" -n "test-root-namespace" -a 1000000 -t -s -l -d 0 -u 1000
private let mosaicId = try! MosaicId(fullName: "test-root-namespace:test-mosaic") // This mosaic is created in functional testing
private var listener: Listener!

Expand Down Expand Up @@ -443,6 +446,50 @@ class E2ETest: XCTestCase {
announceAndValidateTransaction(secretProofTransactionSigned, account.address)
}

func testEncryptAndDecryptMessage() {
let transferTransaction = TransferTransaction.create(
recipient: cosignatoryAccount1.address,
mosaics: [XEM.of(microXemAmount: 1)],
message: try! SecureMessage(
decodedPayload: Array("message".utf8),
privateKey: account.keyPair.privateKey,
publicKey: cosignatoryAccount1.keyPair.publicKey),
networkType: .mijinTest
)

let signedTransaction = account.sign(transaction: transferTransaction)

let confirmedObservable = listener.confirmed(address: account.address)

_ = try! transactionHttp.announce(signedTransaction: signedTransaction).toBlocking().first()!

let transaction = try! confirmedObservable.toBlocking().first()!

XCTAssertEqual(signedTransaction.hash, transaction.transactionInfo!.hash!)

guard let transfer = transaction as? TransferTransaction else {
XCTFail("Transaction is not transfer")
return
}

guard let message = transfer.message else {
XCTFail("Transaction does not have message.")
return
}

guard let secureMessage = transfer.message as? SecureMessage else {
XCTFail("Transaction does not have message.")
return
}
XCTAssertEqual(MessageType.secure, message.type)
XCTAssertEqual("message", String(
bytes: try! secureMessage.getDecodedPayload(
privateKey: cosignatoryAccount1.keyPair.privateKey,
publicKey: account.keyPair.publicKey),
encoding: .utf8))
}


func announceAndValidateTransaction(_ signedTransaction: SignedTransaction, _ address: Address) {
let confirmedObservable = listener.confirmed(address: address)

Expand Down
Loading

0 comments on commit cd92012

Please sign in to comment.