Problem description
I have been debugging an issue for a day now… I’ve read up what I found in the docs, in the forum and in the source - both client side and firmware.
For some legacy reasons I must be able to use 4096 bit RSA keys to access some sysems. 2048 bit RSA and ECC keys work fine, it is only 4096 RSA that fails, but that does consistently.
I’ve tried loading the key in different ways:
- trough the app (checking RSA1 slot, signature use)
onlykey-cli setkey
scripts/onlykey-cli-gpg-add-keys.py
The method of loading does not make a difference. The failure is exactly the same.
I’ve also tried resetting to factory, and reloading the firmware (Signed_OnlyKey_3_0_4_STD.txt
)
I am going to demonstrate with and SSH key, but the failure mode is the same with GPG too. The responses that should be 512 bytes (4096 bits), that is public key, signatures, etc, all end up being only 448 bytes. It seems that a 64 byte packet is missing. (It is usually the last 64 bytes, but not always; see below.)
Reproducing
$ dpkg --status openssh-client | grep Version
Version: 1:9.2p1-2+deb12u2
ssh-keygen -C user@host -b 4096 -f test4096
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): <no pass>
Enter same passphrase again: <no pass>
Your identification has been saved in test4096
Your public key has been saved in test4096.pub
The key fingerprint is:
SHA256:UPmSNN537rSnXxAQuz1zIN0LLOXB7xpnIw1gmTaU27c user@host
$ cat test4096
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEAvYZulN9JpWKlpOlr6qQB46pfzrVahhclN7Z1BCu4IlDdvHsAuHlo
kPv9SHuKHXN1deFdBuBD7Vxt7TGoQNVdUEAbDEBoWTrzbw/TZSqk/Huw6InlBZyK+LUtLu
3JE43RTK8cT1b8KIFIyHm5UOOoPaBtAKUU4DYT9uHu8FPKvgHOJB/QMkLwwnR1NztlKZCe
eAkPSKXx2pbS8PICdJh6vqvxZ+RhEs+k4fOdwe3zBnPDBWXK+i/OtRrFxyK3gtbe1UHyKQ
ax16OOL9I/cFRlmqfC0LjOTbSb6IqReyEXm7T9JLCVAwERzqijNiOzTtt+SQxmrX6N+Lin
6uGKRNMUEA13jjUc9C6OXaudzML+WBPvwBdhXAhrXUlMpFnNUgfQqMtopN3JWh4BkVe2Ir
ZuRiEVe9bV65A7jSzi0AfvQHbzPrSr+HpWbXAglSBS4g5M3n13wCuBaaJjpxWqKxK5NThk
fF+jzgKM2ntwr8Sg+jY1l+vxQyO8pOnOHUaFbcKI2s5OshiOgCMshJhuFFxeu4aAOBUBfz
XhvJIVXHLZKTpJfJvWr3S8UDArWKtN0SPTH8Yu7IrIl3uaVLRigwyXCpauDpboTB38BzQX
LStl3bdvLg2/RvPXthueqlx6wUQBpUnwJdpkw7QFUqLSNf7EVgDByI9kH4Fjy9Is+WnnvD
sAAAdAcJ6H43Ceh+MAAAAHc3NoLXJzYQAAAgEAvYZulN9JpWKlpOlr6qQB46pfzrVahhcl
N7Z1BCu4IlDdvHsAuHlokPv9SHuKHXN1deFdBuBD7Vxt7TGoQNVdUEAbDEBoWTrzbw/TZS
qk/Huw6InlBZyK+LUtLu3JE43RTK8cT1b8KIFIyHm5UOOoPaBtAKUU4DYT9uHu8FPKvgHO
JB/QMkLwwnR1NztlKZCeeAkPSKXx2pbS8PICdJh6vqvxZ+RhEs+k4fOdwe3zBnPDBWXK+i
/OtRrFxyK3gtbe1UHyKQax16OOL9I/cFRlmqfC0LjOTbSb6IqReyEXm7T9JLCVAwERzqij
NiOzTtt+SQxmrX6N+Lin6uGKRNMUEA13jjUc9C6OXaudzML+WBPvwBdhXAhrXUlMpFnNUg
fQqMtopN3JWh4BkVe2IrZuRiEVe9bV65A7jSzi0AfvQHbzPrSr+HpWbXAglSBS4g5M3n13
wCuBaaJjpxWqKxK5NThkfF+jzgKM2ntwr8Sg+jY1l+vxQyO8pOnOHUaFbcKI2s5OshiOgC
MshJhuFFxeu4aAOBUBfzXhvJIVXHLZKTpJfJvWr3S8UDArWKtN0SPTH8Yu7IrIl3uaVLRi
gwyXCpauDpboTB38BzQXLStl3bdvLg2/RvPXthueqlx6wUQBpUnwJdpkw7QFUqLSNf7EVg
DByI9kH4Fjy9Is+WnnvDsAAAADAQABAAACADko0bIRk78O/OE7SwJV24ID0OhhVr9pPJ7M
ZwdOfjbPR8jAGiwI5coQTcfuBm4yc8MNKIbZ3XK5dSfb2aBQGVgKNX4g8Le0qVWeIsX4sh
C551WCvTFPkLRIxCtKqOG+XFjx2SpulBdNQpu/5m2oc4W+nBFdOGoUtTm401zlEJj0p2zK
sXpvKPiksfNp9O0vwjN2nAbvkX05Dr5/ZtEuaZqW9fQ88y/+sv44gKRL9QdFo5Q9qpWBFK
kXPX39EPajgko0HOpVrA5atBflI5/nvEsD/KQBkKrr5TehjxXhGaMq10wY+6AP0wSOZMb/
xmTqmvBCDreVmxDf9DjJM+/k70AQIy5q6eRYihTTPiLkrj1J71w4KaP1sGiYHhKtwSG3Gb
eZnq50DsI/Zg+GC4ErB/m+j/E7uGcAm+w58Zfm83f3kRuBk3pnWHfA/UOXXLH9ZQ5gtsXF
Xz7d21GEW8OT/usMFZgEVpSF4+kGNRurbA/71F427c6AKVk4P5M3/6F2+E4R/u9IR8NjI7
v6UN06hUQghezFT0Yv3/l38tsPI1oYCVq908zd55nvPXaOhzJJN4DAPn+rwAGxqClRza7b
5lbWlxoleky2ogGqImm9KRJ9Y6wlNfTrUHrpf+OTTCFRJUDhRpvi3fyywFRS/ufLqMjo94
sJ3Icrz8gcPnmwA97NAAABAQCbCjjlav4g8PBNL1UxuJqft5YM2Lqt8Hsm2Eiu4wwZPJQm
oE49h8Yplp0dBLCK5pLvCLnNq0P5Ba+2eYZnMBMH3AW37Ik7xK9Tb/Ptzn25cevRDEq/6v
JxmhLkQU1P1rDwuq9mKPp7BwCNSLo5lwLGR1LM2aFqMFw7xVVFS0z6oUWq11Ed1N4FL44c
4Yt8fbKYCZW0049MCo1eOIp2s/A4GN+jMsL//zvmkfiyUOvSh/1FSo9DFhTJMQEQGEWqVj
LBZ8kHO0ef1f/MIioWp7H8aunNrcYySm7p7NoXh2PpOz3A32DBwYhRylIHgNiFeoyNsSFH
d85l8apnXOJgC8zCAAABAQD7QTqhVRSSy9qJNXk7cR36TxEYgXBENy0RZnAC1j9W8D8OYR
1D4uTKDsSwqldZdzevy3aQleOaAY1EDttNigC0bJQpGarsscdqXc6/YPop9JI3fLxfFSa5
ys0TkFcOLYYSy8OH669UT9EWzKGRJe8CvmqeW79UOeLbexC346xgd8RaXHvT9wqY+Zg1xg
8QHllpfIT44CIxwOi683NdUBug4Tq9JM6QDDXBaiaM8u9paTP2OjQDW2WKDcSQr0/PcvCa
yiOM3ubOr/6A6hDO4IVesDdwLaiTI1O8VMm9FBbHc65y9YcfsL/+0zPf9X7t+pCm9mHX3E
T83B3xCBtuCNpnAAABAQDBGsBR4lf9inOtuy8eIn9e8bBhk382bZMKbE+HJFwNwVoU6saO
nCSfcoMi6tRCt1yw0nOg7kn9gahvMWE2WluMMSg1T/NpHt4SfgaAcK44/2fiG1YeYc5dzX
NjaTr7T56ONPjTYrRBbJp/bhfX5sj3dHt+/r/qAyHGgk3pSlSwk9LBkQskVZ2+YWFTuzpM
YY66j2n+XCHoi48wpSHVMRFw4AUBuqT2X8PTWmScjFWYvdzRPc2Dp76k20PsuCVJ4hHggs
auqyLXeFNTosRii8LEILKYBiu3yAKHWex/QiI+Cp57VpbPPxjhiT8QvR2B6kd35tid55FB
8EkypiJRaRMNAAAACXVzZXJAaG9zdAE=
-----END OPENSSH PRIVATE KEY-----
$ cat test4096.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hm6U30mlYqWk6WvqpAHjql/OtVqGFyU3tnUEK7giUN28ewC4eWiQ+/1Ie4odc3V14V0G4EPtXG3tMahA1V1QQBsMQGhZOvNvD9NlKqT8e7DoieUFnIr4tS0u7ckTjdFMrxxPVvwogUjIeblQ46g9oG0ApRTgNhP24e7wU8q+Ac4kH9AyQvDCdHU3O2UpkJ54CQ9IpfHaltLw8gJ0mHq+q/Fn5GESz6Th853B7fMGc8MFZcr6L861GsXHIreC1t7VQfIpBrHXo44v0j9wVGWap8LQuM5NtJvoipF7IRebtP0ksJUDARHOqKM2I7NO235JDGatfo34uKfq4YpE0xQQDXeONRz0Lo5dq53Mwv5YE+/AF2FcCGtdSUykWc1SB9Coy2ik3claHgGRV7Yitm5GIRV71tXrkDuNLOLQB+9AdvM+tKv4elZtcCCVIFLiDkzefXfAK4FpomOnFaorErk1OGR8X6POAozae3CvxKD6NjWX6/FDI7yk6c4dRoVtwojazk6yGI6AIyyEmG4UXF67hoA4FQF/NeG8khVcctkpOkl8m9avdLxQMCtYq03RI9Mfxi7sisiXe5pUtGKDDJcKlq4OluhMHfwHNBctK2Xdt28uDb9G89e2G56qXHrBRAGlSfAl2mTDtAVSotI1/sRWAMHIj2QfgWPL0iz5aee8Ow== user@host
I load the key using the app. First wiping RSA1, then loading with signature use checked.
Now trying to use it:
$ pip3 list
Package Version
---------------------- ------------
aenum 3.1.15
backports.shutil_which 3.5.2
bech32 1.2.0
certifi 2024.7.4
cffi 1.16.0
charset-normalizer 3.3.2
click 8.1.7
ConfigArgParse 1.7
cryptography 43.0.0
Cython 3.0.10
docutils 0.21.2
ecdsa 0.19.0
fido2 0.9.3
hidapi 0.14.0.post2
idna 3.7
intelhex 2.3.0
lib-agent 1.0.6
lockfile 0.12.2
mnemonic 0.21
onlykey 1.2.10
onlykey-agent 1.1.15
onlykey-solo-python 0.0.32
PGPy 0.6.0
pip 23.0.1
prompt_toolkit 3.0.47
pyasn1 0.6.0
pycparser 2.22
pycryptodome 3.20.0
PyMsgBox 1.0.9
PyNaCl 1.5.0
pyserial 3.5
python-daemon 3.0.1
pyusb 1.2.1
requests 2.32.3
semver 3.0.2
setuptools 66.1.1
six 1.16.0
Unidecode 1.3.8
urllib3 2.2.2
wcwidth 0.2.13
wheel 0.44.0
$ onlykey-agent a@b -v -e rsa -sk RSA1 -s
2024-08-05 15:19:44,999 INFO identity #0: <ssh://a@b|rsa> [__init__.py:287]
2024-08-05 15:19:45,003 INFO running '/bin/bash' with {'SSH_AUTH_SOCK': '/tmp/trezor-ssh-agent-6xop7fg_', 'SSH_AGENT_PID': '1186117'} [server.py:156]
(subshell)$ ssh-add -l
2024-08-05 15:19:47,484 INFO Key Slot =1 [onlykey.py:160]
2024-08-05 15:19:47,485 INFO Requesting public key from key slot =1 [onlykey.py:164]
2024-08-05 15:19:47,485 INFO Identity to hash =b'a@b' [onlykey.py:178]
2024-08-05 15:19:47,485 INFO Identity hash =7508d8b5018ea640b85269861a101203f0c26900555268e930025dac844b0f35 [onlykey.py:182]
2024-08-05 15:19:47,486 INFO curve name= 'rsa' [onlykey.py:198]
2024-08-05 15:19:47,539 INFO received part= [189, 134, 110, 148, 223, 73, 165, 98, 165, 164, 233, 107, 234, 164, 1, 227, 170, 95, 206, 181, 90, 134, 23, 37, 55, 182, 117, 4, 43, 184, 34, 80, 221, 188, 123, 0, 184, 121, 104, 144, 251, 253, 72, 123, 138, 29, 115, 117, 117, 225, 93, 6, 224, 67, 237, 92, 109, 237, 49, 168, 64, 213, 93, 80] [onlykey.py:242]
2024-08-05 15:19:47,540 INFO received part= [64, 27, 12, 64, 104, 89, 58, 243, 111, 15, 211, 101, 42, 164, 252, 123, 176, 232, 137, 229, 5, 156, 138, 248, 181, 45, 46, 237, 201, 19, 141, 209, 76, 175, 28, 79, 86, 252, 40, 129, 72, 200, 121, 185, 80, 227, 168, 61, 160, 109, 0, 165, 20, 224, 54, 19, 246, 225, 238, 240, 83, 202, 190, 1] [onlykey.py:242]
2024-08-05 15:19:47,541 INFO received part= [206, 36, 31, 208, 50, 66, 240, 194, 116, 117, 55, 59, 101, 41, 144, 158, 120, 9, 15, 72, 165, 241, 218, 150, 210, 240, 242, 2, 116, 152, 122, 190, 171, 241, 103, 228, 97, 18, 207, 164, 225, 243, 157, 193, 237, 243, 6, 115, 195, 5, 101, 202, 250, 47, 206, 181, 26, 197, 199, 34, 183, 130, 214, 222] [onlykey.py:242]
2024-08-05 15:19:47,542 INFO received part= [213, 65, 242, 41, 6, 177, 215, 163, 142, 47, 210, 63, 112, 84, 101, 154, 167, 194, 208, 184, 206, 77, 180, 155, 232, 138, 145, 123, 33, 23, 155, 180, 253, 36, 176, 149, 3, 1, 17, 206, 168, 163, 54, 35, 179, 78, 219, 126, 73, 12, 102, 173, 126, 141, 248, 184, 167, 234, 225, 138, 68, 211, 20, 16] [onlykey.py:242]
2024-08-05 15:19:47,543 INFO received part= [13, 119, 142, 53, 28, 244, 46, 142, 93, 171, 157, 204, 194, 254, 88, 19, 239, 192, 23, 97, 92, 8, 107, 93, 73, 76, 164, 89, 205, 82, 7, 208, 168, 203, 104, 164, 221, 201, 90, 30, 1, 145, 87, 182, 34, 182, 110, 70, 33, 21, 123, 214, 213, 235, 144, 59, 141, 44, 226, 208, 7, 239, 64, 118] [onlykey.py:242]
2024-08-05 15:19:47,544 INFO received part= [243, 62, 180, 171, 248, 122, 86, 109, 112, 32, 149, 32, 82, 226, 14, 76, 222, 125, 119, 192, 43, 129, 105, 162, 99, 167, 21, 170, 43, 18, 185, 53, 56, 100, 124, 95, 163, 206, 2, 140, 218, 123, 112, 175, 196, 160, 250, 54, 53, 151, 235, 241, 67, 35, 188, 164, 233, 206, 29, 70, 133, 109, 194, 136] [onlykey.py:242]
2024-08-05 15:19:47,545 INFO received part= [150, 174, 14, 150, 232, 76, 29, 252, 7, 52, 23, 45, 43, 101, 221, 183, 111, 46, 13, 191, 70, 243, 215, 182, 27, 158, 170, 92, 122, 193, 68, 1, 165, 73, 240, 37, 218, 100, 195, 180, 5, 82, 162, 210, 53, 254, 196, 86, 0, 193, 200, 143, 100, 31, 129, 99, 203, 210, 44, 249, 105, 231, 188, 59] [onlykey.py:242]
2024-08-05 15:19:49,048 INFO Received Public Key generated by OnlyKey= [189, 134, 110, 148, 223, 73, 165, 98, 165, 164, 233, 107, 234, 164, 1, 227, 170, 95, 206, 181, 90, 134, 23, 37, 55, 182, 117, 4, 43, 184, 34, 80, 221, 188, 123, 0, 184, 121, 104, 144, 251, 253, 72, 123, 138, 29, 115, 117, 117, 225, 93, 6, 224, 67, 237, 92, 109, 237, 49, 168, 64, 213, 93, 80, 64, 27, 12, 64, 104, 89, 58, 243, 111, 15, 211, 101, 42, 164, 252, 123, 176, 232, 137, 229, 5, 156, 138, 248, 181, 45, 46, 237, 201, 19, 141, 209, 76, 175, 28, 79, 86, 252, 40, 129, 72, 200, 121, 185, 80, 227, 168, 61, 160, 109, 0, 165, 20, 224, 54, 19, 246, 225, 238, 240, 83, 202, 190, 1, 206, 36, 31, 208, 50, 66, 240, 194, 116, 117, 55, 59, 101, 41, 144, 158, 120, 9, 15, 72, 165, 241, 218, 150, 210, 240, 242, 2, 116, 152, 122, 190, 171, 241, 103, 228, 97, 18, 207, 164, 225, 243, 157, 193, 237, 243, 6, 115, 195, 5, 101, 202, 250, 47, 206, 181, 26, 197, 199, 34, 183, 130, 214, 222, 213, 65, 242, 41, 6, 177, 215, 163, 142, 47, 210, 63, 112, 84, 101, 154, 167, 194, 208, 184, 206, 77, 180, 155, 232, 138, 145, 123, 33, 23, 155, 180, 253, 36, 176, 149, 3, 1, 17, 206, 168, 163, 54, 35, 179, 78, 219, 126, 73, 12, 102, 173, 126, 141, 248, 184, 167, 234, 225, 138, 68, 211, 20, 16, 13, 119, 142, 53, 28, 244, 46, 142, 93, 171, 157, 204, 194, 254, 88, 19, 239, 192, 23, 97, 92, 8, 107, 93, 73, 76, 164, 89, 205, 82, 7, 208, 168, 203, 104, 164, 221, 201, 90, 30, 1, 145, 87, 182, 34, 182, 110, 70, 33, 21, 123, 214, 213, 235, 144, 59, 141, 44, 226, 208, 7, 239, 64, 118, 243, 62, 180, 171, 248, 122, 86, 109, 112, 32, 149, 32, 82, 226, 14, 76, 222, 125, 119, 192, 43, 129, 105, 162, 99, 167, 21, 170, 43, 18, 185, 53, 56, 100, 124, 95, 163, 206, 2, 140, 218, 123, 112, 175, 196, 160, 250, 54, 53, 151, 235, 241, 67, 35, 188, 164, 233, 206, 29, 70, 133, 109, 194, 136, 150, 174, 14, 150, 232, 76, 29, 252, 7, 52, 23, 45, 43, 101, 221, 183, 111, 46, 13, 191, 70, 243, 215, 182, 27, 158, 170, 92, 122, 193, 68, 1, 165, 73, 240, 37, 218, 100, 195, 180, 5, 82, 162, 210, 53, 254, 196, 86, 0, 193, 200, 143, 100, 31, 129, 99, 203, 210, 44, 249, 105, 231, 188, 59] [onlykey.py:250]
2024-08-05 15:19:49,048 INFO 448 [onlykey.py:251]
2024-08-05 15:19:49,048 INFO disconnected from OnlyKey [onlykey.py:139]
error fetching identities: communication with agent failed
2024-08-05 15:19:49,051 WARNING error: Error response length is not a valid public key [server.py:100]
Traceback (most recent call last):
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/server.py", line 95, in handle_connection
reply = handler.handle(msg=msg)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/ssh/protocol.py", line 106, in handle
reply = method(buf=buf)
^^^^^^^^^^^^^^^
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/ssh/protocol.py", line 114, in list_pubs
keys = self.conn.parse_public_keys()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/ssh/__init__.py", line 227, in parse_public_keys
for pk in self.public_keys()]
^^^^^^^^^^^^^^^^^^
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/ssh/__init__.py", line 221, in public_keys
self.public_keys_cache = conn.export_public_keys(self.identities)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/ssh/client.py", line 26, in export_public_keys
vk = self.device.pubkey(identity=i)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ocsi/opt/onlykey/lib/python3.11/site-packages/libagent/device/onlykey.py", line 271, in pubkey
raise interface.DeviceError("Error response length is not a valid public key")
libagent.device.interface.DeviceError: Error response length is not a valid public key
Debugging client side
As printed in the debug, the response to the public key request was 448 bytes, instead of 512, which lead to the failure. I’ve done some digging, and the first 448 printed above is correct:
$ { echo "ibase=16";
cut -d ' ' -f2 test4096.pub | base64 --decode \
| xxd -p | sed 's/../&\n/g' | grep -v '^$' | tr '[:lower:]' '[:upper:]'; } \
| bc -q | tail -n+24 | tr '\n' ','; echo
189,134,110,148,223,73,165,98,165,164,233,107,234,164,1,227,170,95,206,181,90,134,23,37,55,182,117,4,43,184,34,80,221,188,123,0,184,121,104,144,251,253,72,123,138,29,115,117,117,225,93,6,224,67,237,92,109,237,49,168,64,213,93,80,64,27,12,64,104,89,58,243,111,15,211,101,42,164,252,123,176,232,137,229,5,156,138,248,181,45,46,237,201,19,141,209,76,175,28,79,86,252,40,129,72,200,121,185,80,227,168,61,160,109,0,165,20,224,54,19,246,225,238,240,83,202,190,1,206,36,31,208,50,66,240,194,116,117,55,59,101,41,144,158,120,9,15,72,165,241,218,150,210,240,242,2,116,152,122,190,171,241,103,228,97,18,207,164,225,243,157,193,237,243,6,115,195,5,101,202,250,47,206,181,26,197,199,34,183,130,214,222,213,65,242,41,6,177,215,163,142,47,210,63,112,84,101,154,167,194,208,184,206,77,180,155,232,138,145,123,33,23,155,180,253,36,176,149,3,1,17,206,168,163,54,35,179,78,219,126,73,12,102,173,126,141,248,184,167,234,225,138,68,211,20,16,13,119,142,53,28,244,46,142,93,171,157,204,194,254,88,19,239,192,23,97,92,8,107,93,73,76,164,89,205,82,7,208,168,203,104,164,221,201,90,30,1,145,87,182,34,182,110,70,33,21,123,214,213,235,144,59,141,44,226,208,7,239,64,118,243,62,180,171,248,122,86,109,112,32,149,32,82,226,14,76,222,125,119,192,43,129,105,162,99,167,21,170,43,18,185,53,56,100,124,95,163,206,2,140,218,123,112,175,196,160,250,54,53,151,235,241,67,35,188,164,233,206,29,70,133,109,194,136,218,206,78,178,24,142,128,35,44,132,152,110,20,92,94,187,134,128,56,21,1,127,53,225,188,146,21,92,114,217,41,58,73,124,155,214,175,116,188,80,48,43,88,171,77,209,35,211,31,198,46,236,138,200,151,123,154,84,180,98,131,12,151,10,150,174,14,150,232,76,29,252,7,52,23,45,43,101,221,183,111,46,13,191,70,243,215,182,27,158,170,92,122,193,68,1,165,73,240,37,218,100,195,180,5,82,162,210,53,254,196,86,0,193,200,143,100,31,129,99,203,210,44,249,105,231,188,59
When comparing the correct public key with debug output of the agent, you can see that 7th 64 byte packet (out of 8 packets) is missing:
218,206,78,178,24,142,128,35,44,132,152,110,20,92,94,187,134,128,56,21,1,127,53,225,188,146,21,92,114,217,41,58,73,124,155,21,4,175,116,188,80,48,43,88,171,77,209,35,211,31,198,46,236,138,200,151,123,154,84,180,98,131,12,151,10
After this I’ve edited libagent/device/onlykey.py:263
so the correct public key is returned. Now ssh-add -l
shows the correct public key, identical to test4096.pub
. However, ssh still does not work, because the signatures also miss random 64 byte packets. (Again, only with 4k RSA keys, 2k RSA and ECC work correctly; I suspect this is because the keys and signatures are smaller.)
I’ve digged trough the agent code first, and it seems correct. Tried to tweak some timeouts, but that did not make a difference.
Debugging firmware
Then looked at the firmware code (and the libraries). I think the problem is in here.
// okcrypto.cpp
void okcrypto_getrsapubkey (uint8_t *buffer) {
send_transport_response(rsa_publicN, (type*128), true, true);
} // type == 4 for 4096 RSA
void okcrypto_rsasign (uint8_t *buffer) {
// ...
send_transport_response(rsa_signature, (type*128), true, true);
// ...
}
// okcore.cpp
// len == 4*128 == 512
void send_transport_response(uint8_t *data, int len, uint8_t encrypt, uint8_t store)
{
// ...
for (int i = 0; i < len; i += 64)
{
if (len-i>=64) {
memcpy(resp_buffer, data+i, 64);
}
else {
memcpy(resp_buffer, data+i, len-i);
}
// ...
RawHID.send2(resp_buffer, 0);
}
// ...
}
The loop itself for working the buffer is correct (only the 1st branch is called, because 512 is a multiple of 64).
#define RAWHID_TX_SIZE 64 // in usb_desc.h
#define TX_PACKET_LIMIT 4 // in usb_rawhid.c
// usb_rawhid.c
// timeout==0
int usb_rawhid_send2(const void *buffer, uint32_t timeout)
{
usb_packet_t *tx_packet;
uint32_t begin = millis();
while (1) {
if (!usb_configuration) return -1;
if (usb_tx_packet_count(RAWHID_TX_ENDPOINT2) < TX_PACKET_LIMIT) {
tx_packet = usb_malloc();
if (tx_packet) break;
}
if (millis() - begin > timeout) return 0;
yield();
}
memcpy(tx_packet->buf, buffer, RAWHID_TX_SIZE);
tx_packet->len = RAWHID_TX_SIZE;
usb_tx(RAWHID_TX_ENDPOINT2, tx_packet);
return RAWHID_TX_SIZE;
}
I think this shows the reason why 2k RSA works with: 4*64 = 256 bytes = 2048 bits, so there is always enough room to send that amount (unless some other comm is going on at the same time).
But when there is 512 bytes to TX, only the first few packets (usually 6-7) make it before usb_tx_packet_count(RAWHID_TX_ENDPOINT2) < TX_PACKET_LIMIT)
becomes false, sending control to:
if (millis() - begin > timeout) return 0;
which has an ever increasing chance of timing out and returning, since the timeout
is set to 0
, so all packets would need to be TX’d in less than 1ms, which usually does not happen. (At least, not on my system. Tried different USB ports, etc.)
Possible fix
In okcore.cpp:void send_transport_response
the calls to RawHID.send2
should include a reasonable timeout, say 10ms: RawHID.send2(resp_buffer, 10);
.
Unfortunately I was not able to successfully compile the firmware to test this. It would be nice if some instructions to bring up a working build from scratch would be included.
One more thing: OnlyKey is awesome. Thank you for your work!