Convert Swift struct to Data bytes - ios

I want to pass bytes data to bluetooth service in a format that is expected by bluetooth firmware.
Expected data structure in c struct are:
typedef enum {
CMD_TX_INFO = 0,
CMD_DATA_REQ_START,
CMD_DATA_REQ_END,
CMD_DATA_REQ
} ble_cmd_type_t;
typedef struct ble_data_info_s
{
uint32_t data_size_bytes;
uint32_t info;
} ble_data_info_t;
typedef PACKED ( struct ble_cmd_info
{
ble_cmd_type_t cmd_type;
ble_data_info_t info;
uint8_t len;
uint8_t data[10];
}) ble_cmd_data_t;
I have converted the this to swift struct as follows:
enum BLECmdType : UInt8{
case CMD_TX_INFO = 0
case CMD_DATA_REQ_START
case CMD_DATA_REQ_END
case CMD_DATA_REQ
}
struct BLEDataInfo
{
let dataSizeBytes: UInt32
let info: UInt32
}
struct BLECmdData
{
let cmdType:BLECmdType
let info:BLEDataInfo
let len:UInt8
let data: Array<UInt8>?
}
Then i convert the swit struct object into bytes like this, i am not sure if this approach is correct? I am not getting the bytes in correct expected format:
var command = BLECmdData(cmdType:BLECmdType.CMD_DATA_REQ_START,
info: BLEDataInfo(dataSizeBytes: 100000, info: 10),
len: 1,
data: [1,2,3,4,5,6,7,8,9,10])
let bleData = Data(bytes: &command , count: MemoryLayout<BLECmdData>.stride)
Firstly is it possible to print the bytes generated for bleData object in string format so i can debug and see?
Secondly the bytes sent to BLE device are:
Bytes:
01 00 00 00 A0 86 01 00
E5 73 E0 98 0A 00 00 00
70 C5 CA 80
Firmware engineer mentioned, data should start with following:
Bytes:
01 00 A0 86 01 00
E5 73 E0 98
I am not sure if my approach to convert c struct to swift here is correct.
Looking forward for your reply.
Thanks

the result of the line
let bleData = Data(bytes: &command , count: MemoryLayout<BLECmdData>.stride)
it depends on what cpu architecture the programming you're running on. (32bits vs 64 bits, big-endian vs little-endian).
you have to ask Firmware engineer about the protocol to communicate with their devices, then you will encode the data as the format specified in the protocol. It isn't important that you store the data in a class/struct/enum etc..., the important thing is how the data is encoded.

I found a way to successfully push the data in correct format:
Set the values:
var cmdTypeCopy:UInt8 = BLECmdType.CMD_DATA_REQ_START
var imageInfo = BLEDataInfo(dataSizeBytes: 100000, info: 10)
var cmdLenCopy = 10
var cmdDataCopy:Array<UInt8> = [1,2,3,4,5,6,7,8,9,10]
Create Data:
var cmdTypeData = Data(bytes: &cmdTypeCopy, count: MemoryLayout<UInt8>.stride)
var imageInfoData = Data(bytes: &imageInfo, count: MemoryLayout<BLEImageInfo>.stride)
var cmdLenData = Data(bytes: &cmdLenCopy, count: MemoryLayout<UInt8>.stride)
var cmdDataData = Data(bytes: &cmdDataCopy, count: 10)
Then append one by one:
cmdTypeData.append(imageInfoData)
cmdTypeData.append(cmdLenData)
cmdTypeData.append(cmdDataData)
This worked perfectly and firmware got the data in correct format.

Related

PEM-encoded Elliptic Curve public key conversion iOS

In an iOS application, I receive a PEM-encoded Elliptic curve public key.
I would like to create a SecKey object from it.
This question has been very useful to get RSA key parsing to work.
But I struggle adapting it to work with an EC key.
Example working with an RSA key
var secKeyCreateError : Unmanaged<CFError>?
guard
let stringPublicKey = Data(
base64Encoded: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhT0OXGhPWpbrZBTIScIFQVooi/Qo/NyTYRnrIyZ42nksKCBeSOBu+FPOHCI5U4RUSc2cUOe83dyuKmboU2Kdc1dTq9HDAau3dhpE7VLzZKzMHay+8XW5V6kQJ2oOIGKJphsjJLDM5KxCr5etHEHE5rfrPIBZA0sgcvyT0TsavOAhr55Eu4U2fu8SefxM4CWobXKANiWbmSzzYbo2EIZrfhhe2RncwnH5kr0PMk6Q+kEcuRt58VyYoDAa7vRQvY+KDwxE81CCkIjKpJ55f4uN0/VDclXzFjK8FeOgIiH3n8KD6xqtkvmFc+M8tEJYlzdHWIRN7VoNqbn4IoevnziYhQIDAQAB"
),
let peerPublicKey = SecKeyCreateWithData(
stringPublicKey as CFData,
[
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
] as CFDictionary,
&secKeyCreateError
)
else {
NSLog("Failed to create SecKey : %#", secKeyCreateError!.takeRetainedValue().localizedDescription)
return
}
NSLog("SecKey successfully created")
Example failing with an EC key
var secKeyCreateError : Unmanaged<CFError>?
guard
let stringPublicKey = Data(
base64Encoded: "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhYvCTeKdth6ffyCKReeO7cJSfN94BfieZ/9zkE6sDFz/ZifyMkgeg7mq8XB4UYn7aSEcsnqFNswROLnU4NqVFbmGDi5wAI0jRazdskGFBf+0R/zIPozZgJOSrREMEqi7"
),
let peerPublicKey = SecKeyCreateWithData(
stringPublicKey as CFData,
[
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
] as CFDictionary,
&secKeyCreateError
)
else {
NSLog("Failed to create SecKey : %#", secKeyCreateError!.takeRetainedValue().localizedDescription)
return
}
NSLog("SecKey successfully created")
Execution returns the following logs :
[seckey] SecKeyCreate init(ECPublicKey) failed: -26275
Failed to create SecKey : The operation couldn’t be completed. (OSStatus error -50 - EC public key creation from data failed)
For information, I used https://mkjwk.org/ to generate public keys.
What else did I try
I tried to extract the DER BIT STRING using ASN1Decoder and ASN1Swift without success.
Would you have any idea of what's going on with those EC keys ?
Thanks a lot 🙏
As pointed out by CyonAlexRdx here, a SECG key must be in X9.63 format to be imported using Security Framework.
If your key is in PEM format, you may import it using CryptoKit, then map it to a Security Framework object.
Detailed explainations from eskimo on developer.apple.com forum :
Import SECG Keys with Security Framework
If you’re working with Security framework, use SecKeyCreateWithData to import an SECG key. If you have a secp256r1 public key in X9.63 format:
Apple CryptoKit can import SECG keys in three different ways:
X9.63 raw key bytes
DER encoding
PEM encoding
If you have a secp256r1 public key in X9.63 format, import it with this code:
let u = URL(fileURLWithPath: "p256-public-key.dat")
guard let keyBytes = try? Data(contentsOf: u) else {
… handle error …
}
guard let publicKey = try? P256.Signing.PublicKey(x963Representation: keyBytes) else {
… handle error …
}
print(publicKey)
// prints:
// PublicKey(impl: CryptoKit.CoreCryptoNISTCurvePublicKeyImpl<CryptoKit.P256.CurveDetails>(keyBytes: […]]))
Note I’m using secp256r1 as an example. The code in this section will work for the other SECG key types, secp384r1 and secp521r1.
If you have a secp256r1 private key in X9.63 format import it with this code:
let u = URL(fileURLWithPath: "p256-private-key.dat")
guard let keyBytes = try? Data(contentsOf: u) else {
… handle error …
}
guard let privateKey = try? P256.Signing.PrivateKey(x963Representation: keyBytes) else {
… handle error …
}
print(privateKey)
// prints:
// PrivateKey(impl: CryptoKit.CoreCryptoNISTCurvePrivateKeyImpl<CryptoKit.P256.CurveDetails>(key: CryptoKit.SecureBytes(backing: CryptoKit.SecureBytes.Backing)))
CryptoKit can also import a DER-encoded SECG key. For example, it can import the following using the init(derRepresentation:) initialiser:
% xxd -p public-key-p256.der
3059301306072a8648ce3d020106082a8648ce3d030107034200042c21f3
7049d4464afbf01813c51a4e1ef7a8101d2aa12b6a889635bc7c37e9011b
fdd54006fdebdaef0d86a6d662561347982c95276013d1c1cd2d7865aff0
23
%
% dumpasn1 -p -a public-key-p256.der
SEQUENCE {
SEQUENCE {
OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
}
BIT STRING
04 2C 21 F3 70 49 D4 46 4A FB F0 18 13 C5 1A 4E
1E F7 A8 10 1D 2A A1 2B 6A 88 96 35 BC 7C 37 E9
01 1B FD D5 40 06 FD EB DA EF 0D 86 A6 D6 62 56
13 47 98 2C 95 27 60 13 D1 C1 CD 2D 78 65 AF F0
23
}
%
% xxd -p private-key-p256.der
308187020100301306072a8648ce3d020106082a8648ce3d030107046d30
6b0201010420986a7a91cbb5f4f81636e81aaff0835a771dc66865c407a8
e84469cf6ab8a477a144034200049747e981aab8ea71a255cd9a1562858c
406006f1e418260d31e1a77f6c2b35a9a3132f232db00351d9d8003487d4
ee52847990313daa7c721f88d4bb56da91c7
%
% dumpasn1 -p -a private-key-p256.der
SEQUENCE {
INTEGER 0
SEQUENCE {
OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
}
OCTET STRING, encapsulates {
SEQUENCE {
INTEGER 1
OCTET STRING
98 6A 7A 91 CB B5 F4 F8 16 36 E8 1A AF F0 83 5A
77 1D C6 68 65 C4 07 A8 E8 44 69 CF 6A B8 A4 77
[1] {
BIT STRING
04 97 47 E9 81 AA B8 EA 71 A2 55 CD 9A 15 62 85
8C 40 60 06 F1 E4 18 26 0D 31 E1 A7 7F 6C 2B 35
A9 A3 13 2F 23 2D B0 03 51 D9 D8 00 34 87 D4 EE
52 84 79 90 31 3D AA 7C 72 1F 88 D4 BB 56 DA 91
C7
}
}
}
}
Finally, CryptoKit can import a PEM-encoded SECG. For example, it can import the following using the init(pemRepresentation:) initialiser:
% cat public-key-p256.pem
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELCHzcEnURkr78BgTxRpOHveoEB0q
oStqiJY1vHw36QEb/dVABv3r2u8NhqbWYlYTR5gslSdgE9HBzS14Za/wIw==
-----END PUBLIC KEY-----
% cat private-key-p256.pem
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmGp6kcu19PgWNuga
r/CDWncdxmhlxAeo6ERpz2q4pHehRANCAASXR+mBqrjqcaJVzZoVYoWMQGAG8eQY
Jg0x4ad/bCs1qaMTLyMtsANR2dgANIfU7lKEeZAxPap8ch+I1LtW2pHH
-----END PRIVATE KEY-----
Mapping SECG Keys between Apple CryptoKit and Security Framework
If you need to map an SECG key from Apple CryptoKit to Security framework, or vice versa, use the X9.63 format.
Imagine that you’re working in Security framework but you need to import a PEM key. SecKeyCreateWithData will not accept an SECG key in PEM format; it requires that the key be in X9.63 format. CryptoKit can import a PEM key but you want to continue using your existing Security framework code. Fortunately there’s a way out of this bind:
Import the PEM key using Apple CryptoKit.
Get the X9.63 representation.
Create the Security framework key from that.
For example, the following routine imports a PEM secp256r1 private key and returns a SecKey object:
func createSecKeyWithPEMSecp256r1Private(_ pem: String) throws -> SecKey {
let privateKeyCK = try P256.Signing.PrivateKey(pemRepresentation: pem)
let x963Data = privateKeyCK.x963Representation
var errorQ: Unmanaged<CFError>? = nil
guard let privateKeySF = SecKeyCreateWithData(x963Data as NSData, [
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
] as NSDictionary, &errorQ) else {
throw errorQ!.takeRetainedValue()
}
return privateKeySF
}
To go the other way, from Security framework to CryptoKit, call SecKeyCopyExternalRepresentation to get the X9.63 representation of the key and then create a CryptoKit value using the init(x963Representation:) initialiser.

SHA1 digest compare with String fingerprint in Swift

I have a string representation of web-site's fingerprint:
5D A8 E2 8A F7 D7 82 65 40 14 8C 8A 3C 4B 37 49 CE A4 B2 42
Then I need to match it with the certificate's hash, which I'm using CryptoKit for:
static func sha1FingerprintsForTrust(_ trust: SecTrust) -> [String] {
var sha1s: [String] = []
for index in 0..<SecTrustGetCertificateCount(trust) {
if let certificate = SecTrustGetCertificateAtIndex(trust, index) {
let der = SecCertificateCopyData(certificate) as NSData
let sha1 = Insecure.SHA1.hash(data: der)
print(sha1) //prints **SHA1 digest: 76e27ec14fdb82c1c0a675b505be3d29b4eddbbb**
}
}
return sha1s
}
how can I convert any of those to match it with the other? Either way: Insecure.SHA1.Digest into String or String into Digest to then compare them?
Side note: I am aware that SHA1 is deprecated and should not be used anymore.
As usual the answer is pretty simple:
sha1.hexString
That's the missing thing I was looking for. With that I get a String and the only thing left is to make format of those same (remove spaces and turn uppercased()) and then compare them.

Reading UIDs of NFC Cards in iOS 13

I would like to retrive the UID of MiFare cards. I'm using an iPhone X, Xcode 11 and iOS 13.
I'm aware this wasn't possible (specifically reading the UID) until iOS 13 according to this website: https://gototags.com/blog/apple-expands-nfc-on-iphone-in-ios-13/ and this guy: https://www.reddit.com/r/apple/comments/c0gzf0/clearing_up_misunderstandings_and/
The phones NFC reader is correctly detecting the card however the unique identifier is always returned as empty or nil. I can read the payload however and irrelvant to iOS but I can do this in Android (confirms the card isn't faulty or just odd)
Apple Sample Project: https://developer.apple.com/documentation/corenfc/building_an_nfc_tag-reader_app
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
if case let NFCTag.miFare(tag) = tags.first! {
session.connect(to: tags.first!) { (error: Error?) in
let apdu = NFCISO7816APDU(instructionClass: 0, instructionCode: 0xB0, p1Parameter: 0, p2Parameter: 0, data: Data(), expectedResponseLength: 16)
tag.queryNDEFStatus(completionHandler: {(status: NFCNDEFStatus, e: Int, error: Error?) in
debugPrint("\(status) \(e) \(error)")
})
tag.sendMiFareISO7816Command(apdu) { (data, sw1, sw2, error) in
debugPrint(data)
debugPrint(error)
debugPrint(tag.identifier)
debugPrint(String(data: tag.identifier, encoding: .utf8))
}
}
}
}
I'm aware these sorts of hacks: CoreNFC not reading UID in iOS
But they are closed and only apply to iOS 11 for a short time in the past.
Ok I have an answer.
tag.identifier isn't empty -- per se -- if you examine from Xcodes debugger it appears empty (0x00 is the value!). It's type is Data and printing it will reveal the length of the Data but not how it's encoded. In this case it's a [UInt8] but stored as a bag of bits, I don't understand why Apple have done it this way -- it's clunky -- I'm sure they have good reasons. I would have stored it as a type String -- after all the whole point of a high level language like Swift is to abstract us away from such hadware implementation details.
The following code will retrive the UID from a MiFare card:
if case let NFCTag.miFare(tag) = tags.first! {
session.connect(to: tags.first!) { (error: Error?) in
let apdu = NFCISO7816APDU(instructionClass: 0, instructionCode: 0xB0, p1Parameter: 0, p2Parameter: 0, data: Data(), expectedResponseLength: 16)
tag.sendMiFareISO7816Command(apdu) { (apduData, sw1, sw2, error) in
let tagUIDData = tag.identifier
var byteData: [UInt8] = []
tagUIDData.withUnsafeBytes { byteData.append(contentsOf: $0) }
var uidString = ""
for byte in byteData {
let decimalNumber = String(byte, radix: 16)
if (Int(decimalNumber) ?? 0) < 10 { // add leading zero
uidString.append("0\(decimalNumber)")
} else {
uidString.append(decimalNumber)
}
}
debugPrint("\(byteData) converted to Tag UID: \(uidString)")
}
}
}
I know you have said that it returns nil but for clarity for future readers:
Assuming it is not a Felica tag, it should be on the identifier field when it is detected:
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
if case let NFCTag.miFare(tag) = tags.first! {
print(tag.identifier as NSData)
}
}
But in your case, it's empty (see edit below). For most tags the APDU to get the UID of a tag is
0xff // Class
0xca // INS
0x00 // P1
0x00 // P2
0x00 // Le
so you could try using tag.sendMiFareCommand to send that command manually.
Edit: Response from OP, it wasn't empty but was unclear because printing Data in Swift doesn't show in console
In iOS13 I was able read the Tag.identifier value for various MIFARE family's DESfire and UltraLight tags same as #scott-condron's answer, but for various MIFARE Classic ICs (the unknown family member?) my Console shows different error types.
Perhaps private framework APIs similar to the iOS11 work-around in the hack you mentioned would be helpful in these cases, e.g. to intercept and amend the discovery polling routine, but I wouldn't know which ones or how to use them.
Below you can find some test results for MIFARE Classic 4K (emulation) tags, as also reported in this github thread and this MIFARE support thread. Following Table 6 of Application Note #10833, the Select Acknowledge (SAK) value of 0x38 of the emulation tags below translates into 0 0 1 1 1 0 0 0 for bits 8..1, i.e. bits 6, 5, and 4 are 1, and therefore these SAK values classify as Smart MX with CLASSIC 4K as per Figure 3 of Application Note #10834.
an Infineon Classic 4k Emulation successfully logs 1 tags found with the correct UID (31:9A:2F:88), ATQA (0x0200), SAK (detects 0x20, i.e. ISO 14443-4 protocol, and 0x18, i.e. MIFARE 4K, both part of the expected value: 0x38) and respective tag type (both Generic 4A and MiFare classified correctly), but then throws a Stack Error:
error 14:48:08.675369 +0200 nfcd 00000001 04e04390 -
[NFDriverWrapper connectTag:]:1436 Failed to connect to tag:
<NFTagInternal: 0x104e05cd0>-{length = 8, bytes = 0x7bad030077180efa}
{ Tech=A Type=Generic 4A ID={length = 4, bytes = 0x319a2f88}
SAK={length = 1, bytes = 0x20} ATQA={length = 2, bytes = 0x0200} historicalBytes={length = 0, bytes = 0x}}
:
error 14:48:08.682881 +0200 nfcd 00000001 04e04390 -
[NFDriverWrapper connectTag:]:1436 Failed to connect to tag:
<NFTagInternal: 0x104e1d600>-{length = 8, bytes = 0x81ad0300984374f3}
{ Tech=A Type=MiFare ID={length = 4, bytes = 0x319a2f88}
SAK={length = 1, bytes = 0x18} ATQA={length = 2, bytes = 0x0200} historicalBytes={length = 0, bytes = 0x}}
:
default 14:48:08.683150 +0200 nfcd 00000001 04e07470 -
[_NFReaderSession handleRemoteTagsDetected:]:445 1 tags found
default 14:48:08.685792 +0200 nfcd 00000001 04e07470 -
[_NFReaderSession connect:callback:]:507 NFC-Example
:
error 14:48:08.693429 +0200 nfcd 00000001 04e04390 -
[NFDriverWrapper connectTag:]:1436 Failed to connect to tag:
<NFTagInternal: 0x104e05cd0>-{length = 8, bytes = 0x81ad0300984374f3}
{ Tech=A Type=MiFare ID={length = 4, bytes = 0x319a2f88}
SAK=(null) ATQA=(null) historicalBytes={length = 0, bytes = 0x}}
:
error 14:48:08.694019 +0200 NFC-Example 00000002 802e2700 -
[NFCTagReaderSession _connectTag:error:]:568 Error
Domain=NFCError Code=100 "Stack Error" UserInfo={NSLocalizedDescription=Stack Error, NSUnderlyingError=0x2822a86c0
{Error Domain=nfcd Code=15 "Stack Error" UserInfo={NSLocalizedDescription=Stack Error}}}
an NXP SmartMX (Classic 4k emulation) with UID CF:3E:40:04 is discovered initially, but a reception error during ISO 14443-4A presence check (Proc Iso-Dep pres chk ntf: Receiption failed) continuously restarts the discovery polling until the session finally expires, possibly preventing the other SAK value 0x18 (for MIFARE 4K tag type) to be received:
error 10:44:50.650673 +0200 nfcd Proc Iso-Dep pres chk ntf: Receiption failed
:
error 10:44:50.677470 +0200 nfcd 00000001 04e04390 -
[NFDriverWrapper disconnectTag:tagRemovalDetect:]:1448 Failed to disconnect tag:
<NFTagInternal: 0x104f09930>-{length = 8, bytes = 0x07320d00f3041861}
{ Tech=A Type=Generic 4A ID={length = 4, bytes = 0xcf3e4004}
SAK={length = 1, bytes = 0x20} ATQA={length = 2, bytes = 0x0200} historicalBytes={length = 0, bytes = 0x}}
default 10:44:50.677682 +0200 nfcd 00000001 04e04390 -
[NFDriverWrapper restartDiscovery]:1953
an actual NXP Classic 4k with UID 2D:FE:9B:87 remains undetected and throws no error. The discovery polling session for this tag simply times out after 60 seconds and logs the last 128 discovery messages transmitted (Tx) and received (Rx), among which the following pattern is repeated (which does include the expected UID: 2D FE 9B 87):
error 11:42:19.511354 +0200 nfcd 1571305339.350902 Tx '21 03 07 03 FF 01 00 01 01 01 6F 61'
error 11:42:19.511484 +0200 nfcd 1571305339.353416 Rx '41 03 01'
error 11:42:19.511631 +0200 nfcd 1571305339.353486 Rx '00 F6 89'
error 11:42:19.511755 +0200 nfcd 1571305339.362455 Rx '61 05 14'
error 11:42:19.511905 +0200 nfcd 1571305339.362529 Rx '01 80 80 00 FF 01 09 02 00 04 2D FE 9B 87 01 18 00 00 00 00 2D 11'
error 11:42:19.512152 +0200 nfcd 1571305339.362734 Tx '21 06 01 00 44 AB'
error 11:42:19.512323 +0200 nfcd 1571305339.363959 Rx '41 06 01'
error 11:42:19.512489 +0200 nfcd 1571305339.364028 Rx '00 1D 79'
error 11:42:19.512726 +0200 nfcd 1571305339.364300 Rx '61 06 02'
error 11:42:19.512914 +0200 nfcd 1571305339.364347 Rx '00 00 EB 78'

IOS BLE NSData int to byte issue

I want to write values to BLE with writeValue:forCharacteristic:type:,
the value is an int value from 2000-01-01 00:00:00 to now in seconds.
int time_distance 468436137;
nsigned char *bytes=(unsigned char *)&time_distance;
NSData *da_sec=[NSData dataWithBytes: bytes length:sizeof(time_distance)];
da_sec is a9c4eb1b which is wrong, it rather should be 1bebc4a9.
a9 c4 eb 1b => 1b eb c4 a9.
this is reversed.. How can I solve this?
swift 3
var timeInterval:Int = Int(date.timeIntervalSince1970)
let data = NSData(bytes: &timeInterval, length:
MemoryLayout<Int>.size)
let sendData:Data = (data as Data)
self.selectPeripheral.writeValue(sendData, for: readWriteChar, type: .withResponse)

Constant TAO CORBA IOR

How to configure TAO corba server so that IOR string of this server generated from object_to_string is constant?
Each time the IOR string generated from object_to_string changes once server restarts. This is inconvenient since client has to update its cached server IOR string via reloading IOR file or namingservice accessing. As a result, it would be useful if server can generate a constant IOR string, no matter how many times it restarts.
My corba server is based on ACE+TAO and i do remember TAO supports constant IOR string: the IOR string each time it generates are same, and the solution is add some configurations for server. But i could not remember these configurations now.
=============================================
UPDATE:
In ACE_wrappers/TAO/tests/POA/Persistent_ID/server.cpp, i added a new function named testUniqe() which is similiar to creatPOA method. And the update file content is:
void testUniqu(CORBA::ORB_ptr orb_, PortableServer::POA_ptr poa_){
CORBA::PolicyList policies (2);
policies.length (2);
//IOR is the same even it is SYSTEM_ID
policies[0] = poa_->create_id_assignment_policy (PortableServer::USER_ID);
policies[1] = poa_->create_lifespan_policy (PortableServer::PERSISTENT);
PortableServer::POAManager_var poa_manager = poa_->the_POAManager ();
PortableServer::POA_ptr child_poa_ = poa_->create_POA ("childPOA", poa_manager.in (), policies);
// Destroy the policies
for (CORBA::ULong i = 0; i < policies.length (); ++i) {
policies[i]->destroy ();
}
test_i *servant = new test_i (orb_, child_poa_);
PortableServer::ObjectId_var oid = PortableServer::string_to_ObjectId("xushijie");
child_poa_->activate_object_with_id (oid, servant);
PortableServer::ObjectId_var id = poa_->activate_object (servant);
CORBA::Object_var object = poa_->id_to_reference (id.in ());
test_var test = test::_narrow (object.in ());
CORBA::String_var ior = orb_->object_to_string(test.in());
std::cout<<ior.in()<<std::endl;
poa_->the_POAManager()->activate();
orb_->run();
}
int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
try
{
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv);
int result = parse_args (argc, argv);
CORBA::Object_var obj =
orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var root_poa =
PortableServer::POA::_narrow (obj.in ());
PortableServer::POAManager_var poa_manager =
root_poa->the_POAManager ();
testUniqu(orb.in(), root_poa.in());
orb->destroy ();
}
catch (const CORBA::Exception& ex)
{
ex._tao_print_exception ("Exception caught");
return -1;
}
return 0;
}
The problem is that the output server IORs are still different once restart. I also compared this code to the one in Page 412(Advance Corba Programming), but still fail..
///////////////////////////////////
UPDATE:
With "server -ORBListenEndpoints iiop://:1234 > /tmp/ior1", the generated two IORs are:
IOR:010000000d00000049444c3a746573743a312e300000000001000000000000007400000001010200150000007368696a69652d5468696e6b5061642d543431300000d2041b00000014010f0052535453f60054c6f80c000000000001000000010000000002000000000000000800000001000000004f41540100000018000000010000000100010001000000010001050901010000000000
IOR:010000000d00000049444c3a746573743a312e300000000001000000000000007400000001010200150000007368696a69652d5468696e6b5061642d543431300000d2041b00000014010f0052535468f60054da280a000000000001000000010000000002000000000000000800000001000000004f41540100000018000000010000000100010001000000010001050901010000000000
The result for tao_catior for ior1 and ior2:
ior1:
The Byte Order: Little Endian
The Type Id: "IDL:test:1.0"
Number of Profiles in IOR: 1
Profile number: 1
IIOP Version: 1.2
Host Name: **
Port Number: 1234
Object Key len: 27
Object Key as hex:
14 01 0f 00 52 53 54 53 f6 00 54 c6 f8 0c 00 00
00 00 00 01 00 00 00 01 00 00 00
The Object Key as string:
....RSTS..T................
The component <1> ID is 00 (TAG_ORB_TYPE)
ORB Type: 0x54414f00 (TAO)
The component <2> ID is 11 (TAG_CODE_SETS)
Component length: 24
Component byte order: Little Endian
Native CodeSet for char: Hex - 10001 Description - ISO8859_1
Number of CCS for char 1
Conversion Codesets for char are:
1) Hex - 5010001 Description - UTF-8
Native CodeSet for wchar: Hex - 10109 Description - UTF-16
Number of CCS for wchar 0
ecoding an IOR:
//ior2
The Byte Order: Little Endian
The Type Id: "IDL:test:1.0"
Number of Profiles in IOR: 1
Profile number: 1
IIOP Version: 1.2
Host Name: **
Port Number: 1234
Object Key len: 27
Object Key as hex:
14 01 0f 00 52 53 54 68 f6 00 54 da 28 0a 00 00
00 00 00 01 00 00 00 01 00 00 00
The Object Key as string:
....RSTh..T.(..............
The component <1> ID is 00 (TAG_ORB_TYPE)
ORB Type: 0x54414f00 (TAO)
The component <2> ID is 11 (TAG_CODE_SETS)
Component length: 24
Component byte order: Little Endian
Native CodeSet for char: Hex - 10001 Description - ISO8859_1
Number of CCS for char 1
Conversion Codesets for char are:
1) Hex - 5010001 Description - UTF-8
Native CodeSet for wchar: Hex - 10109 Description - UTF-16
Number of CCS for wchar 0
The diff result is:
< 14 01 0f 00 52 53 54 53 f6 00 54 c6 f8 0c 00 00
---
> 14 01 0f 00 52 53 54 68 f6 00 54 da 28 0a 00 00
19c19
< ....RSTS..T................
---
> ....RSTh..T.(..............
Similar diff result is:
< 14 01 0f 00 52 53 54 62 fd 00 54 2c 9a 0e 00 00
---
> 14 01 0f 00 52 53 54 02 fd 00 54 f9 a9 09 00 00
19c19
< ....RSTb..T,...............
---
> ....RST...T................
The difference is in ObjectKey.
============================================
update:
Instead of using above code, i find a better solution with helper TAO_ORB_Manager which is used NamingService and TAO/examples/Simple. TAO_ORB_Manager encapsulates API and generate persistent IORs, as example code in Simple.cpp:
if (this->orb_manager_.init_child_poa (argc, argv, "child_poa") == -1){
CORBA::String_var str =
this->orb_manager_.activate_under_child_poa (servant_name,
this->servant_.in ());
}
This is some description for TAO_ORB_Manager:
class TAO_UTILS_Export TAO_ORB_Manager
{
/**
* Creates a child poa under the root poa with PERSISTENT and
* USER_ID policies. Call this if you want a #a child_poa with the
* above policies, otherwise call init.
*
* #retval -1 Failure
* #retval 0 Success
*/
int init_child_poa (int &argc,
ACE_TCHAR *argv[],
const char *poa_name,
const char *orb_name = 0);
/**
* Precondition: init_child_poa has been called. Activate <servant>
* using the POA <activate_object_with_id> created from the string
* <object_name>. Users should call this to activate objects under
* the child_poa.
*
* #param object_name String name which will be used to create
* an Object ID for the servant.
* #param servant The servant to activate under the child POA.
*
* #return 0 on failure, a string representation of the object ID if
* successful. Caller of this method is responsible for
* memory deallocation of the string.
*/
char *activate_under_child_poa (const char *object_name,
PortableServer::Servant servant);
...................
}
After build, I can get what i want with -ORBListenEndpoints iiop://localhost:2809 option. Thanks #Johnny Willemsen help
You have to create the POA with a persistent lifespan policy, see ACE_wrappers/TAO/tests/POA/Persistent_ID as part of the TAO distribution.

Resources