How do I access the underlying key of a SymmetricKey in CryptoKit? - swift

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.

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

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)")
}

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.

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)")
}

Related

How do I apply my custom attribute in an AttributedString in SwiftUI

I have my custom attribute here, just for testing, the goal is to swap letters just to see how I can affect the string
enum SwapAttribute : AttributedStringKey {
typealias Value = String
static let name = "swap"
}
extension AttributeScopes {
struct MyTextStyleAttributes: AttributeScope {
let swap: SwapAttribute
}
}
extension AttributeDynamicLookup {
subscript<T: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeScopes.MyTextStyleAttributes, T>) -> T {
return self[T.self]
}
}
func attributesTest() {
var str = AttributedString("It's a secret")
let secret = str.range(of: "secret")!
str[secret].swap = "*"
// expect 'It's a ******'
print(str)
}
I can add my attribute, and I can see it in the .runs variable, but all of the documentation and tutorials gloss over this. How do I execute code or apply changes to the string.
(or perhaps this is intended for other things)
Custom Implementations
Custom attributes only serve as data storage, they are not displayed or processed by SwiftUI or UIKit. Additional custom behavior would have to be implemented manually.
Workaround
For behavior as described in the question, stick to extensions.
var str = AttributedString("It's a ******")
let secret = str.range(of: "******")!
str[secret].swap = "secret"
That would display "It's a ******", while still holding your "secret" data in the attributes.
To improve on that, you could create an extension:
extension AttributedString {
func swapping(_ value: String, with new: Character) -> AttributedString {
let mutableAttributedString = NSMutableAttributedString(self)
let range = mutableAttributedString.mutableString.range(of: value)
let newString = String(repeating: new, count: range.length)
// Use replaceCharacters(in:with:) to keep all original attributes
mutableAttributedString.replaceCharacters(in: range, with: newString)
// Set the "secret" data
var str = AttributedString(mutableAttributedString)
str[str.range(of: newString)!].swap = value
return str
}
}
var str = AttributedString("It's a secret")
.swapping("secret", with: "*")
print(str)
The print output:
It's a {
}
****** {
swap = secret
}

UserDefaults How can I get my own saved keys?

I tried this code but it brings me all keys saved except me. How can I get my own saved keys?
print("UD: \(UserDefaults.standard.dictionaryRepresentation().keys) \n")
Console:
The key I saved is "Ağustos Test 1".
How can I get only this key?
You cannot "get" from user defaults just the keys for user defaults entries that you created in code. What's in your user defaults is what's in your user defaults; it doesn't have any way of distinguishing "who" created a particular entry.
Knowing the keys you added is your business. Typically this information is hard-coded into your app, e.g. you have a list of constants, usually as static properties of an enum or struct. If you are creating keys dynamically, then if you need to know the names of the keys you created, storing that information is entirely up to you.
IDEALLY you would know the key or group all your keys as a child of another key but thats not what you asked for :)
A few workarounds:
1. all:
you COULD enumerate all keys in userDefaults BUT you have to be aware you'll get keys, that aren't yours...
let dict = UserDefaults.standard.dictionaryRepresentation()
for key in dict.keys {
if let value = dict[key] {
print("\(key) = \(value)")
}
}
This will print all that is in there and that includes almost a hundred apple config values. so.. no good!
2. inclusive filter
if your keys have a commonality, you could invert the filter:
import Foundation
let included_prefixes = ["myprefs.", "myprefs2."]
//my keys
UserDefaults.standard.set(1, forKey: "myprefs.int1")
UserDefaults.standard.set("str1", forKey: "myprefs2.str1")
let dict = UserDefaults.standard.dictionaryRepresentation()
let keys = dict.keys.filter { key in
for prefix in included_prefixes {
if key.hasPrefix(prefix) {
return true
}
}
return false
}
for key in keys {
if let value = dict[key] {
print("\(key) = \(value)")
}
}
3. fragile exclusive filter
So if you really dont know your keys you COULD filter them out
import Foundation
let blacklisted_prefixes = ["Country", "NS", "com.apple", "com.omnigroup", "NavPanel", "WebAutomatic", "NSTableViewDefaultSizeMode", "sks_agent", "Apple", "PayloadUUID", "PKSecure", "_HI", "AK", "ContextMenu", "MultipleSession", "CSUI"]
//my keys
UserDefaults.standard.set(1, forKey: "int1")
UserDefaults.standard.set("str1", forKey: "str1")
let dict = UserDefaults.standard.dictionaryRepresentation()
let keys = dict.keys.filter { key in
for prefix in blacklisted_prefixes {
if key.hasPrefix(prefix) {
return false
}
}
return true
}
for key in keys {
if let value = dict[key] {
print("\(key) = \(value)")
}
}
BUT this is very fragile and not really advisable!
#needsmust

withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead [duplicate]

I previously used this code in Swift 4.2 to generate an id:
public static func generateId() throws -> UInt32 {
let data: Data = try random(bytes: 4)
let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
return value // + some other stuff
}
withUnsafeBytes is deprecated on Swift 5.0. How can I solve this?
In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:
let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }
(compare How to use Data.withUnsafeBytes in a well-defined manner? in the Swift forum). Note that this requires that the memory is aligned on a 4-byte boundary. For alternatives see round trip Swift number types to/from Data.
Note also that as of Swift 4.2 you can create a random 32-bit integer simply using the new Random API:
let randomId = UInt32.random(in: .min ... .max)
On Xcode 10.2, Swift 5, using $0.load(as:) didn't work for me, both when reading from the pointer or writing to it.
Instead, using $0.baseAddress?.assumingMemoryBound(to:) seems to work well.
Example reading from the pointer buffer (code is unrelated to the question):
var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
return
}
reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}
Example writing to the buffer pointer (code is unrelated to the question):
try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
passphrase,
passphrase.utf8.count,
salt,
salt.utf8.count,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
rounds,
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
kCCKeySizeAES256)
guard status == kCCSuccess else {
throw Error.keyDerivationError
}
}
The code from the question would look like:
let value = data.withUnsafeBytes {
$0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}
In cases where the 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) warning persists, it seems like the compiler can get confused when the closure has only one line. Making the closure have two or more lines might remove the ambiguity.
One more way to fix this warning to use bindMemory(to:).
var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
return Int32(kCCMemoryFailure)
}
return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}
I got this error as I was trying to figure out a compression stream tutorial. To get it to work, I added a step of converting the raw buffer pointer to a UnsafePointer
Original code from a tutorial I was working on.
--> where input: Data
--> where stream: compression_stream
//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in
//holder
var output = Data()
//Source and destination buffers
stream.src_ptr = srcPointer //UnsafePointer<UInt8>
stream.src_size = input.count
… etc.
}
Code with a conversion to make the above code work with a valid method
return input.withUnsafeBytes { bufferPtr in
//holder
var output = Data()
//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress
//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
return output
}
//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)
// Jump back into the original method
stream.src_ptr = typedPointer //UnsafePointer<UInt8>
}

Swift 5.0: 'withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(...)

I previously used this code in Swift 4.2 to generate an id:
public static func generateId() throws -> UInt32 {
let data: Data = try random(bytes: 4)
let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
return value // + some other stuff
}
withUnsafeBytes is deprecated on Swift 5.0. How can I solve this?
In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:
let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }
(compare How to use Data.withUnsafeBytes in a well-defined manner? in the Swift forum). Note that this requires that the memory is aligned on a 4-byte boundary. For alternatives see round trip Swift number types to/from Data.
Note also that as of Swift 4.2 you can create a random 32-bit integer simply using the new Random API:
let randomId = UInt32.random(in: .min ... .max)
On Xcode 10.2, Swift 5, using $0.load(as:) didn't work for me, both when reading from the pointer or writing to it.
Instead, using $0.baseAddress?.assumingMemoryBound(to:) seems to work well.
Example reading from the pointer buffer (code is unrelated to the question):
var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
return
}
reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}
Example writing to the buffer pointer (code is unrelated to the question):
try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
passphrase,
passphrase.utf8.count,
salt,
salt.utf8.count,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
rounds,
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
kCCKeySizeAES256)
guard status == kCCSuccess else {
throw Error.keyDerivationError
}
}
The code from the question would look like:
let value = data.withUnsafeBytes {
$0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}
In cases where the 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) warning persists, it seems like the compiler can get confused when the closure has only one line. Making the closure have two or more lines might remove the ambiguity.
One more way to fix this warning to use bindMemory(to:).
var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
return Int32(kCCMemoryFailure)
}
return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}
I got this error as I was trying to figure out a compression stream tutorial. To get it to work, I added a step of converting the raw buffer pointer to a UnsafePointer
Original code from a tutorial I was working on.
--> where input: Data
--> where stream: compression_stream
//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in
//holder
var output = Data()
//Source and destination buffers
stream.src_ptr = srcPointer //UnsafePointer<UInt8>
stream.src_size = input.count
… etc.
}
Code with a conversion to make the above code work with a valid method
return input.withUnsafeBytes { bufferPtr in
//holder
var output = Data()
//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress
//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
return output
}
//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)
// Jump back into the original method
stream.src_ptr = typedPointer //UnsafePointer<UInt8>
}

Swift String to Array

I have a string in Swift that looks like this:
["174580798","151240033","69753978","122754394","72373738","183135789","178841809","84104360","122823486","184553211","182415131","70707972"]
I need to convert it into an NSArray.
I've looked at other methods on SO but it is breaking each character into a separate array element, as opposed to breaking on the comma. See: Convert Swift string to array
I've tried to use the map() function, I've also tried various types of casting but nothing seems to come close.
Thanks in advance.
It's probably a JSON string so you can try this
let string = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
let data = string.dataUsingEncoding(NSUTF8StringEncoding)!
let jsonArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: nil) as! [String]
as the type [String] is distinct you can cast it forced
Swift 3+:
let data = Data(string.utf8)
let jsonArray = try! JSONSerialization.jsonObject(with: data) as! [String]
The other two answers are working, although SwiftStudiers isn't the best regarding performance. vadian is right that your string most likely is JSON. Here I present another method which doesn't involve JSON parsing and one which is very fast:
import Foundation
let myString = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
func toArray(var string: String) -> [String] {
string.removeRange(string.startIndex ..< advance(string.startIndex, 2)) // Remove first 2 chars
string.removeRange(advance(string.endIndex, -2) ..< string.endIndex) // Remote last 2 chars
return string.componentsSeparatedByString("\",\"")
}
toArray(myString) // ["174580798", "151240033", "69753978", ...
You probably want the numbers though, you can do this in Swift 2.0:
toArray(myString).flatMap{ Int($0) } // [174'580'798, 151'240'033, 69'753'978, ...
which returns an array of Ints
EDIT: For the ones loving immutability and functional programming, have this solution:
func toArray(string: String) -> [String] {
return string[advance(string.startIndex, 2) ..< advance(string.endIndex, -2)]
.componentsSeparatedByString("\",\"")
}
or this:
func toArray(string: String) -> [Int] {
return string[advance(string.startIndex, 2) ..< advance(string.endIndex, -2)]
.componentsSeparatedByString("\",\"")
.flatMap{ Int($0) }
}
Try this. I've just added my function which deletes any symbols from string except numbers. It helps to delete " and [] in your case
var myString = "[\"174580798\",\"151240033\",\"69753978\",\"122754394\",\"72373738\",\"183135789\",\"178841809\",\"84104360\",\"122823486\",\"184553211\",\"182415131\",\"70707972\"]"
var s=myString.componentsSeparatedByString("\",\"")
var someArray: [String] = []
for i in s {
someArray.append(deleteAllExceptNumbers(i))
}
println(someArray[0]);
func deleteAllExceptNumbers(str:String) -> String {
var rez=""
let digits = NSCharacterSet.decimalDigitCharacterSet()
for tempChar in str.unicodeScalars {
if digits.longCharacterIsMember(tempChar.value) {
rez += tempChar.description
}
}
return rez.stringByReplacingOccurrencesOfString("\u{22}", withString: "")
}
Swift 1.2:
If as has been suggested you are wanting to return an array of Int you can get to that from myString with this single concise line:
var myArrayOfInt2 = myString.componentsSeparatedByString("\"").map{$0.toInt()}.filter{$0 != nil}.map{$0!}
In Swift 2 (Xcode 7.0 beta 5):
var myArrayOfInt = myString.componentsSeparatedByString("\"").map{Int($0)}.filter{$0 != nil}.map{$0!}
This works because the cast returns an optional which will be nil where the cast fails - e.g. with [, ] and ,. There seems therefore to be no need for other code to remove these characters.
EDIT: And as Kametrixom has commented below - this can be further simplified in Swift 2 using .flatMap as follows:
var myArrayOfInt = myString.componentsSeparatedByString("\"").flatMap{ Int($0) }
Also - and separately:
With reference to Vadian's excellent answer. In Swift 2 this will become:
// ...
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! [String]
} catch {
_ = error // or do something with the error
}