Preface
iOS 11.0 can read and write NFC Tags through CoreNFC, but it cannot read information from IC cards. IC cards can only be read by iOS devices after iOS 13.
I have been interested in NFC for a long time, and I also want to read the information from Suica (Japanese transportation IC card) on my own so that I can directly check the balance with my mobile phone (I know there are already several apps doing this, but I want to do it for study purposes).
There are relatively few Chinese and English resources on the Internet about Suica, so I spent a few days researching the FeliCa documentation and implemented it with CoreNFC
.
This article will start with the NFC protocol, then discuss FeliCa
, which is widely used in Japanese transportation IC cards, and finally, the implementation in Swift.
While I have just started learning Swift, there may be some places that are not well-written. Feel free to reach out to me on Twitter.
What is NFC?
NFC (Near Field Communication) is a communication protocol, also known as RFID wireless communication technology.
This protocol mainly defines:
- Communication agreement: how to communicate between the sender and receiver
- Data exchange: how to exchange data between the sender and receiver
While it is possible to write data into a card (if you have the key), this article will only focus on how to read
the data in FeliCa.
The Problems Solved by NFC
In wireless communication, we can use Bluetooth, WiFi, etc. to communicate, but the biggest problems are related to security
and pairing
.
Bluetooth requires devices to be paired before communicating with each other. Pairing might take time and the communication process is more complicated.
If a reader can detect card information from 20 meters and directly request payment, there will be significant problems.
Therefore, in NFC, the detection distance is usually within a few centimeters. This not only ensures security but also reduces noise interference.
FeliCa
FeliCa is an IC card technology developed by Sony
in 2001. Compared to NFC Type-A and Type-B, it is faster. The reason for its speed might be the terrible commute traffic in Japan.
Youyou card in Taiwan is an IC card made by Mifare, designed by Philips. At the same time, Mifare is also a non-contact IC card widely used around the world.
Currently, only FeliCa is widely used in Japan.
FeliCa
is really fast. If you use public transportation in Japan, you should notice that you don't have to stop when entering the platform.
It is currently widely used in transportation IC cards such as Suica, ICOCA, はやかけん, etc. All of these cards use FeliCa
technology.
Not only in transportation, but you can also use your transportation IC card to make payments at convenience stores.
Data Security
In NFC, some data can be read, some cannot, and some cards need to be decrypted with the correct key to read and write data.
Although Android and iOS can now read NFC cards, if you want to modify the balance and other data, you must have the correct key.
FeliCa Architecture
In FeliCa's architecture, it can be divided into two major parts: private domain (プライベート) and common domain.
The common domain stores some information that can be read, while the private domain stores some information such as personal data or control balances, which must be encrypted and decrypted to operate.
In the common field, it can be divided into several parts:
- System: indicates the unit of the card. Each card will have a system code (2 bytes). According to the law of JIS 6319-4, this value will be between FE00 ~ AA00. Cards like Suica have a system code of
0003
(this value is important, we'll use it later). - Area: Contains information such as storage space, the number of blocks stored by each service, and so on.
- Service: Stores data in blocks for external access. Each service will have a service code (2 bytes), such as the service code of the entry record
090f
. Services can be divided into random service, cyclic service, and pass service. - Block: Where data is stored, each block is 16 bytes, and the number of blocks required will vary depending on the service.
If you only want to read the information inside the card, you will encounter two parts: service
and block
.
Service Type
As mentioned earlier, services are divided into random, cyclic, and pass, mainly distinguished by data access.
- Random Service: data that can be freely read and written, determined by the manufacturer.
- Cyclic Service: A place where you can store records like historical data.
- Pass Service: A place to manage things like balances and deductions.
Command
There are many types of instructions in [FeliCa](http://www.proxmark.org/files/Documents/13.56 MHz - Felica/card_usersmanual_2.0.pdf), which can be found in the [FeliCa document](http://www.proxmark.org/files/Documents/13.56 MHz - Felica/card_usersmanual_2.0.pdf). To read FeliCa data, you need several commands:
- Polling
- Request Service
- Read without encryption
Each command has a corresponding request packet, which specifies what should be in the request. This part already provides corresponding functions in CoreNFC
, so we don't have to worry about that.
Communication Process in FeliCa
- Capture cards with the polling command.
- Use the Request Service command and pass in the service code list. The card will confirm whether the service code is correct or readable. If the service does not exist or there is an error, it will return
0xFFFF
. - Use the Read without encryption command and pass in the service code to return the corresponding data in blocks (up to 16 services).
- This command will return status1 and status2. If both are 0, it means there is no problem.
- If they are not 0, it means there is an error. You can find the error code status in the FeliCa documentation.
iOS Implementation
You can refer to the examples on the Apple Developer website to find out how to read NFC Tags. Here, we are focusing on how to read FeliCaTag
.
To implement NFC, you must first have a physical iPhone. There is no way to read NFC in the simulator.
You can use NFCTagReaderSession.isReadingAvailable
to determine if NFC data is readable on your device.
5]) >> 5 & 0b1111 // get month bit let date = data[5] & 0b11111 // get date bit
let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" formatter.locale = Locale(identifier: "en_US_POSIX") return formatter.date(from: "20(year)-(month)-(date)")!
## Demo
You can see the source code on [GitHub](https://github.com/kjj6198/nfc-reader).
## Thought
Although the information was successfully read, some numbers are different from what I expected.
For example, the station code has not been found even after searching for a long time on the Internet. I don't even know if I parsed the station code correctly.
Although it is said that the history record can get 20 blocks, some cards will fail to read because of this. The safer range is 10 (still testing).
Looking at the records inside (my card is a regular ticket), it seems that not every entry and exit will be recorded. If you want to use cards to collect your own inbound and outbound information, I'm afraid it is not so easy to use (such as visualization).
Regarding FeliCa reading, on iOS, Japanese developers have written a library called [TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) which is quite easy to use. If you want to use it directly, you can refer to it. In addition to SUICA, it also supports quite a few types of IC cards. Even the driver's license can be read.
When I saw the CoreNFC SDK, I wanted to try reading FeliCa information, but there are not many Chinese resources on the Internet.
There are quite a lot of Japanese implementations, but I can only see the service code, blockList, etc. without knowing their meaning. So it took a day to read the FeliCa documentation and try to implement it myself.
Also, quite troublesome on the Swift conversion of another type, such as the Data converted into UInt, etc. I found this code on the Internet, but I don't really understand what it is doing XD. I only know the way of conversion using `UInt(bytes: Data)`.
```swift
import Foundation
extension FixedWidthInteger {
init(bytes: UInt8...) {
self.init(bytes: bytes)
}
init<T: DataProtocol>(bytes: T) {
let count = bytes.count - 1
self = bytes.enumerated().reduce(into: 0) { (result, item) in
result += Self(item.element) << (8 * (count - item.offset))
}
}
}
It was a long journey to read all of this, isn't it? I hope you can understand how NFC works, what FeliCa is, and more importantly, how to use it in combination with CoreNFC in Swift!