Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
consuelita committed Nov 14, 2018
0 parents commit eab1b7e
Show file tree
Hide file tree
Showing 12 changed files with 779 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
16 changes: 16 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "Datable",
"repositoryURL": "https://github.com/OperatorFoundation/Datable.git",
"state": {
"branch": null,
"revision": "cc91e8b71dbbe125a19090c26f4e8c21b6d0dcb4",
"version": "1.0.5"
}
}
]
},
"version": 1
}
28 changes: 28 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "ReplicantSwift",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "ReplicantSwift",
targets: ["ReplicantSwift"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/OperatorFoundation/Datable.git", from: "1.0.5"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "ReplicantSwift",
dependencies: ["Datable"]),
.testTarget(
name: "ReplicantSwiftTests",
dependencies: ["ReplicantSwift"]),
]
)
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ReplicantSwift

A description of this package.
17 changes: 17 additions & 0 deletions Sources/ReplicantSwift/Constants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Constants.swift
// ReplicantSwift
//
// Created by Adelita Schule on 11/9/18.
//

import Foundation

let chunkSize = 4096
let aesOverhead = 81
let bufferSize = chunkSize - aesOverhead
let keySize = 64
let keyDataSize = keySize + 1
let cryptoHandshakeSize = chunkSize
let cryptoHandshakePaddingSize = cryptoHandshakeSize - keySize
let responseSize = chunkSize
64 changes: 64 additions & 0 deletions Sources/ReplicantSwift/CryptoHandshake.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// CryptoHandshake.swift
// ReplicantSwift
//
// Created by Adelita Schule on 11/9/18.
//

import Foundation
import Datable
import CommonCrypto

/**
The client will need to know the server’s public key as part of the configuration. The server will not know the client’s public key, so the first thing the client will need to send is it’s public key. After that, it will just send encrypted data. Please note that the client public key when exported to a Data from CommonCrypto will be 65 bytes, where the first byte is always 4. This should be stripped off to remove the redundant 4 and just the remaining 64 bytes should be sent. The key size should then be padded to be the size of one chunk.
The server will then send a response which is the size of one chunk, and which contains random bytes that should be discarded.
*/
class CryptoHandshake: NSObject
{
let encryptor = Encryption()

/// The client will need to know the server’s public key as part of the configuration.
var serverPublicKey: Data

/// The client public key when exported to a Data from CommonCrypto will be 65 bytes, where the first byte is always 4. This should be stripped off to remove the redundant 4 and just the remaining 64 bytes should be sent. The key size should then be padded to be the size of one chunk.
var clientPublicKey: Data

init?(withKeyData clientKeyData: Data, andServerKeyData serverKeyData: Data)
{
guard let allDressedUp = encryptor.cleanAndPadKey(keyData: clientKeyData)
else
{
return nil
}

clientPublicKey = allDressedUp
serverPublicKey = serverKeyData
}

init?(withKey clientKey: SecKey, andServerKeyData serverKeyData: Data)
{
var error: Unmanaged<CFError>?

// Encode public key as data
guard let clientPublicData = SecKeyCopyExternalRepresentation(clientKey, &error) as Data?
else
{
print("\nUnable to generate public key external representation: \(error!.takeRetainedValue() as Error)\n")
return nil
}

//FIXME: Padding
guard let cleanClientPublicData = encryptor.cleanAndPadKey(keyData: clientPublicData)
else
{
return nil
}

clientPublicKey = cleanClientPublicData
serverPublicKey = serverKeyData
}


}


219 changes: 219 additions & 0 deletions Sources/ReplicantSwift/Encryption.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
//
// Encryption.swift
// ReplicantSwift
//
// Created by Adelita Schule on 11/9/18.
//

import Foundation
import Security
import CommonCrypto

class Encryption: NSObject
{
let algorithm: SecKeyAlgorithm = .eciesEncryptionCofactorX963SHA256AESGCM
//var privateKey: SecKey

// public init?(withPrivateKey initKey: Data?)
// {
// if let providedKey = initKey
// {
// guard let secKey = Encryption.decodeKey(fromData: providedKey)
// else
// {
// print("\nFailed to initialize Replicant: Unable to create SecKey from key data provided.")
// return nil
// }
//
// privateKey = secKey
// }
// else
// {
// guard let newKey = Encryption.generatePrivateKey()
// else
// {
// return nil
// }
//
// privateKey = newKey
// }
//
// }


func generatePrivateKey() -> SecKey?
{
// Generate private key
let tag = "com.example.keys.mykey".data(using: .utf8)!

let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.privateKeyUsage,
nil)!

let privateKeyAttributes: [String: Any] = [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag
/*kSecAttrAccessControl as String: access*/
]

let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
/*kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,*/
kSecPrivateKeyAttrs as String: privateKeyAttributes
]

