STIR/SHAKEN in 2021
It’s been a while since the release of Asterisk 18 and the STIR/SHAKEN update. During this time, we’ve discovered that the specification has been solidified more than when we started development. Unfortunately, this means that there are quite a few things missing from STIR/SHAKEN in Asterisk. The good news is that we haven’t forgotten about it, and the code we do have has served as a good base for adding missing functionality. It’s still being actively worked on. For this blog post, I’m going to dive into some of the things on our radar, as well as changes that have already been merged.
I think it’s worth mentioning that this was brought to our attention during OpenSIPit 2021. We quickly discovered that our implementation needed some key features defined in the RFCs to be compatible with others’ implementations. This goes to show just how important it is to test with outside sources!
These changes have already been merged into Asterisk. This does not represent all of the work being done on STIR/SHAKEN, and it doesn’t mean that STIR/SHAKEN is finished. We just wanted to give a status update on some of the things we’ve been working on.
Let’s start with what initially broke everything. Public certificates are expected to be a certain type (in this case, X.509). This is not the type that we were trying to read them as. The fix was fairly straightforward for this, but the tests were slightly more involved. New certificates were generated, tested, and put in the testsuite as well as the unit tests.
The naming convention used to store the public certificates we downloaded was not great at handling things such as duplicate file names. In the end, we decided to get the serial number from the certificate and use it as part of the name to store things in a more dynamic way. This prevents things such as Asterisk thinking we’ve already downloaded a certificate that happens to have the same name as a new one we want, causing verification to fail by using the already downloaded (and incorrect) certificate.
Originally, we were encoding and decoding the payload in BASE64. Thanks to everyone at OpenSIPit, we discovered that STIR/SHAKEN uses BASE64 URL instead. Luckily, Asterisk has built-in support for BASE64, so a similar approach was done for BASE64 URL.
The STIR/SHAKEN configuration file gave the option to specify origid for outgoing STIR/SHAKEN requests. This ended up being unnecessary, as the RFCs state something like a UUID should be used for this field. In the end, this ended up being better for the user as well, since it’s one less configuration option to worry about. Asterisk simply handles this behind the scenes when INVITEs are sent out by generating one randomly.
STIR/SHAKEN requires a Date header. This is something that Asterisk does in other places, but was simply missing from STIR/SHAKEN. Code was added to get the GMT time and format the Date header. This was also required for Date header validity checking for sending response codes.
The header section of the JWT required dest->tn. This was easy to add, but there has been a lot of discussion around this in the community. More on this later.
Another shoutout to the OpenSIPit crew. Some security testing was done, and one of the things it caught was trying to access a file location rather than a URL. This caused several bad things to happen, so a check was added to ensure we are getting a URL rather than something potentially harmful.
These are a few of the STIR/SHAKEN issues we have in the pipeline. There is no ETA on any of this, but work is moving forward at a steady pace. There are more items that aren’t on this list, but we have been creating issues for everything we see that needs a fix.
The RFCs define a number of response codes that should be sent if you run into some kind of error. For example, if there is an invalid ppt parameter (i.e., anything not supported), send a 428. If the Date header falls outside of a valid range (e.g., 60 seconds is recommended), send a 403. Currently, no response codes are sent, but this is the most recent issue being worked on. It might even be merged in before this blog post is released!
Currently, we don’t interact with the certificate authority at all. We simply validate what we have or create an Identity header based on if it is an incoming or outgoing INVITE, respectively. The plan is to use ca_path to get information back from OpenSSL, but this will come at a later date.
Depending on what kind of setup you are running, there is a possibility the certificate storage will get cluttered very fast. We plan on implementing an expiration system that will prune certificates that have been held past a certain duration or have expired completely (the expiration on the certificate itself). There’s more work that needs to be done on getting expiration information, which will likely be through OpenSSL again, but this will involve quite a bit of refactoring.
Overall, we’ve made a decent amount of progress on the things that outright broke our implementation. It seems like the STIR/SHAKEN specification has been ironed out a little better than when we first started working on it. However, there are still several concerns about it as a whole. We’ve seen discussions on several topics including dest->tn and how that is going to be handled when passing through multiple entities. Despite all the uncertainty on how to handle certain scenarios, we’re going to push forward and keep making STIR/SHAKEN in Asterisk better.