Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ieee802154_security: add internal descriptor organization for replay protection #18575

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

fabian18
Copy link
Contributor

Contribution description

At the RIOT summit I got the impression, that there is a big interest in extending the gnrc IEEE 802.15.4 security implementation.
I remembered that I had a branch lying around, where I implemented the replay protection, but I did not release it as a PR because I was not satisfied with the quality, nor was I sure if I correctly understood the descriptor relations and lookup procedures from the specification. But after the summit I gave it another look.

This PR does not concern dynamic key management in the sense of key generation, distribution and update. But it implements a constrained variant of the internal descriptor organization, explained in the specification, to keep track of peer devices and key selection, to realize replay protection. I think the descriptors are more of a prerequisite for IEEE 802.15.4 key management.

Additionally, I wrote a shell tool iwpansec that can be used to manage keys and devices statically for testing.
Regarding LL security, it shall serve the same purpose as wpan-tools in Linux. But of course it is more limited than that. Ideally, devices should be discovered dynamically, (maybe from the neighbor cache), and keys and parameters should be exchanged properly between devices. But for testing, the tool should be fine.

More explanation can be found in the code comments.

Testing procedure

This is not so heavily tested yet.

Setup two boards running our gnrc_networking example, add each others device addresses and enter the (pseudo-)peering command, which does not actually send a peering request but assumes that the peering succeeds.

board 1:

2022-09-11 10:03:19,166 # Iface  7  HWaddr: 00:01  Channel: 26  Page: 0  NID: 0x23  PHY: O-QPSK 
2022-09-11 10:03:19,167 #           
2022-09-11 10:03:19,172 #           Long HWaddr: 4E:49:6F:54:4F:76:00:01 
2022-09-11 10:03:19,179 #            TX-Power: 0dBm  State: IDLE  max. Retrans.: 3  CSMA Retries: 4 
2022-09-11 10:03:19,185 #           AUTOACK  ACK_REQ  CSMA  L2-PDU:88  MTU:1280  HL:64  RTR  
2022-09-11 10:03:19,187 #           6LO  IPHC  
2022-09-11 10:03:19,194 #           Source address length: 8
2022-09-11 10:03:19,196 #           Link type: wireless
2022-09-11 10:03:19,199 #           inet6 addr: fe80::4c49:6f54:4f76:1  scope: link  VAL
2022-09-11 10:03:19,201 #           inet6 group: ff02::2
2022-09-11 10:03:19,205 #           inet6 group: ff02::1
2022-09-11 10:03:19,207 #           inet6 group: ff02::1:ff76:1
2022-09-11 10:03:19,208 #           
2022-09-11 10:03:19,210 #           Statistics for Layer 2
2022-09-11 10:03:19,214 #             RX packets 2  bytes 114
2022-09-11 10:03:19,219 #             TX packets 3 (Multicast: 3)  bytes 171
2022-09-11 10:03:19,223 #             TX succeeded 3 errors 0
2022-09-11 10:03:19,226 #           Statistics for IPv6
2022-09-11 10:03:19,229 #             RX packets 0  bytes 0
2022-09-11 10:03:19,231 #             TX packets 3 (Multicast: 3)  bytes 192
2022-09-11 10:03:19,234 #             TX succeeded 3 errors 0
2022-09-11 10:03:19,234 #

$iwpansec 7 dev add 0x23 00:00 4E:49:6F:54:4F:76:00:00
$iwpansec 7 dev peer 0 0x23 00:00 4E:49:6F:54:4F:76:00:00

board 2:

2022-09-11 10:03:01,271 # Iface  7  HWaddr: 00:00  Channel: 26  Page: 0  NID: 0x23  PHY: O-QPSK 
2022-09-11 10:03:01,272 #           
2022-09-11 10:03:01,277 #           Long HWaddr: 4E:49:6F:54:4F:76:00:00 
2022-09-11 10:03:01,283 #            TX-Power: 0dBm  State: IDLE  max. Retrans.: 3  CSMA Retries: 4 
2022-09-11 10:03:01,293 #           AUTOACK  ACK_REQ  CSMA  L2-PDU:88  MTU:1280  HL:64  RTR  
2022-09-11 10:03:01,295 #           6LO  IPHC  
2022-09-11 10:03:01,296 #           Source address length: 8
2022-09-11 10:03:01,299 #           Link type: wireless
2022-09-11 10:03:01,313 #           inet6 addr: fe80::4c49:6f54:4f76:0  scope: link  VAL
2022-09-11 10:03:01,315 #           inet6 group: ff02::2
2022-09-11 10:03:01,316 #           inet6 group: ff02::1
2022-09-11 10:03:01,317 #           inet6 group: ff02::1:ff76:0
2022-09-11 10:03:01,318 #           
2022-09-11 10:03:01,319 #           Statistics for Layer 2
2022-09-11 10:03:01,322 #             RX packets 0  bytes 0
2022-09-11 10:03:01,323 #             TX packets 2 (Multicast: 2)  bytes 114
2022-09-11 10:03:01,326 #             TX succeeded 2 errors 0
2022-09-11 10:03:01,331 #           Statistics for IPv6
2022-09-11 10:03:01,336 #             RX packets 0  bytes 0
2022-09-11 10:03:01,338 #             TX packets 2 (Multicast: 2)  bytes 128
2022-09-11 10:03:01,341 #             TX succeeded 2 errors 0
2022-09-11 10:03:01,342 # 

$iwpansec 7 dev add 0x23 00:01 4E:49:6F:54:4F:76:00:01
$iwpansec 7 dev peer 0 0x23 00:01 4E:49:6F:54:4F:76:00:01

The boards should be able to ping each other after that.

> ping fe80::4c49:6f54:4f76:1%7
2022-09-11 10:07:15,042 # ping fe80::4c49:6f54:4f76:1%7
2022-09-11 10:07:15,057 # 12 bytes from fe80::4c49:6f54:4f76:1%7: icmp_seq=0 ttl=64 rssi=-61 dBm time=7.582 ms
2022-09-11 10:07:16,053 # 12 bytes from fe80::4c49:6f54:4f76:1%7: icmp_seq=1 ttl=64 rssi=-61 dBm time=11.427 ms
2022-09-11 10:07:17,043 # 12 bytes from fe80::4c49:6f54:4f76:1%7: icmp_seq=2 ttl=64 rssi=-61 dBm time=10.796 ms
2022-09-11 10:07:17,043 # 
2022-09-11 10:07:17,047 # --- fe80::4c49:6f54:4f76:1%7 PING statistics ---
2022-09-11 10:07:17,053 # 3 packets transmitted, 3 packets received, 0% packet loss
2022-09-11 10:07:17,057 # round-trip min/avg/max = 7.582/9.935/11.427 ms

Now, reset one board and repeat the setup on that board.
The ping intentionally no longer works, because the other board identifies the frame as a replay.

I know replay protection is not so useful until we store the data structures on persistent storage,
but it is good for testing and if I had to implement the persistent storage for LL security and proper key management,
I would go on from here.

Issues/PRs references

I think before we tackle #16844, we must know what exactly must be stored persistently. This implementation adds the internal data structures that must be stored.

Copy link
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand it correctly that with this we have a separate key for each pair of devices?

So for a simple star topology CONFIG_IEEE802154_SEC_DEFAULT_DEVSTORE_SIZE would be 1 for the leaf nodes and n for the border router, where n is the number of leaf nodes?

} ieee802154_sec_dev_lookup_t;

