I tried to make an app that sends messages from iPhone to Bluetooth LE module. But for some reason, it gives the following error:
NSLocalizedDescription=Writing is not permitted.
Even though the types of the blePeripheral and the blePeripheral!.write are CBCharacteristicWrite.withResponse, the error says that writing is not permitted. How come the following code does not work for me?
func writeValue(data: String) {
let valueString = (data as NSString).data(using: String.Encoding.utf8.rawValue)
//change the "data" to valueString
if let blePeripheral = blePeripheral {
if let txCharacteristic = txCharacteristic {
blePeripheral.writeValue(valueString!, for: txCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
func writeCharacteristic(val: Int8) {
var val = val
let ns = NSData(bytes: &val, length: MemoryLayout<Int8>.size)
blePeripheral!.writeValue(ns as Data, for: txCharacteristic!, type: CBCharacteristicWriteType.withResponse)
}
The resource where I found the code is:
https://learn.adafruit.com/crack-the-code/communication
Related
I am making an app that activates a VPN connection based on OpenVPN, retrieves a certificate from the database, and opens a tunnel using NEPacketTunnelProvider and NetworkExtension.
I used the following repository, and now my VPN is working fine.
But the problem is that I want to allow only one app to use this VPN when enabled (WhatsApp precisely), and I want to restrict all other apps of using it.
On Android it's possible by giving the bundle identifier of the allowed apps to the PackageManager.
Can you please help me?
This is my PacketTunnelProvider class:
import NetworkExtension
import OpenVPNAdapter
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
class PacketTunnelProvider: NEPacketTunnelProvider {
lazy var vpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
// There are many ways to provide OpenVPN settings to the tunnel provider. For instance,
// you can use `options` argument of `startTunnel(options:completionHandler:)` method or get
// settings from `protocolConfiguration.providerConfiguration` property of `NEPacketTunnelProvider`
// class. Also you may provide just content of a ovpn file or use key:value pairs
// that may be provided exclusively or in addition to file content.
// In our case we need providerConfiguration dictionary to retrieve content
// of the OpenVPN configuration file. Other options related to the tunnel
// provider also can be stored there.
print("started!")
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else {
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else {
fatalError()
}
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
// configuration.settings = [
// // Additional parameters as key:value pairs may be provided here
// ]
// Uncomment this line if you want to keep TUN interface active during pauses or reconnections
// configuration.tunPersist = true
// Apply OpenVPN configuration
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try vpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
// Provide credentials if needed
if !evaluation.autologin {
// If your VPN configuration requires user credentials you can provide them by
// `protocolConfiguration.username` and `protocolConfiguration.passwordReference`
// properties. It is recommended to use persistent keychain reference to a keychain
// item containing the password.
guard let username: String = protocolConfiguration.username else {
fatalError()
}
// Retrieve a password from the keychain
// guard let password: String = ... {
// fatalError()
// }
let credentials = OpenVPNCredentials()
credentials.username = username
// credentials.password = password
do {
try vpnAdapter.provide(credentials: credentials)
} catch {
completionHandler(error)
return
}
}
// Checking reachability. In some cases after switching from cellular to
// WiFi the adapter still uses cellular data. Changing reachability forces
// reconnection so the adapter will use actual connection.
vpnReachability.startTracking { [weak self] status in
guard status == .reachableViaWiFi else { return }
self?.vpnAdapter.reconnect(afterTimeInterval: 5)
}
// Establish connection and wait for .connected event
startHandler = completionHandler
vpnAdapter.connect(using: packetFlow)
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
vpnAdapter.disconnect()
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
// protocol if the tunnel is configured without errors. Otherwise send nil.
// `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
// you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
// send `self.packetFlow` to `completionHandler` callback.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (Error?) -> Void) {
// In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers
// send empty string to NEDNSSettings.matchDomains
networkSettings?.dnsSettings?.matchDomains = [""]
// Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
}
// Process events returned by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
// Handle errors thrown by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
// Handle only fatal errors
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else {
return
}
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else {
cancelTunnelWithError(error)
}
}
// Use this method to process any log message returned by OpenVPN library.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
// Handle log messages
print(logMessage)
}
}
This is the function used in my VPN View Model to start a tunnel:
func configureVPN(serverAddress: String, username: String, password: String) {
var configData:Data = Data.init()
self.getCertificate{certificate in
configData = certificate!
guard
//If we want to read from a file
// let configData = self.readFile(name: "vtest2"),
let providerManager = self.providerManager
else {
return
}
self.providerManager?.loadFromPreferences { error in
if error == nil {
let tunnelProtocol = NETunnelProviderProtocol()
tunnelProtocol.username = username
tunnelProtocol.serverAddress = serverAddress
tunnelProtocol.providerBundleIdentifier = self.providerId // bundle id of the network extension target
tunnelProtocol.providerConfiguration = ["ovpn": configData]
tunnelProtocol.disconnectOnSleep = false
providerManager.protocolConfiguration = tunnelProtocol
providerManager.localizedDescription = "Slyfone Guard" // the title of the VPN profile which will appear on Settings
providerManager.isEnabled = true
providerManager.saveToPreferences(completionHandler: { (error) in
if error == nil {
providerManager.loadFromPreferences(completionHandler: { (error) in
do {
try providerManager.connection.startVPNTunnel(options: nil) // starts the VPN tunnel.
} catch let error {
print(error.localizedDescription)
}
})
}
})
}
}
}
}
As an engineer from Apple said:
The way to do it is to use Per-App VPN. See the Per-App VPN On Demand section in the NETunnelProviderManager documentation.
With NEPacketTunnelProvider on macOS (as of 10.15.4) you can set this up yourself with NEAppRule. A very generic example of setting up Safari to trigger the VPN would be:
var perAppManager = NETunnelProviderManager.forPerAppVPN()
/* ... */
NETunnelProviderManager.forPerAppVPN().loadFromPreferences(completionHandler: { error in
precondition(Thread.isMainThread)
/* ... */
let proto = (perAppManager.protocolConfiguration as? NETunnelProviderProtocol) ?? NETunnelProviderProtocol()
proto.serverAddress = "server.vpn.com"
proto.providerBundleIdentifier = "com.perapp-vpn.macOSPacketTunnel.PacketTunnelTest"
var appRules = [NEAppRule]()
let appRule = NEAppRule(signingIdentifier: "com.apple.Safari", designatedRequirement: "identifier \"com.apple.Safari\" and anchor apple")
appRule.matchDomains = ["example.com"]
appRules.append(appRule)
perAppManager.appRules = appRules
perAppManager.isOnDemandEnabled = true
perAppManager.protocolConfiguration = proto
perAppManager.isEnabled = true
perAppManager.localizedDescription = "Testing Per-App VPN"
self.perAppManager.saveToPreferences { saveError in
/* Proceed to connect */
}
})
That was a very generic case and forPerAppVPN() is only available on macOS. A more real-world case world case for iOS would be to create this process through MDM. That entire flow is explained in the documentation I mentioned previously. I would start by just creating a configuration profile in Configurator 2 and testing it out.
No idea if it works on OpenVPN
I have been trying to implement encryption using CommonCrypto library in swift 4.2. But no luck, ending up with some unknown error.
Somebody please look at this code and help me.
func encrypty(data value: String) -> EncryptionResult {
guard var messageData = value.data(using: .utf8), var key = getSecretkey()?.data(using: .utf8) else {
return EncryptionResult.failure
}
//iv ata
guard let ivData = generateRandomBytes(of: Int32(SecurityConstants.blockSize))?.data(using: .utf8) else {
return EncryptionResult.failure
}
//output
var outputData = Data(count: (messageData.count + SecurityConstants.blockSize + ivData.count))
var localOutput = outputData
//output length
var outputLength: size_t = 0
//encyrption
let status = key.withUnsafeBytes { keyBytes in
messageData.withUnsafeBytes { messageBytes in
localOutput.withUnsafeMutableBytes { mutableOutput in
ivData.withUnsafeBytes { ivDataBytes in
CCCrypt( CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES128),
CCOptions(kCCOptionPKCS7Padding),
keyBytes,
key.count,
ivDataBytes,
messageBytes,
messageData.count,
mutableOutput,
outputData.count,
&outputLength)
}
}
}
}
guard status == Int32(kCCSuccess) else {
logError("Error in encryption")
return EncryptionResult.failure
}
outputData.count = outputLength
return EncryptionResult.success(value: outputData.base64EncodedString())
}
Error -4310 is kCCKeySizeError (see CommonCryptoError.h). That means your key is not the right size.
Looking at this code, this in particular is very suspicious:
getSecretkey()?.data(using: .utf8)
If a key is decodable as UTF-8, it's not a proper key. You seem to have the same problem with your IV. I suspect that generateRandomBytes() does not quite do what it says it does. It's also not going to be possible to decrypt this data because you throw away the random IV (which the decryptor will require). You create room for it in the output (which is good), but you never write it.
I am getting the opposite value of a boolean when using .boolValue
I have this code.
let _ = channel.bind(eventName: "like-unlike-cake", callback:
{(data:Any?)->Void in
let likedCake = JSON(data!)["like"]
print("liked cake: \(likedCake)")
print("Boolean: \(likedCake["liked_by_current_user"].boolValue)")
liked cake: {
"liked_by_current_user" : true
}
}
Boolean: false
But when I use .bool it gives me nil. Does someone help me on this.
UPDATE This would be the json I want to fetch
{
"like": {
"id": "66642122-7737-4eac-94d2-09c9a35cbef8",
"liked_by_current_user": true
}
}
Try the following:
let _ = channel.bind(eventName: "like-unlike-cake", callback {
(data:Any?)->Void in
if let jsonData = data {
let json = JSON(jsonData)
let isLikedByCurrentUser = json["like"]["liked_by_current_user"].boolValue
print(isLikedByCurrentUser)
} else {
// your data is nil
debugPrint("data error")
}
}
also you can try to cast your data: Any into Data by replacing the line:
if let jsonData = data as? Data {
let json = JSON(data: jsonData)...
cause sometimes SwiftyJSON have problems with creating JSON objects from Any.
My current code is:
#IBAction func sendData(sender: UISwitch) {
if advertisingSwitch.on {
var parameter = NSInteger(45)
let data = NSData(bytes: ¶meter, length: 1)
if let connectedPeripheral = discoveredPeripheral {
println("========= In connected peripheral \(connectedPeripheral)")
//println("========= Send data is \(currentSendData)")
println("========= Characteristic is \(sendDataCharacteristic)")
println("========= data length is \(data.bytes)")
self.sendDataToCentral(connectedPeripheral, characteristic: sendDataCharacteristic!, data: data)
}
}
}
private func sendDataToCentral(peripheral: CBPeripheral, characteristic: CBCharacteristic, data: NSData) {
println("data is \(data)")
peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithoutResponse)
println("writed characteristic \(characteristic)")
}
When I checked the Peripheral, it is connected showing:
BPeripheral: 0x1700ee980, identifier = E2377588-84CB-87ED-570A-B51614287B3C, name = TAv22u-FDF1, state = connected
The characteristic is getting from service scan with known UUID. I am sure the characteristic I got has function "write", which is
<CBCharacteristic: 0x174086090, UUID = FFE9, properties = 0x8, value = (null), notifying = NO>
after I executing function:
peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithoutResponse)
The value in characteristic is not changing. I don't know where did I get wrong.
peripheral.writeValue(data, forCharacteristic: characteristic, type:
CBCharacteristicWriteType.WithResponse)
try .withResponse and check what is response from peripheral.
I use this piece of code but the reload table always seems to load directly after the alamofire request instead of filling the data first. The braces are put in the correct way but it still loads Step 3 before Step 2.
Output:
- The filter list is not empty filling data according to filters.
- step 1
- step 3
- step 2: found 35 houses for Haarlem
- step 2: found 100 houses for Amsterdam
println("The filter list is not empty filling data according to filters.")
if let castedFilters = filters as? [Filter] {
println("step 1")
for filter in castedFilters{
var parameters : [String : NSObject] = ["apisleutel": "#########", "module": "Objecten", "get": "Huur", "plaats": filter.plaats, "pt": filter.maximumprijs, "pv": filter.minimumprijs, "wov": filter.oppervlakte, "ka": filter.kamers, "output": "json"]
self.makeCall(parameters) { responseObject, error in
let json = JSON(responseObject!)
/**/
let count: Int? = json["Response"]["objecten"]["object"].array?.count
if((count) != nil)
{
println("step 2: found \(count!) houses for "+filter.plaats)
if let ct = count {
for index in 0...ct-1 {
//Adding house to houses array
var adres = json["Response"]["objecten"]["object"][index]["adres"].string
let newHouse = House(straat: adres!)
self.houses.append(newHouses)
}
}
}
else
{
let alert = UIAlertView()
alert.title = "Fout"
alert.message = "No houses found, consider changing the filters."
alert.addButtonWithTitle("Ok")
alert.show()
}
return
}
}
println("step 3")
tableView.reloadData()
}
Call function
func makeCall(parameters: [String : NSObject], completionHandler: (responseObject: NSDictionary?, error: NSError?) -> ()) {
var heap: NSDictionary
Alamofire.request(.GET, "http://www.huizenzoeker.nl/api/v2/", parameters: parameters)
.responseJSON { request, response, responseObject, error in
completionHandler(responseObject: responseObject as? NSDictionary, error: error)
}
}