Skip to content

Instantly share code, notes, and snippets.

@dnoliver
Last active May 28, 2020 17:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dnoliver/ee977c80003fe26c782ebf9d5c9d55e2 to your computer and use it in GitHub Desktop.
Save dnoliver/ee977c80003fe26c782ebf9d5c9d55e2 to your computer and use it in GitHub Desktop.
Certificate Signing Request generation with tpm2-pkcs11
#!/bin/bash
set -euxo pipefail
export TPM2TOOLS_TCTI="device:/dev/tpmrm0"
export TPM2_PKCS11_TCTI="device:/dev/tpmrm0"
#export TPM2_PKCS11_LOG_LEVEL=2
tpm2_print_handles () {
for i in transient saved-session loaded-session;
do
tpm2_getcap handles-$i;
done
}
tpm2_flush_handles () {
for i in transient-object saved-session loaded-session;
do
tpm2_flushcontext --$i;
done
}
tpm2_clear
rm tpm2_pkcs11.sqlite3
tpm2_ptool init
tpm2_ptool addtoken --pid=1 --sopin=mysopin --userpin=myuserpin --label=label
tpm2_ptool addkey --algorithm=rsa2048 --label=label --userpin=myuserpin
tpm2_ptool config --key tcti --value "device:/dev/tpmrm0" --label label
p11-kit list-modules
TOKEN=$(p11tool --list-token-urls | grep "token=label")
expect <(cat <<EOF
spawn p11tool --login --list-all "${TOKEN}" --outfile p11tool.out
expect "Enter PIN: "
send -- "myuserpin\r"
interact
EOF
)
RANDOM=$$
ID=${RANDOM}
KEY=$(cat p11tool.out | grep private | awk '{ print $2 }')
SUBJ="/C=FR/ST=Radius/L=Somewhere/O=Example Inc./CN=testing-${ID}/emailAddress=testing-${ID}@123.com"
openssl req -new -engine pkcs11 -keyform engine -key "${KEY};pin-value=myuserpin" -subj "${SUBJ}" -out client-${ID}.csr
# Sign CSR in RADIUS Server with openssl
#
# cd /etc/raddb/certs
# openssl ca \
# -batch -keyfile ./ca.key -cert ./ca.pem -passin pass:whatever \
# -in client-${ID}.csr -out client-${ID}.crt \
# -extensions xpclient_ext -extfile xpextensions
# -config client.cnf
cat <<EOF > wpa_supplicant-${ID}.conf
network={
ssid="SSID"
key_mgmt=WPA-EAP
eap=TLS
identity="testing"
ca_cert="./ca.pem"
client_cert="./client-${ID}.crt"
private_key="${KEY}"
pin="myuserpin"
}
EOF
echo "wpa_supplicant -c wpa_supplicant-${ID}.conf -i wlp1s0"
@dnoliver
Copy link
Author

dnoliver commented Jan 22, 2020

Server side signature:

