SkillAgentSearch skills...

Libstirshaken

C library implementing STIR-shaken STI-SP AS/VS, STI-CA

Install / Use

/learn @signalwire/Libstirshaken
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Overview

STIR-Shaken is a technology for making secure calls by use of SSL certificates and JSON Web Tokens. For a general overview of the framwork please search web for: ATIS, "Signature-based Handling of Asserted Information using Tokens (SHAKEN). Governance Model and Certificate Management",

This library implements STIR (Secure Telephony Identity Revisited) and SHAKEN (Signature-based Handling of Asserted information using toKENs) (RFC8224, RFC8588), with X509 certificate path check (ATIS "Signature-based Handling of Asserted information using toKENs (SHAKEN)", RFC5280 "6. Certification Path Validation").

You can find a comprehensive list of specs relevant to Shaken at the bottom of this document.

libstirshaken

This library provides building blocks for implementing STIR-Shaken authentication and verification services, (STI-SP/AS, STI-SP/VS), as well as elements of STI-CA and STI-PA.

Interoperability

libstirshaken was tested for interoperability with other leading Shaken implementations (e.g. TransNexus).

Basic usage

Authentication

Create PASSporT using Authentication Service interface

stir_shaken_context_t ss = { 0 };
stir_shaken_passport_params_t params = {
	.x5u = "https://shaken.signalwire.cloud/sp.pem"",
	.attest = "B",
	.desttn_key = "tn",
	.desttn_val = "01256700800",
	.iat = time(NULL),
	.origtn_key = "tn",
	.origtn_val = "01256500600",
	.origid = "e32f4189-cb86-460f-bb92-bd3acb89f29c"
};
stir_shaken_passport_t *passport = NULL;
char *s = NULL, *sih = NULL;
stir_shaken_as_t *as = NULL;

stir_shaken_init(&ss, STIR_SHAKEN_LOGLEVEL_NOTHING);
as = stir_shaken_as_create(&ss);
stir_shaken_as_load_private_key(&ss, as, "sp.priv"); 
encoded = stir_shaken_as_authenticate_to_passport(&ss, as, &params, &passport);

Print PASSporT in encoded form

printf("\n1. PASSporT encoded:\n%s\n", encoded);
1. PASSporT encoded:
eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly9zaGFrZW4uc2lnbmFsd2lyZS5jbG91ZC9zcC5wZW0ifQ.eyJhdHRlc3QiOiJCIiwiZGVzdCI6eyJ0biI6WyIwMTI1NjcwMDgwMCJdfSwiaWF0IjoxNjE2NDQyNTIzLCJvcmlnIjp7InRuIjoiMDEyNTY1MDA2MDAifSwib3JpZ2lkIjoiZTMyZjQxODktY2I4Ni00NjBmLWJiOTItYmQzYWNiODlmMjljIn0.VT_KOQtrCS3WctNBFT7PKcUowTqHI1cZU3XhBaYEji8eH07XE5rYxomns1EnnePpw96zUF7cr-mBBro-wP65jg

Print PASSporT in decoded (plain) form

s = stir_shaken_passport_dump_str(&ss, passport, 1);
printf("\n2. PASSporT decoded:\n%s\n", s);
2. PASSporT decoded:

{
    "alg": "ES256",
    "ppt": "shaken",
    "typ": "passport",
    "x5u": "https://shaken.signalwire.cloud/sp.pem"
}
.
{
    "attest": "B",
    "dest": {
        "tn": [
            "01256700800"
        ]
    },
    "iat": 1616442523,
    "orig": {
        "tn": "01256500600"
    },
    "origid": "e32f4189-cb86-460f-bb92-bd3acb89f29c"
}

Create and print SIP Identity Header

sih = stir_shaken_as_authenticate_to_sih(&ss, as, &params, &passport);
printf("\n3. SIP Identity Header:\n%s\n", sih);
3. SIP Identity Header:
eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly9zaGFrZW4uc2lnbmFsd2lyZS5jbG91ZC9zcC5wZW0ifQ.eyJhdHRlc3QiOiJCIiwiZGVzdCI6eyJ0biI6WyIwMTI1NjcwMDgwMCJdfSwiaWF0IjoxNjE2NDQyNTIzLCJvcmlnIjp7InRuIjoiMDEyNTY1MDA2MDAifSwib3JpZ2lkIjoiZTMyZjQxODktY2I4Ni00NjBmLWJiOTItYmQzYWNiODlmMjljIn0.rN3n-2qjP9eVPMViBbK6sVUmN3tMRbI-8ffVs1M7J9KL0q0hMKtdZNBWj_TS5RkvakiDUoSErkDsahh2nRGD8Q;info=<https://shaken.signalwire.cloud/sp.pem>;alg=ES256;ppt=shaken

Verification


char *passport_encoded = "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly9zaGFrZW4uc2lnbmFsd2lyZS5jbG91ZC9zcC5wZW0ifQ.eyJhdHRlc3QiOiJCIiwiZGVzdCI6eyJ0biI6WyIwMTI1NjcwMDgwMCJdfSwiaWF0IjoxNjE2NDQyNTIzLCJvcmlnIjp7InRuIjoiMDEyNTY1MDA2MDAifSwib3JpZ2lkIjoiZTMyZjQxODktY2I4Ni00NjBmLWJiOTItYmQzYWNiODlmMjljIn0.VT_KOQtrCS3WctNBFT7PKcUowTqHI1cZU3XhBaYEji8eH07XE5rYxomns1EnnePpw96zUF7cr-mBBro-wP65jg";
char *sip_identity_header = "eyJhbGciOiJFUzI1NiIsInBwdCI6InNoYWtlbiIsInR5cCI6InBhc3Nwb3J0IiwieDV1IjoiaHR0cHM6Ly9zaGFrZW4uc2lnbmFsd2lyZS5jbG91ZC9zcC5wZW0ifQ.eyJhdHRlc3QiOiJCIiwiZGVzdCI6eyJ0biI6WyIwMTI1NjcwMDgwMCJdfSwiaWF0IjoxNjE2NDQyNTIzLCJvcmlnIjp7InRuIjoiMDEyNTY1MDA2MDAifSwib3JpZ2lkIjoiZTMyZjQxODktY2I4Ni00NjBmLWJiOTItYmQzYWNiODlmMjljIn0.rN3n-2qjP9eVPMViBbK6sVUmN3tMRbI-8ffVs1M7J9KL0q0hMKtdZNBWj_TS5RkvakiDUoSErkDsahh2nRGD8Q;info=<https://shaken.signalwire.cloud/sp.pem>;alg=ES256;ppt=shaken";
stir_shaken_passport_t *passport_out = NULL;
stir_shaken_cert_t *cert_out = NULL;
int iat_freshness_seconds = 60;
unsigned long connect_timeout_s = 5;
stir_shaken_vs_t *vs = NULL;

stir_shaken_init(&ss, STIR_SHAKEN_LOGLEVEL_NOTHING);

vs = stir_shaken_vs_create(&ss);

Optionally turn on X509 certificate path verification (and configure CA dir containing trusted CA root certificates)

stir_shaken_vs_set_x509_cert_path_check(&ss, vs, 1);
stir_shaken_vs_load_ca_dir(&ss, vs, "path/to/ca/dir");

Optionally set your own callback to supply certificates from cache

stir_shaken_vs_set_callback(&ss, vs, cache_callback);

Optionally set timeout on connect (default is 2 seconds)

stir_shaken_vs_set_connect_timeout(&ss, vs, connect_timeout_s);

Verify PASSporT's signature (involves certificate downloading or fetching from cache, and X509 cert path check if turned on), input is PASSporT encoded, output is status and optionally certificate and PASSporT used during verification

status = stir_shaken_vs_passport_verify(&ss, vs, passport_encoded, &cert_out, &passport_out);
if (STIR_SHAKEN_STATUS_OK != status) {
	printf("PASSporT failed verification");
} else {
	printf("PASSporT Verified");
}

Same as before, but input is PASSporT wrapped into SIP Identity Header

status = stir_shaken_vs_sih_verify(&ss, vs, sip_identity_header, &cert_out, &passport_out);
if (STIR_SHAKEN_STATUS_OK != status) {
	printf("SIP Identity Header failed verification");
} else {
	printf("SIP Identity Header verified");
}

Check that PASSporT applies to the current moment in time

if (STIR_SHAKEN_STATUS_OK != stir_shaken_passport_validate_iat_against_freshness(&ss, passport_out, iat_freshness_seconds)) {
	error_description = stir_shaken_get_error(&ss, &error_code);
	if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_FUTURE) {
		printf("PASSporT not valid yet\n");
	} else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_EXPIRED) {
		printf("PASSporT expired\n");
	} else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT) {
		printf("PASSporT is missing @iat grant\n");
	} else {
		printf("You called this method with NULL PASSporT\n");
	}
	printf("PASSporT doesn't apply to the current moment in time\n");
	printf("Error description is:\n%s\n", error_description);
}

Print PASSporT

passport_decoded = stir_shaken_passport_dump_str(&ss, passport, 1);
if (passport_decoded) {
	printf("PASSporT is:\n%s\n", passport_decoded);
	stir_shaken_free_jwt_str(passport_decoded);
	passport_decoded = NULL;
}
PASSporT Verified

PASSporT is:

{
    "alg": "ES256",
    "ppt": "shaken",
    "typ": "passport",
    "x5u": "https://shaken.signalwire.cloud/sp.pem"
}
.
{
    "attest": "B",
    "dest": {
        "tn": [
            "01256700800"
        ]
    },
    "iat": 1616442523,
    "orig": {
        "tn": "01256500600"
    },
    "origid": "e32f4189-cb86-460f-bb92-bd3acb89f29c"
}

Print the certificate

if (STIR_SHAKEN_STATUS_OK == stir_shaken_read_cert_fields(&ss, cert)) {
	printf("Certificate is:\n");
	stir_shaken_print_cert_fields(stdout, cert);
}
Certificate is:
STIR-Shaken: STI Cert: Serial number: 01 1
STIR-Shaken: STI Cert: Issuer: /C=US/CN=SignalWire STI-CA Test
STIR-Shaken: STI Cert: Subject: /C=US/CN=SignalWire STI-SP Test
STIR-Shaken: STI Cert: Valid from: Aug  1 00:37:19 2020 GMT
STIR-Shaken: STI Cert: Valid to: Dec 17 00:37:19 2047 GMT
STIR-Shaken: STI Cert: Version: 3

Folders

/ - main folder
	README.txt	- this file
	src/		- library sources
	include/	- library headers
	util/		- helper programs (stirshaken tool for running multiple commands, see below)
	test/		- unit tests
	examples/	- examples:
						stir_shaken_as_basic.c - shows how Authentication Service may be constructed from the basic blocks
						stir_shaken_as_easy.c - shows how to use default Authentication Service interface
						stir_shaken_vs_basic.c - shows how Verification Service may be constructed from the basic blocks
						stir_shaken_vs_easy.c - shows how to use default Verification Service interface
						stir_shaken_ca.c - shows how Certificate Authority may be constructed from the basic blocks
						stir_shaken_cert_req.c - shows how certificate may be requested and downloaded from CA

Compilation

Dependencies

CURL: https://github.com/curl/curl

OpenSSL: https://github.com/openssl/openssl version 1.1 or later

LibJWT: https://github.com/benmcollins/libjwt version 1.12 or later

LibKS: https://github.com/signalwire/libks

Signalwire Personal Access Token: https://freeswitch.org/confluence/display/FREESWITCH/HOWTO+Create+a+SignalWire+Personal+Access+Token

Packages for latest libks and libjwt which are required are available in the freeswitch package repositories:

Debian 10:

TOKEN=YOURSIGNALWIRETOKEN
apt-get update && apt-get install -y gnupg2 wget lsb-release
wget --http-user=signalwire --http-password=$TOKEN -O /usr/share/keyrings/signalwire-freeswitch-repo.gpg https://freeswitch.signalwire.com/repo/deb/debian-release/signalwire-freeswitch-repo.gpg
 
echo "machine freeswitch.signalwire.com login signalwire password $TOKEN" > /etc/apt/auth.conf
echo "deb [signed-by=/usr/share/keyrings/signalwire-freeswitch-repo.gpg] https://freeswitch.signalwire.com/repo/deb/debian-release/ `lsb_release -sc` main" > /etc/apt/sources.list.d/freeswitch.list
echo "deb-src [signed-by=/usr/share/keyrings/signalwire-freeswitch-repo.gpg] https://freeswitch.signalwire.com/repo/deb/debian-release/ `lsb_release -sc` main" >> /etc/apt/sources.list.d/freeswi
View on GitHub
GitHub Stars34
CategoryDevelopment
Updated2mo ago
Forks24

Languages

C

Security Score

90/100

Audited on Jan 11, 2026

No findings