var error: Unmanaged<CFError>?
guard let alicePrivate = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
else
{
print("\nUnable to generate the client private key: \(error!.takeRetainedValue() as Error)\n")
return nil
}

return alicePrivate
}

/**
Generate a public key from the provided private key and encodes it as data.

- Returns: optional, encoded key as data
*/
func generatePublicKey(usingPrivateKey privateKey: SecKey) -> Data?
{
var error: Unmanaged<CFError>?

guard let alicePublic = SecKeyCopyPublicKey(privateKey)
else
{
print("\nUnable to generate a public key from the provided private key.\n")
return nil
}

// Encode public key as data
guard let alicePublicData = SecKeyCopyExternalRepresentation(alicePublic, &error) as Data?
else
{
print("\nUnable to generate public key external representation: \(error!.takeRetainedValue() as Error)\n")
return nil
}

return alicePublicData
}

/// Decode data to get public key
static func decodeKey(fromData publicKeyData: Data) -> SecKey?
{
var error: Unmanaged<CFError>?

let options: [String: Any] = [kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits as String: 256]

guard let decodedBobPublicKey = SecKeyCreateWithData(publicKeyData as CFData, options as CFDictionary, &error)
else
{
print("\nUnable to decode server public key: \(error!.takeRetainedValue() as Error)\n")
return nil
}

return decodedBobPublicKey
}

/// Encrypt payload
func encrypt(payload: Data, usingServerKey serverPublicKey: SecKey) -> Data?
{
var error: Unmanaged<CFError>?

guard let cipherText = SecKeyCreateEncryptedData(serverPublicKey, algorithm, payload as CFData, &error) as Data?
else
{
print("\nUnable to encrypt payload: \(error!.takeRetainedValue() as Error)\n")
return nil
}

return cipherText
}

/// Decrypt payload
/// - Parameter payload: Data
/// - Parameter privateKey: SecKey
func decrypt(payload: Data, usingPrivateKey privateKey: SecKey) -> Data?
{
var error: Unmanaged<CFError>?

guard let decryptedText = SecKeyCreateDecryptedData(privateKey, algorithm, payload as CFData, &error) as Data?
else
{
print("\nUnable to decrypt payload: \(error!.takeRetainedValue() as Error)\n")
return nil
}

return decryptedText
}

func cleanKeyData(keyData: Data) -> Data?
{
if keyData.count == keyDataSize
{
if keyData.first! == 4
{
// Strip the redundant 4 from the key data
let cleanKey = keyData.dropFirst()
return cleanKey
}
else
{
print("\nFailed to clean key: Data was 65 bytes but the first byte was not 4.\n")
return nil
}
}
else if keyData.count == keySize
{
print("\nReturning unchanged key data, the byte count was already 64.\n")
return keyData
}
else
{
print("Failed to clean key data: unexpected byte count of \(keyData.count)")
return nil
}
}

func getKeyPadding() -> Data?
{
var bytes = [UInt8](repeating: 0, count: chunkSize - keySize)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)

if status == errSecSuccess
{
// Always test the status.
print(bytes)
// Prints something different every time you run.
return Data(array: bytes)
}
else
{
print("\nFailed to gnerate padding: \(status)\n")
return nil
}
}

func cleanAndPadKey(keyData: Data) -> Data?
{
guard let cleanKey = cleanKeyData(keyData: keyData)
else
{
return nil
}

guard let padding = getKeyPadding()
else
{
return nil
}

return cleanKey + padding
}
}
14 changes: 14 additions & 0 deletions Sources/ReplicantSwift/ReplicantSwift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import Security
import CommonCrypto

/**
* [Using Keys For Encryption](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/using_keys_for_encryption),
* [Storing Keys in the Secure Enclave](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave),
* [seckeyalgorithm](https://developer.apple.com/documentation/security/seckeyalgorithm/2091905-eciesencryptioncofactorx963sha25)
*/
public struct Replicant
{


}
Loading

0 comments on commit eab1b7e

Please sign in to comment.