NDEF Record Structure
The HaLo URL NDEF record contains information about the chip's unique public keys as well as information about the last issued command and last returned response for that command. There is a proprietary interface allowing the user to issue arbitrary commands against the chip using the NDEF layer.
Parameters are expected to change in version c4
and higher.
Example URL
v
parameter
The v
parameter is a single byte indicating the version of the chip.
static
parameter
The static
parameter contains the public key information corresponding to the secp256k1 keys stored within the chip.
From the factory this field contains two public keys corresponding to the key slots #1 and #2. If the HaLo is instructed to generate the key in slot #3, the corresponding public key will be added to this field as well.
The structure is encoded as follows:
[key size - 1 byte] [public key - N bytes] [key size - 1 byte] [...]
Although current HaLo chips store public keys in an uncompressed format starting with the 04
byte, this may change and as such you should always use the key size
byte to determine the nature of the key.
The parsing algorithm for that byte should be implemented as follows:
- Set
i
= 1. - Read 1 byte from the buffer and store it in the
key_size
variable. If the read has failed because there are no bytes left in the buffer - stop algorithm. - If
key_size
is equal to 0, stop the algorithm. - Read
key_size
number of bytes from the buffer and store them aspublic_key[i]
. - Increment
i
by 1. - Return to step 2 (and continue until there are no bytes remaining in the buffer).
Example input:
4104CB18C1B56949A13EFA4468F50D81006BCD2B9009E3F7B83AF50F9B474537405FF34B6362F43ECA60F28FDC1ECC4488E6DE1A19C638A7E3F0D92ABD931A61AB434104295CA8CB0476091B242D8C990F9E34638FF7969D83014BCD4F9BD8B78D0AC25CBEA6A6CF5BBECD88CEBE994F6070E708518D0D9393968008C946B42E16987DB3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Example parsed result:
- [1]
04CB18C1B56949A13EFA4468F50D81006BCD2B9009E3F7B83AF50F9B474537405FF34B6362F43ECA60F28FDC1ECC4488E6DE1A19C638A7E3F0D92ABD931A61AB43
- [2]
04295CA8CB0476091B242D8C990F9E34638FF7969D83014BCD4F9BD8B78D0AC25CBEA6A6CF5BBECD88CEBE994F6070E708518D0D9393968008C946B42E16987DB3
If the size of the element is 65 - the key is a secp256k1 uncompressed public key. If the size of the element is 33 - the key is a secp256k1 compressed public key. The count of array indexes determine the number of corresponding key slots.
latch
parameters
Later editions of the chip (version c2
and higher) noted with the also contain two one-time writeable 32 byte slots, called latches
.
Once a non-zero buffer is written to any of the slots, that slot is locked against any further modification. The latch1
and latch2
parameters contain hex encoded values of the corresponding latch slots.
cmd
parameter
The cmd
or command field is always composed as follows:
command code
: 2 bytes (always: 8102).challenge
: 32 bytes (e.g. 00000009DC18…), which consists of:- Big-endian UInt32: incremental counter, automatically incremented by 1 on each tap.
- Random 28 bytes: randomly generated by the tag on each tap.
- Zero-padding: 1 byte (always: 00).
Example cmd
field value:
8102 00000009DC18D3F30E06E9A3C10D41EDDA09404654C502F70E67E80D781876D8 00
res
parameter
The res
or response field is always composed as follows:
- The DER encoded ECDSA raw signature created over the
challenge
. - Zero-padding to the length of 76 bytes.
Note that no SHA1/SHA256 hashing is applied before the challenge
is signed.
Example res
field value:
3045022100FCAAEFFCAB25E4B78BABB68C733DD2827FDC76E927F87C50A392E257583797DD02201A9CA7012FF3CC1C3380E1832BC733C69D743445A8DA4F664095F134927DA917 0000000000
The exact length of the signature is variable. The length can be found by reading 2nd byte of res
field and adding 2 to it.
In the example above: 0x45 + 2 = signature length is 71 bytes.
Note that you should discard excess zero bytes before validating the signature with a cryptographic library.