/**
* @brief Lookup table for peer devices to securely communicate with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't quite fully wrap my head around this, but could netstats_nb_t help here? (as in #16312)

Copy link
Contributor Author

@fabian18 fabian18 Oct 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand it correctly that with this we have a separate key for each pair of devices?

This would be the implicit key mode, which means after key exchange phase with a peer, the key to use is implicitly known by both, from the peer device address.
This requires 1 Keydescriptor + 1 KeyLookupDescriptor + 1 DeviceDescriptor per peer.

There are explicit keys, also called group keys. Let´s say a PAN coordinator is able to distribute some kind of key database in a network (or preconfigured).
The keys are somehow advertised with an indentifier (index). Each node will know key at index 1 is <KEY1> and key at index 2 is <KEY2> and so on.
So devices in that PAN use for example key_mode = IEEE802154_SEC_SCF_KEYMODE_INDEX and kex_index = i in the frame security header.
And if two peers are aware of the same key database, they will know which key is key i.
This requires 1 KeyDescriptor + 1 KeyLookupDescriptor + n DeviceDescriptors, for n peers.

We do need a DeviceDescriptor for each peer we communicate with, anyway to check the incoming frame counter.
I made a sketch in sys/net/link_layer/ieee802154/security.c. But I noticed there is one missing edge.

So for a simple star topology CONFIG_IEEE802154_SEC_DEFAULT_DEVSTORE_SIZE would be 1 for the leaf nodes and n for the border router, where n is the number of leaf nodes?

Yes, given that communication always happens through the border router as a PAN coordinator. But I think RIOT is not exposing the concept of a PAN coordinator at the moment.

I didn't quite fully wrap my head around this, but could netstats_nb_t help here? (as in #16312)

I would not extend netstats_nb_t because this is volatile information but ieee802154_sec_dev_lookup_t must go into persistent storage.
The DeviceDescriptor and especially the frame counter in it must remain, even if a neighbor is thrown out of the neighbor cache.
I think the neighbor cache could be useful to bootstrap peering.
In the manner of: "If I see a neighbor that I don´t have a security DeviceDescrptor for, I start some kind of peering phase and key negotiation."

@chrysn
Copy link
Member

chrysn commented Feb 8, 2024

IIUC this only adds replay protection. At least from the perspective of the gaps in our L2 security story, this doesn't fix them: The trouble occurs when a device uses the same (key, mac, seqno) combination twice, because that is when the nonce reuse happens; at receive time, best you could do is scream loudly that some peer is horribly broken and the key must now be changed.

What is the value of replay protection anyway, given that IP packets may generally be duplicated outside the local network? (I.e., any UDP based protocol needs to deal with duplicates anyway).

@fabian18
Copy link
Contributor Author

fabian18 commented Feb 24, 2024

This does not fix our nonce reuse, true.
Ways to solve would be either store the sequence counter of a used key, or use a new key on every reboot.
You need some data structures to do that as described in the spec and rudimentary implemneted here.
I would nedd to read it again though.
Obviously at least some kind of array of keys along wih the usage counter for the sender, where our problem happens.
The receiver side notices that by replay protection.

What is the value of replay protection anyway, given that IP packets may generally be duplicated outside the local network? (I.e., any UDP based protocol needs to deal with duplicates anyway).

The L2 security only secures communication in the link local network.
If an IP packets or UDP datagram from outside the lokal network arrives multiple times due to routing or whatever it will get a new L2 header with a new sequence counter for the last hop.
So that is not really concerning replay protection, or did I get you wrong?

From a security perspective reusing the same none is probably worse than not having replay protection.
But our problem is related because the internal data structure relations are used to handle both.

When I should come up with an example why replay protection is not useless, I would say that it prevents that some frame froma remote control can be replayed to get access to a door.
It could prevent that some measure sensor data records can be replayed.
Or in a local network you could manipulate IPv6 neighbor discovery.
You could for example replay a router advertisement to timeout a router, or replay router solicitations.

@chrysn
Copy link
Member

chrysn commented Feb 25, 2024

You need some data structures to do that as described in the spec and rudimentary implemneted here.

The data structures I see here on a first glance are bit fields; the data structure on the sending side is a persisted counter (ideally with precommittment to reduce flash operations / wear). What are the data structures that can be reused for sequence number persistence?

If an IP packets or UDP datagram from outside the lokal network arrives multiple times due to routing or whatever it will get a new L2 header with a new sequence counter for the last hop.
So that is not really concerning replay protection, or did I get you wrong?

Exactly, it will be encrypted twice -- which means that the IP based application will need to have a mechanism to deal with retransmissions anyway.

When I should come up with an example why replay protection is not useless, I would…

… hope that there was a motivating example in the first place? The IEEE 802.15.4 standard is vast, implementing things just because they are there sounds like a recipe for bloat (either in compiled code or in options to sift through when deciding what to use). Freshness with door lock control is well understood in applications, eg. in OSCORE's replay protection or RFC9175's Echo based freshness. IPv6 ND replay is something I don't know well enough to understand its fallout, and it may well be a justifying use case, but I'd like to ask you to look a bit more into where this actually makes a difference.

To illustrate why I think that replay protection at this level is problematic, consider that there is an upper limit to the number of replay peers (call it N), and that any peers not in that list needs to be regarded as trust-any initially1. Thus, if there is an attack in which an LL replayed packet can cause trouble, all the attacker needs to circumvent the security afforded by the LL replay protection is to gather N packets using the same key from peers outside the device's radio range (which allows them to be arbitrarily old), send those, thus flush the cache, and then inject the replayed packet.

Footnotes

  1. By comparison, Group OSCORE that faces similar challenges describes the different cases of whether or not any peer was ever evicted from that list, and if so, prescribes that any new peer's sequence number needs to be verified in a roundtrip challenge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: drivers Area: Device drivers Area: Kconfig Area: Kconfig integration Area: network Area: Networking Area: sys Area: System
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants