72 lines
2.6 KiB
Swift
72 lines
2.6 KiB
Swift
import Foundation
|
|
import CryptoKit
|
|
|
|
extension SymmetricKey {
|
|
|
|
var data: Data {
|
|
withUnsafeBytes { Data(Array($0)) }
|
|
}
|
|
|
|
var base64: String {
|
|
data.base64EncodedString()
|
|
}
|
|
|
|
var displayString: String {
|
|
data.hexEncoded.uppercased().split(by: 4).joined(separator: " ")
|
|
}
|
|
|
|
var codeString: String {
|
|
" {" +
|
|
withUnsafeBytes {
|
|
return Data(Array($0))
|
|
}.map(String.init).joined(separator: ", ") +
|
|
"},"
|
|
}
|
|
}
|
|
|
|
extension SHA256.Digest {
|
|
|
|
var hexEncoded: String {
|
|
Data(map { $0 }).hexEncoded
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
|
|
func split(by length: Int) -> [String] {
|
|
var startIndex = self.startIndex
|
|
var results = [Substring]()
|
|
|
|
while startIndex < self.endIndex {
|
|
let endIndex = self.index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex
|
|
results.append(self[startIndex..<endIndex])
|
|
startIndex = endIndex
|
|
}
|
|
|
|
return results.map { String($0) }
|
|
}
|
|
}
|
|
|
|
let protocolSalt = "CryptoKit Playgrounds Putting It Together".data(using: .utf8)!
|
|
|
|
/// Generates an ephemeral key agreement key and performs key agreement to get the shared secret and derive the symmetric encryption key.
|
|
func encrypt(_ data: Data, to theirEncryptionKey: Curve25519.KeyAgreement.PublicKey, signedBy ourSigningKey: Curve25519.Signing.PrivateKey) throws ->
|
|
(ephemeralPublicKeyData: Data, ciphertext: Data, signature: Data) {
|
|
let ephemeralKey = Curve25519.KeyAgreement.PrivateKey()
|
|
let ephemeralPublicKey = ephemeralKey.publicKey.rawRepresentation
|
|
|
|
let sharedSecret = try ephemeralKey.sharedSecretFromKeyAgreement(with: theirEncryptionKey)
|
|
|
|
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(using: SHA256.self,
|
|
salt: protocolSalt,
|
|
sharedInfo: ephemeralPublicKey +
|
|
theirEncryptionKey.rawRepresentation +
|
|
ourSigningKey.publicKey.rawRepresentation,
|
|
outputByteCount: 32)
|
|
|
|
let ciphertext = try ChaChaPoly.seal(data, using: symmetricKey).combined
|
|
let signature = try ourSigningKey.signature(for: ciphertext + ephemeralPublicKey + theirEncryptionKey.rawRepresentation)
|
|
|
|
return (ephemeralPublicKey, ciphertext, signature)
|
|
}
|