How do I access the underlying key of a SymmetricKey in CryptoKit?
Asked Answered
S

4

6

I am messing about with Apple's new CryptoKit framework on Xcode 11.0 beta 2. I want to create a SymmetricKey, then obtain the raw bytes of the key. I would like to use those bytes to create the same key, then check to make sure the keys are equal. From what I can understand in the documentation, the only way to get access to the raw bytes of a key is by using the withUnsafeBytes(_:) method. I have a Playground with the following code:

import Foundation
import CryptoKit

let key1 = SymmetricKey(size: .bits256)
key1.withUnsafeBytes { body in
  let rawKeyBytes = body.load(as: Data.self)
  let key2 = SymmetricKey(data: rawKeyBytes)
  print("Are they equal? \(key1 == key2)")
}

The output of this is Are they equal? false, so unfortunately the keys do not match. Assuming I could get these keys to match, I'm also not sure how to convert rawKeyBytes into a string in order to view it in my Playground output. Overall I'm just not very familiar with UnsafeRawBufferPointer or ContiguousBytes.

Selfmortification answered 30/6, 2019 at 21:14 Comment(0)
E
15

There's no sense to compare one key to another like you said. If you want to extract the key, use this simple lines:

let keyb64 = key.withUnsafeBytes {
    return Data(Array($0)).base64EncodedString()
}

Or remove base64EncodedString() only for Data to send to a server or put in the keychain.

Best regards

Esse answered 21/8, 2019 at 12:15 Comment(0)
H
10

I had to do this same thing and ended up making some extensions to streamline the process:

import CryptoKit
import Foundation

extension SymmetricKey {

    // MARK: Custom Initializers

    /// Creates a `SymmetricKey` from a Base64-encoded `String`.
    ///
    /// - Parameter base64EncodedString: The Base64-encoded string from which to generate the `SymmetricKey`.
    init?(base64EncodedString: String) {
        guard let data = Data(base64Encoded: base64EncodedString) else {
            return nil
        }

        self.init(data: data)
    }

    // MARK: - Instance Methods

    /// Serializes a `SymmetricKey` to a Base64-encoded `String`.
    func serialize() -> String {
        return self.withUnsafeBytes { body in
            Data(body).base64EncodedString()
        }
    }
}

And tested this like so:

import CryptoKit

func symmetricKeyTest() {
    let symmetricKey = SymmetricKey(size: .bits256)
    let serializedSymmetricKey = symmetricKey.serialize()

    guard let deserializedSymmetricKey = SymmetricKey(base64EncodedString: serializedSymmetricKey) else {
        print("deserializedSymmetricKey was nil.")
        return
    }

    print("Keys match: \(symmetricKey == deserializedSymmetricKey)")
}
Hook answered 12/9, 2019 at 18:22 Comment(0)
J
6

I don't believe you can use load on Data quite that way. But in any case, what you want looks like this:

let key1 = SymmetricKey(size: .bits256)
let key2 = key1.withUnsafeBytes { SymmetricKey(data: Data($0)) }
print("Are they equal? \(key1 == key2)")

It is generally dangerous to access a value inside its own withUnsafeBytes block (for example, accessing key1 inside key1.withUnsafeBytes). I can't remember if it's forbidden in this specific case, but as a rule you should avoid it because it leads to exclusive-access violations.

Japonica answered 25/7, 2019 at 14:20 Comment(1)
If anyone curious, the code printed Are they equal? trueConceptacle
D
-2

The following seems to work for me

import CryptoKit
import Foundation

let key1 = SymmetricKey(size: .bits256)
key1.withUnsafeBytes { ptr in
    let rawKeyBytes = Data(bytes: ptr.baseAddress!, count: ptr.count)
    let key2 = SymmetricKey(data: rawKeyBytes)
    print("Are they equal? \(key1 == key2)")
}
Damali answered 25/7, 2019 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.