CryptoKitとCoreBluetoothを利用したスマートキー開発/iOSDC2020

820c51c7e79c45286e41a41a2a721c0f?s=47 saiten
September 21, 2020

 CryptoKitとCoreBluetoothを利用したスマートキー開発/iOSDC2020

WWDC20ではAppleからCarKeyが発表され、iOSの標準機能としてiPhoneをクルマの鍵として利用できるようになりました。とはいえ、CarKeyは現在BMWの最新機種でしか利用することができません。

CarKeyを利用しなくてもiOS13から追加されたCryptoKitを利用すると様々な暗号アルゴリズムを簡単に利用することができ、CoreBluetoothと組み合わせることで簡単にiPhoneを利用したスマートキーシステムを実現することができます。

このトークでは、リモコンキーすらない古いクルマにArduinoとiPhoneを利用したスマートキーシステムを構築する過程で得た様々な知見を共有します。

820c51c7e79c45286e41a41a2a721c0f?s=128

saiten

September 21, 2020
Tweet

Transcript

  1. !TBJUFO $PSF#MVFUPPUIͱ$SZQUP,JUΛ ར༻ͨ͠εϚʔτΩʔ։ൃ J04%$ -5

  2. ࣗݾ঺հ w 5PNPBLJ4IJCBUB w (JU)VC 5XJUUFSTBJUFO w %F/"40.10.PCJMJUZ w ΧʔγΣΞαʔϏεʮ"OZDBʯͷ

    J04ΞϓϦ։ൃΛ͍ͯ͠·͢
  3.  Λങ͍·ͨ͠

  4. None
  5. ͋ͱͰ௥Ճ 伴ͱυΞͷࣸਅ

  6. None
  7. ෺ཧ͕ͳ͍ͱυΞ͕։͔ͳ͍

  8. ͪΐͬͱෆศ

  9. Ͱ։͚ΒΕΔΑ͏ʹ͠Α͏

  10. Ͱಈ࡞͢ΔεϚʔτΩʔΛ ࡞Γ·ͨ͠ w ΞϓϦ͔ΒΫϧϚͷݤͷࢪৣɾղৣૢ࡞ w ۙ͘ʹΫϧϚΛݟ͚ͭͨ৔߹ʹ௨஌͢Δػೳ

  11. ंࡌ୺຤ "SEVJOP ߏ੒ਤ ᶆݤૢ࡞ ᶃΞυόλΠζύέοτૹ৴ ᶄεΩϟϯɾ઀ଓ ᶅݤૢ࡞ࢦࣔ ΞϓϦ ΫϧϚ

  12. ंࡌ୺຤ "SEVJOP ߏ੒ਤ ᶆݤૢ࡞ ᶃΞυόλΠζύέοτૹ৴ ᶄεΩϟϯɾ઀ଓ ᶅݤૢ࡞ࢦࣔ ΞϓϦ ΫϧϚ

  13. $PSF#MVFUPPUI w J04͔Βར༻ՄೳͳެࣜϑϨʔϜϫʔΫ w %FMFHBUFϕʔεͰ࣮૷͢Δඞཁ͕͋ΓɺॲཧͷྲྀΕ͕௥͍ͮΒ͍

  14. 3Y#MVFUPPUI,JU w HJUIVCDPN1PMJEFB3Y#MVFUPPUI,JU w 3Y4XJGUϕʔεͰ$PSF#MVFUPPUIΛѻ͑ΔϥΠϒϥϦ w #MVFUPPUIσόΠεΛεΩϟϯͯ͠઀ଓ͢Δ·ͰͷॲཧΛ਺ߦͰهड़Ͱ͖Δ

  15. class CentralManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { var manager: CBCentralManager? =

    nil func start() { manager = CBCentralManager(delegate: self, queue: nil) } // MARK: - CBCentralManagerDelegate func centralManagerDidUpdateState(_ central: CBCentralManager) { guard central.state == .poweredOn else { return } central.scanForPeripherals(withServices: [smartLockServiceUUID]) } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { central.stopScan() central.connect(peripheral) } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices([smartLockServiceUUID]) self.connectedPeripheral = peripheral } // MARK: - CBPeripheralDelegate func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { guard let service = peripheral.services?.first(where: { $0.uuid == smartLockServiceUUID }) else { return } peripheral.discoverCharacteristics([smartLockCharacteristicUUID], for: service) } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristic = service.characteristics?.first(where: { $0.uuid == smartLockCharacteristicUUID }) else { return } peripheral.setNotifyValue(true, for: characteristic) } } Πϕϯτຖʹϝιου͕ผΕ͓ͯΓɺ ॲཧͷྲྀΕ͕௥͍ͮΒ͍
  16. let manager = CentralManager() manager .observeState() .startWith(manager.state) .filter { $0

    == .poweredOn } .flatMap { _ in manager.scanForPeripherals(withServices: [smartLockServiceUUID]) } .take(1) .flatMap { $0.peripheral.establishConnection() } .flatMap { $0.discoverServices([smartLockServiceUUID]) } .flatMap { Observable.from($0) } .flatMap { $0.discoverCharacteristics([smartLockCharacteristicUUID]) } ॲཧΛ࿈݁ͯ͠ॻ͘͜ͱ͕Ͱ͖ɺ ॲཧͷྲྀΕΛ௥͍΍͍͢
  17. ηΩϡϦςΟରࡦ w ंࡌثͷଘࡏ͸؆୯ʹ୳͠ग़͢͜ͱ͕Ͱ͖Δɻ w ंࡌثʹ୭Ͱ΋઀ଓͰ͖ͯ͠·͏ͨΊɺૢ࡞ίϚϯυ͕෼͔ͬͯ͠·͑͹ ୭Ͱ΋ΫϧϚͷυΞΛૢ࡞Ͱ͖ͯ͠·͏ w ૢ࡞ίϚϯυ͸#-&4OJGGFSΛ࢖͑͹ղੳͰ͖Δ ࣄલʹར༻Ͱ͖ΔϢʔβΛݶఆ͠ɺ ͦͷϢʔβ͔Βͷૢ࡞ͷΈड͚෇͚ΔΑ͏ʹ͢Δ

  18. ղৣγʔέϯε ंࡌ୺຤ʹ઀ଓ νϟϨϯδίʔυΛऔಘ νϟϨϯδίʔυ ʮૢ࡞ίϚϯυʴνϟϨϯδίʔυʯ ɹͷ4)"ϋογϡΛੜ੒͠ɺ ɹൿີݤ &$%4" Ͱॺ໊Λ࡞੒ ʮૢ࡞ίϚϯυʴνϟϨϯδίʔυʯ

    ɹͱʮॺ໊ʯΛૹ৴ νϟϨϯδίʔυ͕༗ޮ͔֬ೝ͠ɺ ࣄલʹొ࿥͞Εͨެ։ݤΛར༻ͯ͠ ॺ໊Λݕূ ղৣ׬ྃίʔυΛฦ͢ ໰୊ͳ͚Ε͹ ΞϯϩοΫ৴߸ Λૹ৴
  19. ղৣγʔέϯε ंࡌ୺຤ʹ઀ଓ νϟϨϯδίʔυΛऔಘ νϟϨϯδίʔυ ʮૢ࡞ίϚϯυʴνϟϨϯδίʔυʯ ɹͷ4)"ϋογϡΛੜ੒͠ɺ ɹൿີݤ &$%4" Ͱॺ໊Λ࡞੒ ʮૢ࡞ίϚϯυʴνϟϨϯδίʔυʯ

    ɹͱʮॺ໊ʯΛૹ৴ νϟϨϯδίʔυ͕༗ޮ͔֬ೝ͠ɺ ࣄલʹొ࿥͞Εͨެ։ݤΛར༻ͯ͠ ॺ໊Λݕূ ղৣ׬ྃίʔυΛฦ͢ ໰୊ͳ͚Ε͹ ΞϯϩοΫ৴߸ Λૹ৴
  20. $SZQUP,JU w J04͔Β௥Ճ͞Εͨϋογϡੜ੒ɺ҉߸Խɺॺ໊౳ͷ ॲཧΛఏڙ͢ΔެࣜϥΠϒϥϦ w ͦͷͨΊJ04Ҏ߱ରԠͰͳ͚Ε͹ར༻Ͱ͖ͳ͍ w ͜Ε·ͰͷެࣜϥΠϒϥϦͰ͋ͬͨ$PNNPO$SZQUP΍ 4FDVSJUZGSBNFXPSLͰ͸ϙΠϯλΛҙࣝ͢Δඞཁ͕͕͋ͬͨɺ $SZQUP,JU͸4XJGU࣮૷ͷͨΊɺΑΓॻ͖΍͘͢ͳ͍ͬͯΔ

  21. 4)"ϋογϡੜ੒ $PNNPO$SZQUP let payload = "\(operationCode)_\(challengeCode)" var digestData = Data(count:

    Int(CC_SHA256_DIGEST_LENGTH)) _ = digestData.withUnsafeMutableBytes { mutableBytes in CC_SHA256(payload, CC_LONG(payload.utf8.count), mutableBytes.bindMemory(to: UInt8.self).baseAddress) }
  22. 4)"ϋογϡੜ੒ $SZQUP,JU let payload = "\(operationCode)_\(challengeCode)" let digest = SHA256.hash(data:

    payload.data(using: .utf8)!) let digestData = Data(digest)
  23. ॺ໊ 4FDVSJUZGSBNFXPSL var error: Unmanaged<CFError>? var privateKey: SecKey? var publicKey:

    SecKey? let osStatus = SecKeyGeneratePair([kSecAttrApplicationTag: "co.saiten.smartkey", kSecAttrKeyType: kSecAttrKeyTypeEC, kSecAttrKeySizeInBits: 256] as CFDictionary, &publicKey, &privateKey) guard osStatus == noErr else { return } guard SecKeyIsAlgorithmSupported(privateKey!, .sign, .ecdsaSignatureDigestX962SHA256) else { return } let signature = SecKeyCreateSignature(privateKey, .ecdsaSignatureDigestX962SHA256, digestData as CFData, &error) as Data?
  24. ॺ໊ $SZQUP,JU let privateKey = P256.Signing.PrivateKey() let signature = try?

    privateKey.signature(for: digest) let signatureData = signature?.rawRepresentation
  25. ·ͱΊ w Ͱಈ࡞͢ΔεϚʔτΩʔγεςϜΛ։ൃ͠·ͨ͠ w 3Y#MVFUPPUI,JU΍$SZQUP,JUΛར༻͢Δ͜ͱͰɺ #-&௨৴ॲཧ΍҉߸ԽॲཧΛγϯϓϧʹهड़͢Δ͜ͱ͕Ͱ͖ͨ w ΫϧϚͱ͍͏ΨδΣοτΛ͍͡Δͷ͸ͱͯ΋ָ͍͠ w ࣍͸λίϝʔλʔΛ௥Ճ͍ͨ͠

  26. DeNA Tech ͷ Twitter ΞΧ΢ϯτͰ͸ɺ
 DeNA ͷΤϯδχΞϦϯάʹؔ͢Δ
 ొஃࢿྉ΍ϒϩάΛ঺հ͍ͯ͠·͢ʂ ͥͻ Twitter

    ΛϑΥϩʔͯ͠Έ͍ͯͩ͘͞ʂ