Is it possible to create a DH symmetric key from multiple shared secrets using CryptoKit ?

Hello,


I'm currently trying (in Swift, iOS 13+) to reproduce an end to end communication mechanism inspired by apps like SignalApp or iMessage. But there is something I don't see how to do. When encrypting a new message the idea for a better forward secrecy and post-compromise security implies the use of not 1 but 3 or 4 DH shared secrets to compute the symmetric key (here is the part of the SignalApp protocol documentation that explains that part https://signal.org/docs/specifications/x3dh#sending-the-initial-message). But I don't think this is currently possible with CryptoKit since I don't see any way to combine multiple CryptoKit.SharedSecret together. Or am I missing something ?


I could use a third party library for that of course, but I would rather not if it's possible.



Thank you ! (and sorry for my english :s)

Replies

Hello,

I know the answer is too late for you but I am leaving it in case that someone finds it useful. What you've mentioned so far is correct you do combine multiple keys to create 3 or 4 shared secrets (depending on if you have the Onte time pre-keys or not). DH or Diffie-Hellman is a key agreement. And you should treat it as such by using the KeyAgreement of an appropriate Public-key cryptography curve like (P256, Curve25519,...). This will get you 3 or 4 shared secrets that you will concatenate together in one string.

Here is a rough outline:

let s1 = try self?.myIdKeyPair?.privateKey.sharedSecretFromKeyAgreement(with: otherSignedPubKey)
let s2 = try self?.oneTimeKey?.privateKey.sharedSecretFromKeyAgreement(with: otherIdPub)
let s3 = try self?.oneTimeKey?.privateKey.sharedSecretFromKeyAgreement(with: otherSignedPubKey)
let s4 = try self?.oneTimeKey?.privateKey.sharedSecretFromKeyAgreement(with: otherOneTimeKey)
         
let computedSharedSecretString = s1!.description.split(separator: " ")[1] +
                        s2!.description.split(separator: " ")[1] +
                        s3!.description.split(separator: " ")[1] +
                        s4!.description.split(separator: " ")[1]

You'd want to use this computedSharedSecretString and run it through a KDF function like HKDF and hash the output afterwards (SHA256)

func hkdf<H: HashFunction>(_ seed: Data, salt: Data, info: Data, outputSize: Int = 32, hashFunction: H) -> Data? {
  let hashByteCount = H.Digest.byteCount
  let iterations = UInt8(ceil(Double(outputSize) / Double(hashByteCount)))
  guard iterations <= 255 else {
    return nil
  }
   
  let prk = HMAC<H>.authenticationCode(for: seed, using: SymmetricKey(data: salt))
  let key = SymmetricKey(data: prk)
  var hkdf = Data()
  var value = Data()
   
  for i in 1...iterations {
    value.append(info)
    value.append(i)
     
    let code = HMAC<H>.authenticationCode(for: value, using: key)
    hkdf.append(contentsOf: code)
     
    value = Data(code)
  }

  return hkdf.prefix(outputSize)
}
  • Thanks!!

  • I opted using swift-sodium instead of having to implement crypto myself

Add a Comment