cd /etc/raddb/certs
openssl ca -batch -keyfile ./ca.key -cert ./ca.pem -in client.csr -out client.crt -extensions xpclient_ext -extfile xpextensions -config client.cnf`
# password is "whatever"
cat client.crt

wpa_supplicant configuration

wpa_supplicant -c ASUS_AP_EAP-TLS_TPM.conf -i wlp1s0

network={
        ssid="ASUS_AP"
        key_mgmt=WPA-EAP
        eap=TLS
        identity="testing"

        ca_cert="/root/ca.pem"
        client_cert="/root/client3.crt"
        private_key="pkcs11:model=Intel;manufacturer=Intel;serial=0000000000000000;token=label;id=%32%62%37%30%65%62%36%32%66%33%32%62%31%63%65%37;object=0;type=private"
        pin="myuserpin"
}

@dnoliver
Copy link
Author

wpa_supplicant output:

Successfully initialized wpa_supplicant
nl80211: Could not set interface 'p2p-dev-wlp1s0' UP
nl80211: deinit ifname=p2p-dev-wlp1s0 disabled_11b_rates=0
p2p-dev-wlp1s0: Failed to initialize driver interface
P2P: Failed to enable P2P Device interface
wlp1s0: SME: Trying to authenticate with 1c:87:2c:6a:c3:80 (SSID='ASUS_AP' freq=2447 MHz)
wlp1s0: Trying to associate with 1c:87:2c:6a:c3:80 (SSID='ASUS_AP' freq=2447 MHz)
wlp1s0: Associated with 1c:87:2c:6a:c3:80
wlp1s0: CTRL-EVENT-SUBNET-STATUS-UPDATE status=0
wlp1s0: CTRL-EVENT-EAP-STARTED EAP authentication started
wlp1s0: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=4 -> NAK
wlp1s0: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=13
wlp1s0: CTRL-EVENT-EAP-METHOD EAP vendor 0 method 13 (TLS) selected
wlp1s0: CTRL-EVENT-EAP-PEER-CERT depth=1 subject='/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.org/CN=Example Certificate Authority' hash=be68a58061e340d61535b8ad9e9d39f3fa14ee23ee436edd652de19f54afc694
wlp1s0: CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/C=FR/ST=Radius/O=Example Inc./CN=Example Server Certificate/emailAddress=admin@example.org' hash=9b0429b013845795d6fc1f0544a62fe7e9de6ff3304288a8811e6155c16a5a20
wlp1s0: CTRL-EVENT-EAP-FAILURE EAP authentication failed
wlp1s0: Authentication with 1c:87:2c:6a:c3:80 timed out.
wlp1s0: CTRL-EVENT-DISCONNECTED bssid=1c:87:2c:6a:c3:80 reason=3 locally_generated=1
wlp1s0: CTRL-EVENT-SSID-TEMP-DISABLED id=0 ssid="ASUS_AP" auth_failures=1 duration=10 reason=AUTH_FAILED
wlp1s0: CTRL-EVENT-SSID-REENABLED id=0 ssid="ASUS_AP"
wlp1s0: SME: Trying to authenticate with 1c:87:2c:6a:c3:80 (SSID='ASUS_AP' freq=2447 MHz)
wlp1s0: Trying to associate with 1c:87:2c:6a:c3:80 (SSID='ASUS_AP' freq=2447 MHz)
wlp1s0: Associated with 1c:87:2c:6a:c3:80
wlp1s0: CTRL-EVENT-EAP-STARTED EAP authentication started
wlp1s0: CTRL-EVENT-SUBNET-STATUS-UPDATE status=0
wlp1s0: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=4 -> NAK
wlp1s0: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=13
wlp1s0: CTRL-EVENT-EAP-METHOD EAP vendor 0 method 13 (TLS) selected
wlp1s0: CTRL-EVENT-EAP-PEER-CERT depth=1 subject='/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.org/CN=Example Certificate Authority' hash=be68a58061e340d61535b8ad9e9d39f3fa14ee23ee436edd652de19f54afc694
wlp1s0: CTRL-EVENT-EAP-PEER-CERT depth=0 subject='/C=FR/ST=Radius/O=Example Inc./CN=Example Server Certificate/emailAddress=admin@example.org' hash=9b0429b013845795d6fc1f0544a62fe7e9de6ff3304288a8811e6155c16a5a20
wlp1s0: CTRL-EVENT-EAP-FAILURE EAP authentication failed
wlp1s0: Authentication with 1c:87:2c:6a:c3:80 timed out.
wlp1s0: CTRL-EVENT-DISCONNECTED bssid=1c:87:2c:6a:c3:80 reason=3 locally_generated=1
wlp1s0: CTRL-EVENT-SSID-TEMP-DISABLED id=0 ssid="ASUS_AP" auth_failures=2 duration=26 reason=AUTH_FAILED
wlp1s0: CTRL-EVENT-SSID-TEMP-DISABLED id=0 ssid="ASUS_AP" auth_failures=3 duration=57 reason=CONN_FAILED

radiusd -X output:

(7) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 127
(7)   User-Name = "testing"
(7)   NAS-IP-Address = 192.168.1.1
(7)   Called-Station-Id = "1c872c6ac380"
(7)   Calling-Station-Id = "2aa9892665c7"
(7)   NAS-Identifier = "1c872c6ac380"
(7)   NAS-Port = 4
(7)   Framed-MTU = 1400
(7)   NAS-Port-Type = Wireless-802.11
(7)   EAP-Message = 0x0200000c0174657374696e67
(7)   Message-Authenticator = 0xabe815fe7ce8f654442a01c66734f2f1
(7) # Executing section authorize from file /etc/raddb/sites-enabled/default
(7)   authorize {
(7)     policy filter_username {
(7)       if (&User-Name) {
(7)       if (&User-Name)  -> TRUE
(7)       if (&User-Name)  {
(7)         if (&User-Name =~ / /) {
(7)         if (&User-Name =~ / /)  -> FALSE
(7)         if (&User-Name =~ /@[^@]*@/ ) {
(7)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(7)         if (&User-Name =~ /\.\./ ) {
(7)         if (&User-Name =~ /\.\./ )  -> FALSE
(7)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(7)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(7)         if (&User-Name =~ /\.$/)  {
(7)         if (&User-Name =~ /\.$/)   -> FALSE
(7)         if (&User-Name =~ /@\./)  {
(7)         if (&User-Name =~ /@\./)   -> FALSE
(7)       } # if (&User-Name)  = notfound
(7)     } # policy filter_username = notfound
(7)     [preprocess] = ok
(7)     [chap] = noop
(7)     [mschap] = noop
(7)     [digest] = noop
(7) suffix: Checking for suffix after "@"
(7) suffix: No '@' in User-Name = "testing", looking up realm NULL
(7) suffix: No such realm "NULL"
(7)     [suffix] = noop
(7) eap: Peer sent EAP Response (code 2) ID 0 length 12
(7) eap: EAP-Identity reply, returning 'ok' so we can short-circuit the rest of authorize
(7)     [eap] = ok
(7)   } # authorize = ok
(7) Found Auth-Type = eap
(7) # Executing group from file /etc/raddb/sites-enabled/default
(7)   authenticate {
(7) eap: Peer sent packet with method EAP Identity (1)
(7) eap: Calling submodule eap_md5 to process data
(7) eap_md5: Issuing MD5 Challenge
(7) eap: Sending EAP Request (code 1) ID 1 length 22
(7) eap: EAP session adding &reply:State = 0xd5e4295ed5e52d7f
(7)     [eap] = handled
(7)   } # authenticate = handled
(7) Using Post-Auth-Type Challenge
(7) # Executing group from file /etc/raddb/sites-enabled/default
(7)   Challenge { ... } # empty sub-section is ignored
(7) Sent Access-Challenge Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 0
(7)   EAP-Message = 0x010100160410d371123d6a511ba084a80bca424e1d9b
(7)   Message-Authenticator = 0x00000000000000000000000000000000
(7)   State = 0xd5e4295ed5e52d7f074246c6f7330379
(7) Finished request
Waking up in 4.9 seconds.
(7) Cleaning up request packet ID 3 with timestamp +22
(8) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 139
(8)   User-Name = "testing"
(8)   NAS-IP-Address = 192.168.1.1
(8)   Called-Station-Id = "1c872c6ac380"
(8)   Calling-Station-Id = "2aa9892665c7"
(8)   NAS-Identifier = "1c872c6ac380"
(8)   NAS-Port = 4
(8)   Framed-MTU = 1400
(8)   State = 0xd5e4295ed5e52d7f074246c6f7330379
(8)   NAS-Port-Type = Wireless-802.11
(8)   EAP-Message = 0x02010006030d
(8)   Message-Authenticator = 0xf0e702e093fefc21632c98580020f77e
(8) session-state: No cached attributes
(8) # Executing section authorize from file /etc/raddb/sites-enabled/default
(8)   authorize {
(8)     policy filter_username {
(8)       if (&User-Name) {
(8)       if (&User-Name)  -> TRUE
(8)       if (&User-Name)  {
(8)         if (&User-Name =~ / /) {
(8)         if (&User-Name =~ / /)  -> FALSE
(8)         if (&User-Name =~ /@[^@]*@/ ) {
(8)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(8)         if (&User-Name =~ /\.\./ ) {
(8)         if (&User-Name =~ /\.\./ )  -> FALSE
(8)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(8)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(8)         if (&User-Name =~ /\.$/)  {
(8)         if (&User-Name =~ /\.$/)   -> FALSE
(8)         if (&User-Name =~ /@\./)  {
(8)         if (&User-Name =~ /@\./)   -> FALSE
(8)       } # if (&User-Name)  = notfound
(8)     } # policy filter_username = notfound
(8)     [preprocess] = ok
(8)     [chap] = noop
(8)     [mschap] = noop
(8)     [digest] = noop
(8) suffix: Checking for suffix after "@"
(8) suffix: No '@' in User-Name = "testing", looking up realm NULL
(8) suffix: No such realm "NULL"
(8)     [suffix] = noop
(8) eap: Peer sent EAP Response (code 2) ID 1 length 6
(8) eap: No EAP Start, assuming it's an on-going EAP conversation
(8)     [eap] = updated
(8) files: users: Matched entry testing at line 1
(8)     [files] = ok
(8)     [expiration] = noop
(8)     [logintime] = noop
(8) pap: WARNING: Auth-Type already set.  Not setting to PAP
(8)     [pap] = noop
(8)   } # authorize = updated
(8) Found Auth-Type = eap
(8) # Executing group from file /etc/raddb/sites-enabled/default
(8)   authenticate {
(8) eap: Expiring EAP session with state 0xd5e4295ed5e52d7f
(8) eap: Finished EAP session with state 0xd5e4295ed5e52d7f
(8) eap: Previous EAP request found for state 0xd5e4295ed5e52d7f, released from the list
(8) eap: Peer sent packet with method EAP NAK (3)
(8) eap: Found mutually acceptable type TLS (13)
(8) eap: Calling submodule eap_tls to process data
(8) eap_tls: Initiating new TLS session
(8) eap_tls: Setting verify mode to require certificate from client
(8) eap_tls: [eaptls start] = request
(8) eap: Sending EAP Request (code 1) ID 2 length 6
(8) eap: EAP session adding &reply:State = 0xd5e4295ed4e6247f
(8)     [eap] = handled
(8)   } # authenticate = handled
(8) Using Post-Auth-Type Challenge
(8) # Executing group from file /etc/raddb/sites-enabled/default
(8)   Challenge { ... } # empty sub-section is ignored
(8) Sent Access-Challenge Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 0
(8)   EAP-Message = 0x010200060d20
(8)   Message-Authenticator = 0x00000000000000000000000000000000
(8)   State = 0xd5e4295ed4e6247f074246c6f7330379
(8) Finished request
Waking up in 4.9 seconds.
(8) Cleaning up request packet ID 3 with timestamp +22
(9) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 347
(9)   User-Name = "testing"
(9)   NAS-IP-Address = 192.168.1.1
(9)   Called-Station-Id = "1c872c6ac380"
(9)   Calling-Station-Id = "2aa9892665c7"
(9)   NAS-Identifier = "1c872c6ac380"
(9)   NAS-Port = 4
(9)   Framed-MTU = 1400
(9)   State = 0xd5e4295ed4e6247f074246c6f7330379
(9)   NAS-Port-Type = Wireless-802.11
(9)   EAP-Message = 0x020200d60d0016030100cb010000c703036c9bd25529fa1137e65bbf6c809f8b2df0f808bea3cc85a76bc9ccb4818a2b9f00004ac02cc030cca9cca8c0adc02bc02fc0acc023c027c00ac014c009c013009dc09d009cc09c003d003c0035002f009fccaac09f009ec09e006b006700390033c008c012000a0016001300ff01000054000b000403000102000a000c000a001d0017001e001900180016000000170000000d0030002e040305030603080708080809080a080b080408050806040105010601030302030301020103020202040205020602
(9)   Message-Authenticator = 0xe49bba37cdc6277e3954d580d4028534
(9) session-state: No cached attributes
(9) # Executing section authorize from file /etc/raddb/sites-enabled/default
(9)   authorize {
(9)     policy filter_username {
(9)       if (&User-Name) {
(9)       if (&User-Name)  -> TRUE
(9)       if (&User-Name)  {
(9)         if (&User-Name =~ / /) {
(9)         if (&User-Name =~ / /)  -> FALSE
(9)         if (&User-Name =~ /@[^@]*@/ ) {
(9)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(9)         if (&User-Name =~ /\.\./ ) {
(9)         if (&User-Name =~ /\.\./ )  -> FALSE
(9)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(9)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(9)         if (&User-Name =~ /\.$/)  {
(9)         if (&User-Name =~ /\.$/)   -> FALSE
(9)         if (&User-Name =~ /@\./)  {
(9)         if (&User-Name =~ /@\./)   -> FALSE
(9)       } # if (&User-Name)  = notfound
(9)     } # policy filter_username = notfound
(9)     [preprocess] = ok
(9)     [chap] = noop
(9)     [mschap] = noop
(9)     [digest] = noop
(9) suffix: Checking for suffix after "@"
(9) suffix: No '@' in User-Name = "testing", looking up realm NULL
(9) suffix: No such realm "NULL"
(9)     [suffix] = noop
(9) eap: Peer sent EAP Response (code 2) ID 2 length 214
(9) eap: No EAP Start, assuming it's an on-going EAP conversation
(9)     [eap] = updated
(9) files: users: Matched entry testing at line 1
(9)     [files] = ok
(9)     [expiration] = noop
(9)     [logintime] = noop
(9) pap: WARNING: Auth-Type already set.  Not setting to PAP
(9)     [pap] = noop
(9)   } # authorize = updated
(9) Found Auth-Type = eap
(9) # Executing group from file /etc/raddb/sites-enabled/default
(9)   authenticate {
(9) eap: Expiring EAP session with state 0xd5e4295ed4e6247f
(9) eap: Finished EAP session with state 0xd5e4295ed4e6247f
(9) eap: Previous EAP request found for state 0xd5e4295ed4e6247f, released from the list
(9) eap: Peer sent packet with method EAP TLS (13)
(9) eap: Calling submodule eap_tls to process data
(9) eap_tls: Continuing EAP-TLS
(9) eap_tls: [eaptls verify] = ok
(9) eap_tls: Done initial handshake
(9) eap_tls: (other): before SSL initialization
(9) eap_tls: TLS_accept: before SSL initialization
(9) eap_tls: TLS_accept: before SSL initialization
(9) eap_tls: <<< recv TLS 1.3  [length 00cb] 
(9) eap_tls: TLS_accept: SSLv3/TLS read client hello
(9) eap_tls: >>> send TLS 1.2  [length 003d] 
(9) eap_tls: TLS_accept: SSLv3/TLS write server hello
(9) eap_tls: >>> send TLS 1.2  [length 08e9] 
(9) eap_tls: TLS_accept: SSLv3/TLS write certificate
(9) eap_tls: >>> send TLS 1.2  [length 014d] 
(9) eap_tls: TLS_accept: SSLv3/TLS write key exchange
(9) eap_tls: >>> send TLS 1.2  [length 00d2] 
(9) eap_tls: TLS_accept: SSLv3/TLS write certificate request
(9) eap_tls: >>> send TLS 1.2  [length 0004] 
(9) eap_tls: TLS_accept: SSLv3/TLS write server done
(9) eap_tls: TLS_accept: Need to read more data: SSLv3/TLS write server done
(9) eap_tls: TLS - In Handshake Phase
(9) eap_tls: TLS - got 2914 bytes of data
(9) eap_tls: [eaptls process] = handled
(9) eap: Sending EAP Request (code 1) ID 3 length 1004
(9) eap: EAP session adding &reply:State = 0xd5e4295ed7e7247f
(9)     [eap] = handled
(9)   } # authenticate = handled
(9) Using Post-Auth-Type Challenge
(9) # Executing group from file /etc/raddb/sites-enabled/default
(9)   Challenge { ... } # empty sub-section is ignored
(9) Sent Access-Challenge Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 0
(9)   EAP-Message = 0x010303ec0dc000000b62160303003d0200003903039a613f5ac92b3063e748ef3c1ba02093fcf55a92cdccea7651e601acb03cbff300c030000011ff01000100000b0004030001020017000016030308e90b0008e50008e20003de308203da308202c2a003020102020101300d06092a864886f70d01010b0500308193310b3009060355040613024652310f300d06035504080c065261646975733112301006035504070c09536f6d65776865726531153013060355040a0c0c4578616d706c6520496e632e3120301e06092a864886f70d010901161161646d696e406578616d706c652e6f72673126302406035504030c1d4578616d706c6520436572746966696361746520417574686f72697479301e170d3230303132313231353730315a170d3230303332313231353730315a307c310b3009060355040613024652310f300d06035504080c0652616469757331153013060355040a0c0c4578616d706c6520496e632e3123302106035504030c1a4578616d70
(9)   Message-Authenticator = 0x00000000000000000000000000000000
(9)   State = 0xd5e4295ed7e7247f074246c6f7330379
(9) Finished request
Waking up in 4.9 seconds.
(9) Cleaning up request packet ID 3 with timestamp +22
(10) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 139
(10)   User-Name = "testing"
(10)   NAS-IP-Address = 192.168.1.1
(10)   Called-Station-Id = "1c872c6ac380"
(10)   Calling-Station-Id = "2aa9892665c7"
(10)   NAS-Identifier = "1c872c6ac380"
(10)   NAS-Port = 4
(10)   Framed-MTU = 1400
(10)   State = 0xd5e4295ed7e7247f074246c6f7330379
(10)   NAS-Port-Type = Wireless-802.11
(10)   EAP-Message = 0x020300060d00
(10)   Message-Authenticator = 0x318d9f41b1b0418fa5211191c18113f9
(10) session-state: No cached attributes
(10) # Executing section authorize from file /etc/raddb/sites-enabled/default
(10)   authorize {
(10)     policy filter_username {
(10)       if (&User-Name) {
(10)       if (&User-Name)  -> TRUE
(10)       if (&User-Name)  {
(10)         if (&User-Name =~ / /) {
(10)         if (&User-Name =~ / /)  -> FALSE
(10)         if (&User-Name =~ /@[^@]*@/ ) {
(10)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(10)         if (&User-Name =~ /\.\./ ) {
(10)         if (&User-Name =~ /\.\./ )  -> FALSE
(10)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(10)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(10)         if (&User-Name =~ /\.$/)  {
(10)         if (&User-Name =~ /\.$/)   -> FALSE
(10)         if (&User-Name =~ /@\./)  {
(10)         if (&User-Name =~ /@\./)   -> FALSE
(10)       } # if (&User-Name)  = notfound
(10)     } # policy filter_username = notfound
(10)     [preprocess] = ok
(10)     [chap] = noop
(10)     [mschap] = noop
(10)     [digest] = noop
(10) suffix: Checking for suffix after "@"
(10) suffix: No '@' in User-Name = "testing", looking up realm NULL
(10) suffix: No such realm "NULL"
(10)     [suffix] = noop
(10) eap: Peer sent EAP Response (code 2) ID 3 length 6
(10) eap: No EAP Start, assuming it's an on-going EAP conversation
(10)     [eap] = updated
(10) files: users: Matched entry testing at line 1
(10)     [files] = ok
(10)     [expiration] = noop
(10)     [logintime] = noop
(10) pap: WARNING: Auth-Type already set.  Not setting to PAP
(10)     [pap] = noop
(10)   } # authorize = updated
(10) Found Auth-Type = eap
(10) # Executing group from file /etc/raddb/sites-enabled/default
(10)   authenticate {
(10) eap: Expiring EAP session with state 0xd5e4295ed7e7247f
(10) eap: Finished EAP session with state 0xd5e4295ed7e7247f
(10) eap: Previous EAP request found for state 0xd5e4295ed7e7247f, released from the list
(10) eap: Peer sent packet with method EAP TLS (13)
(10) eap: Calling submodule eap_tls to process data
(10) eap_tls: Continuing EAP-TLS
(10) eap_tls: Peer ACKed our handshake fragment
(10) eap_tls: [eaptls verify] = request
(10) eap_tls: [eaptls process] = handled
(10) eap: Sending EAP Request (code 1) ID 4 length 1004
(10) eap: EAP session adding &reply:State = 0xd5e4295ed6e0247f
(10)     [eap] = handled
(10)   } # authenticate = handled
(10) Using Post-Auth-Type Challenge
(10) # Executing group from file /etc/raddb/sites-enabled/default
(10)   Challenge { ... } # empty sub-section is ignored
(10) Sent Access-Challenge Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 0
(10)   EAP-Message = 0x010403ec0dc000000b628ce599d11d884b8445126034dddeb72314d5a4f55aca9f683876fa84a2dc015f689284587b8db0fd0ee15113ec74b8f6ed72f040c603bcb3386bb2dcacb94526c9ee5cd320c0126597d6e5ba280004fe308204fa308203e2a003020102021415d8945c1d0c0e92ca36371461de2bf381dd9d02300d06092a864886f70d01010b0500308193310b3009060355040613024652310f300d06035504080c065261646975733112301006035504070c09536f6d65776865726531153013060355040a0c0c4578616d706c6520496e632e3120301e06092a864886f70d010901161161646d696e406578616d706c652e6f72673126302406035504030c1d4578616d706c6520436572746966696361746520417574686f72697479301e170d3230303132313231353730315a170d3230303332313231353730315a308193310b3009060355040613024652310f300d06035504080c065261646975733112301006035504070c09536f6d657768657265
(10)   Message-Authenticator = 0x00000000000000000000000000000000
(10)   State = 0xd5e4295ed6e0247f074246c6f7330379
(10) Finished request
Waking up in 4.9 seconds.
(10) Cleaning up request packet ID 3 with timestamp +22
(11) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 139
(11)   User-Name = "testing"
(11)   NAS-IP-Address = 192.168.1.1
(11)   Called-Station-Id = "1c872c6ac380"
(11)   Calling-Station-Id = "2aa9892665c7"
(11)   NAS-Identifier = "1c872c6ac380"
(11)   NAS-Port = 4
(11)   Framed-MTU = 1400
(11)   State = 0xd5e4295ed6e0247f074246c6f7330379
(11)   NAS-Port-Type = Wireless-802.11
(11)   EAP-Message = 0x020400060d00
(11)   Message-Authenticator = 0xdfc7b5f49fa1604293c2700c9bb3b9e0
(11) session-state: No cached attributes
(11) # Executing section authorize from file /etc/raddb/sites-enabled/default
(11)   authorize {
(11)     policy filter_username {
(11)       if (&User-Name) {
(11)       if (&User-Name)  -> TRUE
(11)       if (&User-Name)  {
(11)         if (&User-Name =~ / /) {
(11)         if (&User-Name =~ / /)  -> FALSE
(11)         if (&User-Name =~ /@[^@]*@/ ) {
(11)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(11)         if (&User-Name =~ /\.\./ ) {
(11)         if (&User-Name =~ /\.\./ )  -> FALSE
(11)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(11)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(11)         if (&User-Name =~ /\.$/)  {
(11)         if (&User-Name =~ /\.$/)   -> FALSE
(11)         if (&User-Name =~ /@\./)  {
(11)         if (&User-Name =~ /@\./)   -> FALSE
(11)       } # if (&User-Name)  = notfound
(11)     } # policy filter_username = notfound
(11)     [preprocess] = ok
(11)     [chap] = noop
(11)     [mschap] = noop
(11)     [digest] = noop
(11) suffix: Checking for suffix after "@"
(11) suffix: No '@' in User-Name = "testing", looking up realm NULL
(11) suffix: No such realm "NULL"
(11)     [suffix] = noop
(11) eap: Peer sent EAP Response (code 2) ID 4 length 6
(11) eap: No EAP Start, assuming it's an on-going EAP conversation
(11)     [eap] = updated
(11) files: users: Matched entry testing at line 1
(11)     [files] = ok
(11)     [expiration] = noop
(11)     [logintime] = noop
(11) pap: WARNING: Auth-Type already set.  Not setting to PAP
(11)     [pap] = noop
(11)   } # authorize = updated
(11) Found Auth-Type = eap
(11) # Executing group from file /etc/raddb/sites-enabled/default
(11)   authenticate {
(11) eap: Expiring EAP session with state 0xd5e4295ed6e0247f
(11) eap: Finished EAP session with state 0xd5e4295ed6e0247f
(11) eap: Previous EAP request found for state 0xd5e4295ed6e0247f, released from the list
(11) eap: Peer sent packet with method EAP TLS (13)
(11) eap: Calling submodule eap_tls to process data
(11) eap_tls: Continuing EAP-TLS
(11) eap_tls: Peer ACKed our handshake fragment
(11) eap_tls: [eaptls verify] = request
(11) eap_tls: [eaptls process] = handled
(11) eap: Sending EAP Request (code 1) ID 5 length 936
(11) eap: EAP session adding &reply:State = 0xd5e4295ed1e1247f
(11)     [eap] = handled
(11)   } # authenticate = handled
(11) Using Post-Auth-Type Challenge
(11) # Executing group from file /etc/raddb/sites-enabled/default
(11)   Challenge { ... } # empty sub-section is ignored
(11) Sent Access-Challenge Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 0
(11)   EAP-Message = 0x010503a80d8000000b620c0e92ca36371461de2bf381dd9d02300f0603551d130101ff040530030101ff30360603551d1f042f302d302ba029a0278625687474703a2f2f7777772e6578616d706c652e6f72672f6578616d706c655f63612e63726c300d06092a864886f70d01010b050003820101005fe53ceeb060ada3be502fd29fd37b5a90c75b0d7084232a1270778619fa3f5d17096c1c0a62372a37742086eaf07d29f6224a1a0e8f85ffedc5fd126d56deb2045915d95d1ec9a85211b3a4021b30fc63c45eb2e36c94ac73a6214fc0e1792a0b31c4f6559259ca0c56dacd01a89020e00d4f4b3030fcaaea7206a454e49abed34a0796d5ee3f8a23f6975690854aea2d45e7137b71b0b8ae116ee8e37b9ad7e1f97592dfd1dad45de4138812aa6a6a270a4d48cefb4bb3d9319a6e4aeab4ddf65aef3addc839ef645e6e3b8f667da016a448b6202827c767166e0ff5bf91f947269110a7b8652d12b4235a5f7ee36033ac33a00e87398cc6bf5b1b79eec0e416
(11)   Message-Authenticator = 0x00000000000000000000000000000000
(11)   State = 0xd5e4295ed1e1247f074246c6f7330379
(11) Finished request
Waking up in 4.9 seconds.
(11) Cleaning up request packet ID 3 with timestamp +22
(12) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 1551
(12)   User-Name = "testing"
(12)   NAS-IP-Address = 192.168.1.1
(12)   Called-Station-Id = "1c872c6ac380"
(12)   Calling-Station-Id = "2aa9892665c7"
(12)   NAS-Identifier = "1c872c6ac380"
(12)   NAS-Port = 4
(12)   Framed-MTU = 1400
(12)   State = 0xd5e4295ed1e1247f074246c6f7330379
(12)   NAS-Port-Type = Wireless-802.11
(12)   EAP-Message = 0x020505800dc000000a6816030308d80b0008d40008d10003cd308203c9308202b1a003020102020106300d06092a864886f70d01010b0500308193310b3009060355040613024652310f300d06035504080c065261646975733112301006035504070c09536f6d65776865726531153013060355040a0c0c4578616d706c6520496e632e3120301e06092a864886f70d010901161161646d696e406578616d706c652e6f72673126302406035504030c1d4578616d706c6520436572746966696361746520417574686f72697479301e170d3230303132323231353035395a170d3230303332323231353035395a306b310b3009060355040613024652310f300d06035504080c0652616469757331153013060355040a0c0c4578616d706c6520496e632e3112301006035504030c0974657374696e672d313120301e06092a864886f70d010901161174657374696e672d31403132332e636f6d30820122300d06092a864886f70d01010105000382010f003082010a
(12)   Message-Authenticator = 0x49855b06055dd4deb5ca3796e6bad880
(12) session-state: No cached attributes
(12) # Executing section authorize from file /etc/raddb/sites-enabled/default
(12)   authorize {
(12)     policy filter_username {
(12)       if (&User-Name) {
(12)       if (&User-Name)  -> TRUE
(12)       if (&User-Name)  {
(12)         if (&User-Name =~ / /) {
(12)         if (&User-Name =~ / /)  -> FALSE
(12)         if (&User-Name =~ /@[^@]*@/ ) {
(12)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(12)         if (&User-Name =~ /\.\./ ) {
(12)         if (&User-Name =~ /\.\./ )  -> FALSE
(12)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(12)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(12)         if (&User-Name =~ /\.$/)  {
(12)         if (&User-Name =~ /\.$/)   -> FALSE
(12)         if (&User-Name =~ /@\./)  {
(12)         if (&User-Name =~ /@\./)   -> FALSE
(12)       } # if (&User-Name)  = notfound
(12)     } # policy filter_username = notfound
(12)     [preprocess] = ok
(12)     [chap] = noop
(12)     [mschap] = noop
(12)     [digest] = noop
(12) suffix: Checking for suffix after "@"
(12) suffix: No '@' in User-Name = "testing", looking up realm NULL
(12) suffix: No such realm "NULL"
(12)     [suffix] = noop
(12) eap: Peer sent EAP Response (code 2) ID 5 length 1408
(12) eap: No EAP Start, assuming it's an on-going EAP conversation
(12)     [eap] = updated
(12) files: users: Matched entry testing at line 1
(12)     [files] = ok
(12)     [expiration] = noop
(12)     [logintime] = noop
(12) pap: WARNING: Auth-Type already set.  Not setting to PAP
(12)     [pap] = noop
(12)   } # authorize = updated
(12) Found Auth-Type = eap
(12) # Executing group from file /etc/raddb/sites-enabled/default
(12)   authenticate {
(12) eap: Expiring EAP session with state 0xd5e4295ed1e1247f
(12) eap: Finished EAP session with state 0xd5e4295ed1e1247f
(12) eap: Previous EAP request found for state 0xd5e4295ed1e1247f, released from the list
(12) eap: Peer sent packet with method EAP TLS (13)
(12) eap: Calling submodule eap_tls to process data
(12) eap_tls: Continuing EAP-TLS
(12) eap_tls: Peer indicated complete TLS record size will be 2664 bytes
(12) eap_tls: Expecting 2 TLS record fragments
(12) eap_tls: Got first TLS record fragment (1398 bytes).  Peer indicated more fragments to follow
(12) eap_tls: [eaptls verify] = first fragment
(12) eap_tls: ACKing Peer's TLS record fragment
(12) eap_tls: [eaptls process] = handled
(12) eap: Sending EAP Request (code 1) ID 6 length 6
(12) eap: EAP session adding &reply:State = 0xd5e4295ed0e2247f
(12)     [eap] = handled
(12)   } # authenticate = handled
(12) Using Post-Auth-Type Challenge
(12) # Executing group from file /etc/raddb/sites-enabled/default
(12)   Challenge { ... } # empty sub-section is ignored
(12) Sent Access-Challenge Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 0
(12)   EAP-Message = 0x010600060d00
(12)   Message-Authenticator = 0x00000000000000000000000000000000
(12)   State = 0xd5e4295ed0e2247f074246c6f7330379
(12) Finished request
Waking up in 4.9 seconds.
(12) Cleaning up request packet ID 3 with timestamp +22
(13) Received Access-Request Id 3 from 192.168.1.1:38469 to 192.168.1.49:1812 length 1415
(13)   User-Name = "testing"
(13)   NAS-IP-Address = 192.168.1.1
(13)   Called-Station-Id = "1c872c6ac380"
(13)   Calling-Station-Id = "2aa9892665c7"
(13)   NAS-Identifier = "1c872c6ac380"
(13)   NAS-Port = 4
(13)   Framed-MTU = 1400
(13)   State = 0xd5e4295ed0e2247f074246c6f7330379
(13)   NAS-Port-Type = Wireless-802.11
(13)   EAP-Message = 0x020604f80d0082010a0282010100a394d6ab8f4a9a9f49ffe482c92471719fbf1fc18a71213bf6cddd8b8ad3f65ef0ae213c76b2d4c43874ed56eda0dda9b6173b30341f1682f94c90cde0c5b9adf2dc91b6d7f34bd7113c939336a482ece46f19a67f0b1868eb60c5366c251201f1fcff10146d36066f1542a6190a72a568287445c180f33ac5241afeaea9800a39b84947c09685d45d49b4e3738e3085bf0f697f630e47e0ab2fdcde04b381e6f2a5c0bfb137917f9ec5a0af67b112c032101a9d6b8dbd2e0c39c8a6bddfbae5741429edcbb2e87f4e3a1de3905cb39a9c511f847843a4204d522081079477d3f58038c8f5ca313721bf3800f1db2621a81733d4daa8396ca0bde8b7f5b4edcf0203010001a38201423082013e301d0603551d0e041604149bff906fb2216a3e6e3dbb4b0ee58747f9254c813081d30603551d230481cb3081c880149bff906fb2216a3e6e3dbb4b0ee58747f9254c81a18199a48196308193310b3009060355040613024652310f30
(13)   Message-Authenticator = 0x9a8879d808c366dfe9c0e3eafa61333b
(13) session-state: No cached attributes
(13) # Executing section authorize from file /etc/raddb/sites-enabled/default
(13)   authorize {
(13)     policy filter_username {
(13)       if (&User-Name) {
(13)       if (&User-Name)  -> TRUE
(13)       if (&User-Name)  {
(13)         if (&User-Name =~ / /) {
(13)         if (&User-Name =~ / /)  -> FALSE
(13)         if (&User-Name =~ /@[^@]*@/ ) {
(13)         if (&User-Name =~ /@[^@]*@/ )  -> FALSE
(13)         if (&User-Name =~ /\.\./ ) {
(13)         if (&User-Name =~ /\.\./ )  -> FALSE
(13)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
(13)         if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))   -> FALSE
(13)         if (&User-Name =~ /\.$/)  {
(13)         if (&User-Name =~ /\.$/)   -> FALSE
(13)         if (&User-Name =~ /@\./)  {
(13)         if (&User-Name =~ /@\./)   -> FALSE
(13)       } # if (&User-Name)  = notfound
(13)     } # policy filter_username = notfound
(13)     [preprocess] = ok
(13)     [chap] = noop
(13)     [mschap] = noop
(13)     [digest] = noop
(13) suffix: Checking for suffix after "@"
(13) suffix: No '@' in User-Name = "testing", looking up realm NULL
(13) suffix: No such realm "NULL"
(13)     [suffix] = noop
(13) eap: Peer sent EAP Response (code 2) ID 6 length 1272
(13) eap: No EAP Start, assuming it's an on-going EAP conversation
(13)     [eap] = updated
(13) files: users: Matched entry testing at line 1
(13)     [files] = ok
(13)     [expiration] = noop
(13)     [logintime] = noop
(13) pap: WARNING: Auth-Type already set.  Not setting to PAP
(13)     [pap] = noop
(13)   } # authorize = updated
(13) Found Auth-Type = eap
(13) # Executing group from file /etc/raddb/sites-enabled/default
(13)   authenticate {
(13) eap: Expiring EAP session with state 0xd5e4295ed0e2247f
(13) eap: Finished EAP session with state 0xd5e4295ed0e2247f
(13) eap: Previous EAP request found for state 0xd5e4295ed0e2247f, released from the list
(13) eap: Peer sent packet with method EAP TLS (13)
(13) eap: Calling submodule eap_tls to process data
(13) eap_tls: Continuing EAP-TLS
(13) eap_tls: Got final TLS record fragment (1266 bytes)
(13) eap_tls: [eaptls verify] = ok
(13) eap_tls: Done initial handshake
(13) eap_tls: TLS_accept: SSLv3/TLS write server done
(13) eap_tls: <<< recv TLS 1.2  [length 08d8] 
(13) eap_tls: TLS - Creating attributes from certificate OIDs
(13) eap_tls:   TLS-Cert-Serial := "15d8945c1d0c0e92ca36371461de2bf381dd9d02"
(13) eap_tls:   TLS-Cert-Expiration := "200321215701Z"
(13) eap_tls:   TLS-Cert-Subject := "/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.org/CN=Example Certificate Authority"
(13) eap_tls:   TLS-Cert-Issuer := "/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.org/CN=Example Certificate Authority"
(13) eap_tls:   TLS-Cert-Common-Name := "Example Certificate Authority"
(13) eap_tls: TLS - Creating attributes from certificate OIDs
(13) eap_tls:   TLS-Client-Cert-Serial := "06"
(13) eap_tls:   TLS-Client-Cert-Expiration := "200322215059Z"
(13) eap_tls:   TLS-Client-Cert-Subject := "/C=FR/ST=Radius/O=Example Inc./CN=testing-1/emailAddress=testing-1@123.com"
(13) eap_tls:   TLS-Client-Cert-Issuer := "/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.org/CN=Example Certificate Authority"
(13) eap_tls:   TLS-Client-Cert-Common-Name := "testing-1"
(13) eap_tls:   TLS-Client-Cert-X509v3-Extended-Key-Usage += "TLS Web Client Authentication"
(13) eap_tls:   TLS-Client-Cert-X509v3-Extended-Key-Usage-OID += "1.3.6.1.5.5.7.3.2"
(13) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(13) eap_tls: <<< recv TLS 1.2  [length 0046] 
(13) eap_tls: TLS_accept: SSLv3/TLS read client key exchange
(13) eap_tls: <<< recv TLS 1.2  [length 0108] 
(13) eap_tls: >>> send TLS 1.2  [length 0002] 
(13) eap_tls: ERROR: TLS Alert write:fatal:decrypt error
tls: TLS_accept: Error in error
(13) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read)
(13) eap_tls: ERROR: error:0407E088:rsa routines:RSA_verify_PKCS1_PSS_mgf1:salt length check failed
(13) eap_tls: ERROR: error:1417B07B:SSL routines:tls_process_cert_verify:bad signature
(13) eap_tls: ERROR: System call (I/O) error (-1)
(13) eap_tls: ERROR: TLS receive handshake failed during operation
(13) eap_tls: ERROR: [eaptls process] = fail
(13) eap: ERROR: Failed continuing EAP TLS (13) session.  EAP sub-module failed
(13) eap: Sending EAP Failure (code 4) ID 6 length 4
(13) eap: Failed in EAP select
(13)     [eap] = invalid
(13)   } # authenticate = invalid
(13) Failed to authenticate the user
(13) Using Post-Auth-Type Reject
(13) # Executing group from file /etc/raddb/sites-enabled/default
(13)   Post-Auth-Type REJECT {
(13) attr_filter.access_reject: EXPAND %{User-Name}
(13) attr_filter.access_reject:    --> testing
(13) attr_filter.access_reject: Matched entry DEFAULT at line 11
(13)     [attr_filter.access_reject] = updated
(13)     [eap] = noop
(13)     policy remove_reply_message_if_eap {
(13)       if (&reply:EAP-Message && &reply:Reply-Message) {
(13)       if (&reply:EAP-Message && &reply:Reply-Message)  -> FALSE
(13)       else {
(13)         [noop] = noop
(13)       } # else = noop
(13)     } # policy remove_reply_message_if_eap = noop
(13)   } # Post-Auth-Type REJECT = updated
(13) Delaying response for 1.000000 seconds
Waking up in 0.3 seconds.
Waking up in 0.6 seconds.
(13) Sending delayed response
(13) Sent Access-Reject Id 3 from 192.168.1.49:1812 to 192.168.1.1:38469 length 44
(13)   EAP-Message = 0x04060004
(13)   Message-Authenticator = 0x00000000000000000000000000000000
Waking up in 3.9 seconds.
(13) Cleaning up request packet ID 3 with timestamp +22

@dnoliver
Copy link
Author

#/bin/bash

set -euxo pipefail

cd tpm2-tss
git clean -fxd
git checkout 2.3.0
./bootstrap && ./configure && make -j4 && make install
cd ..

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/

cd tpm2-tools
git clean -fxd
git checkout 4.0.1
./bootstrap && ./configure && make -j4 && make install
cd ..

cd tpm2-pkcs11
git clean -fxd
git checkout 1.0
./bootstrap && ./configure && make -j4 && make install
cd tools && python3 setup.py install && cd ..
cd ..
[root@localhost ~]# ldd /usr/lib64/pkcs11/libtpm2_pkcs11.so
        linux-vdso.so.1 (0x00007ffe2f3e7000)
        libtss2-esys.so.0 => /usr/local/lib/libtss2-esys.so.0 (0x00007f95a2714000)
        libtss2-sys.so.0 => /usr/local/lib/libtss2-sys.so.0 (0x00007f95a26eb000)
        libtss2-mu.so.0 => /usr/local/lib/libtss2-mu.so.0 (0x00007f95a26a3000)
        libtss2-tctildr.so.0 => /usr/local/lib/libtss2-tctildr.so.0 (0x00007f95a269a000)
        libtss2-rc.so.0 => /usr/local/lib/libtss2-rc.so.0 (0x00007f95a2690000)
        libsqlite3.so.0 => /lib64/libsqlite3.so.0 (0x00007f95a2571000)
        libcrypto.so.1.1 => /lib64/libcrypto.so.1.1 (0x00007f95a228f000)
        libyaml-0.so.2 => /lib64/libyaml-0.so.2 (0x00007f95a226d000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f95a2267000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f95a2246000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f95a2080000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f95a27d5000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f95a1f3a000)
        libz.so.1 => /lib64/libz.so.1 (0x00007f95a1f1e000)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment