├── ecc_private.key ├── tests ├── cli.py ├── test_pub.asc ├── ssh_auth_ed25519.py ├── rsa_decrypt_testkey.py ├── rsa_decrypt_1024.py ├── ecdh_secp256k1.py ├── ecdh_p256.py ├── rsa_decrypt_2048.py ├── ssh_auth_rsa_1024.py ├── ecdh_curve25519.py ├── rsa_decrypt_3072.py ├── test_priv.asc ├── rsa_decrypt_4096.py └── PGP_message.py ├── onlykey ├── __init__.py ├── client.py └── cli.py ├── .gitmodules ├── .gitignore ├── setup.py ├── scripts ├── PGPparseprivate.py └── onlykey-cli-gpg-add-keys.py └── README.md /ecc_private.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustcrypto/python-onlykey/HEAD/ecc_private.key -------------------------------------------------------------------------------- /tests/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/opt/python/bin/python2.7 2 | import sys 3 | import onlykey 4 | from onlykey.cli import main 5 | 6 | main() 7 | -------------------------------------------------------------------------------- /onlykey/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import absolute_import 3 | from .client import OnlyKey, Message, MessageField # Make `OnlyKey` available -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "PGPy"] 2 | path = PGPy 3 | url = https://github.com/trustcrypto/PGPy.git 4 | branch = master 5 | [submodule "onlykey-solo-python"] 6 | path = onlykey-solo-python 7 | url = https://github.com/trustcrypto/onlykey-solo-python 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | onlykey.egg* 3 | .ropeproject 4 | cython-hidapi 5 | 6 | build/lib/onlykey/__init__.py 7 | 8 | build/lib/onlykey/cli.py 9 | 10 | build/lib/onlykey/client.py 11 | 12 | dist/onlykey-0.0.1-py2.7.egg 13 | 14 | tests/signedfw.txt 15 | 16 | tests/fw_sign_ed25519.py 17 | 18 | tests/inputfw.txt 19 | 20 | 21 | *.gz 22 | 23 | *.whl 24 | 25 | *.whl 26 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from codecs import open 3 | from os import path 4 | 5 | here = path.abspath(path.dirname(__file__)) 6 | 7 | # Get the long description from the README file 8 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 9 | long_description = f.read() 10 | 11 | setup( 12 | name='onlykey', 13 | version='1.2.10', 14 | description='OnlyKey client and command-line tool', 15 | # long_description=long_description, 16 | url='https://github.com/trustcrypto/python-onlykey', 17 | author='CryptoTrust', 18 | author_email='admin@crp.to', 19 | license='MIT', 20 | packages=find_packages(exclude=['contrib', 'docs', 'tests']), 21 | entry_points = { 22 | 'console_scripts': [ 23 | 'onlykey-cli=onlykey.cli:main' 24 | ], 25 | }, 26 | install_requires=['hidapi', 'aenum', 'six', 'prompt_toolkit>=2', 'pynacl>=1.4.0', 'ecdsa>=0.13', 'Cython>=0.23.4', 'onlykey-solo-python>=0.0.31'], 27 | ) 28 | -------------------------------------------------------------------------------- /tests/test_pub.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: Mailvelope v1.7.1 3 | Comment: https://www.mailvelope.com 4 | 5 | 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 54 | =t3Tu 55 | -----END PGP PUBLIC KEY BLOCK----- 56 | 57 | -------------------------------------------------------------------------------- /tests/ssh_auth_ed25519.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | import ed25519 7 | 8 | from onlykey import OnlyKey, Message 9 | 10 | print('Generating a new ed25519 key pair...') 11 | privkey, pubkey = ed25519.create_keypair(entropy=os.urandom) 12 | print('Done') 13 | print() 14 | print('privkey=', repr(privkey.to_seed())) 15 | print('privkey hex=', privkey.to_seed().hex()) 16 | print('pubkey=', repr(pubkey.to_bytes())) 17 | print('pubkey hex=', pubkey.to_ascii(encoding='hex').decode("utf-8")) 18 | print() 19 | 20 | print() 21 | print('Initialize OnlyKey client...') 22 | ok = OnlyKey(connect=True) 23 | print('Done') 24 | print() 25 | 26 | time.sleep(2) 27 | 28 | ok.read_string(timeout_ms=100) 29 | empty = 'a' 30 | while not empty: 31 | empty = ok.read_string(timeout_ms=100) 32 | 33 | print('You should see your OnlyKey blink 3 times') 34 | print() 35 | 36 | print('Setting SSH private...') 37 | 38 | ok.set_ecc_key(101, (1+64), privkey.to_seed()) 39 | # ok.set_ecc_privsend_message(msg=Message.OKSETPRIV, payload=privkey.to_seed()) 40 | time.sleep(1.5) 41 | print(ok.read_string()) 42 | 43 | time.sleep(2) 44 | print('You should see your OnlyKey blink 3 times') 45 | print() 46 | 47 | print('Trying to read the pubkey...') 48 | ok.send_message(msg=Message.OKGETPUBKEY, payload=101, slot_id=65) #, payload=[1, 1]) 49 | time.sleep(1.5) 50 | for i in range(10): 51 | print(i) 52 | ok_pubkey = ok.read_bytes(32, to_bytes=True) 53 | print(ok_pubkey) 54 | if len(ok_pubkey) == 32: 55 | break 56 | time.sleep(1) 57 | 58 | print() 59 | print(ok_pubkey) 60 | print('received=', repr(ok_pubkey)) 61 | 62 | if not ok_pubkey: 63 | raise Exception('failed to set the SSH key') 64 | 65 | print('Assert that the received pubkey match the one generated locally') 66 | assert ok_pubkey == pubkey.to_bytes() 67 | print('Ok, pubkey matches') 68 | print() 69 | 70 | test_payload = os.urandom(150) 71 | print('test_payload=', repr(test_payload)) 72 | print() 73 | 74 | # Compute the challenge pin 75 | h = hashlib.sha256() 76 | h.update(test_payload) 77 | d = h.digest() 78 | 79 | assert len(d) == 32 80 | 81 | def get_button(byte): 82 | ibyte = ord(byte) 83 | if ibyte < 6: 84 | return 1 85 | return ibyte % 5 + 1 86 | 87 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 88 | 89 | print('Sending the payload to the OnlyKey...') 90 | ok.send_large_message2(msg=Message.OKSIGNCHALLENGE, payload=test_payload, slot_id=101) 91 | 92 | # Tim - The OnlyKey can send the code to enter but it would be better if the app generates 93 | # the code, this way in order to trick a user into approving an unauthorized signature 94 | # the app, in this case this python code would have to be hacked on the user's system. 95 | # How the OnlyKey creates the three digit code: 96 | # SHA256_CTX CRYPTO; 97 | # sha256_init(&CRYPTO); 98 | # sha256_update(&CRYPTO, large_buffer, large_data_offset); //step 1 create a sha256 hash of 99 | # sha256_final(&CRYPTO, rsa_signature); //the data to sign 100 | # if (rsa_signature[0] < 6) Challenge_button1 = '1'; //step 2 Convert first byte of hash to 101 | # else { //first button to press (remainder of byte is a base 6 number 0 - 5) 102 | # Challenge_button1 = rsa_signature[0] % 5; 103 | # Challenge_button1 = Challenge_button1 + '0' + 1; //Add '0' and 1 so number will be ASCII 1 - 6 104 | # } 105 | # if (rsa_signature[15] < 6) Challenge_button2 = '1'; //step 3 do the same with 16th byte to 106 | # else { // get Challenge_button2 107 | # Challenge_button2 = rsa_signature[15] % 5; 108 | # Challenge_button2 = Challenge_button2 + '0' + 1; 109 | #} 110 | # if (rsa_signature[31] < 6) Challenge_button3 = '1'; //step 4 do the same with 32nd byte to 111 | # else { // get Challenge_button 112 | # Challenge_button3 = rsa_signature[31] % 5; 113 | # Challenge_button3 = Challenge_button3 + '0' + 1; 114 | # } 115 | # step 5 display the code to user to enter on OnlyKey 116 | 117 | # This method prevents some malware on a users system from sending fake requests to be signed 118 | # at the same time as real requests and tricking the user into signing the wrong data 119 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 120 | print('{} {} {}'.format(b1, b2, b3)) 121 | input() 122 | signature = '' 123 | while signature == '': 124 | time.sleep(0.5) 125 | signature = ok.read_bytes(64, to_bytes=True) 126 | 127 | print('Signed by OnlyKey, signature=', repr(signature)) 128 | 129 | print('Local signature=', repr(privkey.sign(bytes(test_payload)))) 130 | print('Assert that the signature generated locally match the one generated on the OnlyKey') 131 | assert repr(signature) == repr(privkey.sign(bytes(test_payload))) 132 | print('Ok, signatures match') 133 | print() 134 | 135 | print('Done') 136 | -------------------------------------------------------------------------------- /tests/rsa_decrypt_testkey.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Cipher import PKCS1_v1_5 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Hash import SHA 9 | from Crypto.Random import get_random_bytes 10 | from Crypto import Random 11 | import binascii 12 | 13 | from onlykey import OnlyKey, Message 14 | 15 | 16 | print('Initialize OnlyKey client...') 17 | ok = OnlyKey() 18 | print('Done') 19 | print() 20 | 21 | time.sleep(2) 22 | 23 | ok.read_string(timeout_ms=100) 24 | empty = 'a' 25 | while not empty: 26 | empty = ok.read_string(timeout_ms=100) 27 | 28 | time.sleep(1) 29 | print('You should see your OnlyKey blink 3 times') 30 | print() 31 | 32 | print('Enter RSA slot number to use for decryption (1 - 4)') 33 | print() 34 | slot = int(raw_input()) 35 | 36 | print('Trying to read the public RSA N part 1...') 37 | ok.send_message(msg=Message.OKGETPUBKEY, payload=chr(slot)) #, payload=[1, 1]) 38 | time.sleep(1.5) 39 | for _ in xrange(10): 40 | ok_pubkey1 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 41 | if len(ok_pubkey1) == 64: 42 | break 43 | time.sleep(1) 44 | 45 | print() 46 | 47 | print('received=', repr(ok_pubkey1)) 48 | 49 | print('Trying to read the public RSA N part 2...') 50 | for _ in xrange(10): 51 | ok_pubkey2 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 52 | if len(ok_pubkey2) == 64: 53 | break 54 | time.sleep(1) 55 | 56 | print() 57 | 58 | print('received=', repr(ok_pubkey2)) 59 | 60 | print('Trying to read the public RSA N part 3...') 61 | for _ in xrange(10): 62 | ok_pubkey3 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 63 | if len(ok_pubkey3) == 64: 64 | break 65 | time.sleep(1) 66 | 67 | print() 68 | 69 | print('received=', repr(ok_pubkey3)) 70 | 71 | print('Trying to read the public RSA N part 4...') 72 | for _ in xrange(10): 73 | ok_pubkey4 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 74 | if len(ok_pubkey4) == 64: 75 | break 76 | time.sleep(1) 77 | 78 | print() 79 | 80 | print('received=', repr(ok_pubkey4)) 81 | 82 | print('Trying to read the public RSA N part 5...') 83 | for _ in xrange(10): 84 | ok_pubkey5 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 85 | if len(ok_pubkey5) == 64: 86 | break 87 | time.sleep(1) 88 | 89 | print() 90 | 91 | print('received=', repr(ok_pubkey5)) 92 | 93 | print('Trying to read the public RSA N part 6...') 94 | for _ in xrange(10): 95 | ok_pubkey6 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 96 | if len(ok_pubkey6) == 64: 97 | break 98 | time.sleep(1) 99 | 100 | print() 101 | 102 | print('received=', repr(ok_pubkey6)) 103 | 104 | print('Trying to read the public RSA N part 7...') 105 | for _ in xrange(10): 106 | ok_pubkey7 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 107 | if len(ok_pubkey7) == 64: 108 | break 109 | time.sleep(1) 110 | 111 | print() 112 | 113 | print('received=', repr(ok_pubkey7)) 114 | 115 | print('Trying to read the public RSA N part 8...') 116 | for _ in xrange(10): 117 | ok_pubkey8 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 118 | if len(ok_pubkey8) == 64: 119 | break 120 | time.sleep(1) 121 | 122 | print() 123 | 124 | print('received=', repr(ok_pubkey8)) 125 | 126 | if not ok_pubkey2: 127 | raise Exception('failed to set the RSA key') 128 | 129 | print('Received Public Key generated by OnlyKey') 130 | ok_pubkey = ok_pubkey1 + ok_pubkey2 + ok_pubkey3 + ok_pubkey4 + ok_pubkey5 + ok_pubkey6 + ok_pubkey7 + ok_pubkey8 131 | print('Public N=', repr(ok_pubkey)) 132 | print() 133 | 134 | print('Key Size =', len(ok_pubkey)) 135 | print() 136 | random_generator = Random.new().read 137 | key = RSA.generate((len(ok_pubkey)*8), random_generator) #generate pub and priv key 138 | ok_pubkey = ok_pubkey.encode("HEX") 139 | 140 | n = long(ok_pubkey, 16) 141 | e = int('10001', 16) 142 | 143 | key = RSA.construct((n, e)) 144 | 145 | print('N =', repr(key.n)) 146 | print() 147 | 148 | message = 'Secret message' 149 | #h = SHA.new(message) 150 | cipher = PKCS1_v1_5.new(key) 151 | ciphertext = cipher.encrypt(message) 152 | 153 | #hex_enc_data = bin2hex(enc_data) 154 | print('encrypted payload = ', repr(ciphertext)) 155 | print() 156 | 157 | 158 | # Compute the challenge pin 159 | h = hashlib.sha256() 160 | h.update(ciphertext) 161 | d = h.digest() 162 | 163 | assert len(d) == 32 164 | 165 | def get_button(byte): 166 | ibyte = ord(byte) 167 | if ibyte < 6: 168 | return 1 169 | return ibyte % 5 + 1 170 | 171 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 172 | 173 | print('Sending the payload to the OnlyKey...') 174 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=ciphertext, slot_id=slot) 175 | 176 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 177 | print('{} {} {}'.format(b1, b2, b3)) 178 | raw_input() 179 | print('Trying to read the decrypted data from OnlyKey...') 180 | ok_decrypted = '' 181 | while ok_decrypted == '': 182 | time.sleep(0.5) 183 | ok_decrypted = ok.read_bytes(len(message), to_bytes=True) 184 | 185 | print('Decrypted by OnlyKey, data=', repr(ok_decrypted)) 186 | 187 | print('Original Message =', repr(message)) 188 | print('Assert that the original message matches the data generated on the OnlyKey') 189 | assert repr(ok_decrypted) == repr(message) 190 | print('Ok, data matches') 191 | print() 192 | 193 | print('Done') 194 | -------------------------------------------------------------------------------- /tests/rsa_decrypt_1024.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Cipher import PKCS1_v1_5 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Hash import SHA 9 | from Crypto.Random import get_random_bytes 10 | from Crypto import Random 11 | import binascii 12 | 13 | from onlykey import OnlyKey, Message 14 | 15 | print('Generating a new rsa key pair...') 16 | random_generator = Random.new().read 17 | key = RSA.generate(1024, random_generator) #generate pub and priv key 18 | 19 | p = key.p 20 | q = key.q 21 | n = key.n 22 | 23 | binPrivKey = key.exportKey('DER') 24 | binPubKey = key.publickey().exportKey('DER') 25 | 26 | print('Done') 27 | print() 28 | print('RSA p value =', repr(p)) 29 | print('RSA q value =', repr(q)) 30 | print('RSA n value =', repr(n)) 31 | print() 32 | print('Initialize OnlyKey client...') 33 | ok = OnlyKey() 34 | print('Done') 35 | print() 36 | 37 | time.sleep(2) 38 | 39 | ok.read_string(timeout_ms=100) 40 | empty = 'a' 41 | while not empty: 42 | empty = ok.read_string(timeout_ms=100) 43 | 44 | print('You should see your OnlyKey blink 3 times') 45 | print() 46 | 47 | print('Setting RSA private...') 48 | 49 | def pack_long(n): 50 | """this conert 10045587143827198209824131064458461027107542643158086193488942239589004873324146472911535357118684101051965945865943581473431374244810144984918148150975257L 51 | to "\xbf\xcd\xce\xa0K\x93\x85}\xf0\x18\xb3\xd3L}\x14\xdb\xce0\x00uE,\x05'\xeeW\x1c\xeb\xcf\x8b\x1f\xcc\xc5\xc1\xe2\x17\xb7\xa3\xb6C\x16\xea?\xcchz\xebF1\xb7\xb1\x86\xb8\n}\x82\xebx\xce\x1b\x13\xdf\xdb\x19" 52 | it seems to be want you wanted? it's 64 bytes. 53 | """ 54 | h = '%x' % n 55 | s = bytes.fromhex('0'*(len(h) % 2) + h) 56 | return s 57 | 58 | def bin2hex(binStr): 59 | return binascii.hexlify(binStr) 60 | 61 | def hex2bin(hexStr): 62 | return binascii.unhexlify(hexStr) 63 | 64 | hexPrivKey = binPrivKey.hex() 65 | hexPubKey = binPubKey.hex() 66 | 67 | # p and q are long ints that are no more than 1/2 the size of pubkey 68 | # I need to convert these into a single byte array put p in the first 69 | # half byte[0] of the byte array and q in the second half byte[(type*128) / 2] 70 | # send the byte array to OnlyKey splitting into 56 bytes per packet 71 | q_and_p = q.to_bytes(64, "little") + p.to_bytes(64, "little") 72 | public_n = n.to_bytes(128, "little") 73 | # 74 | ok.send_large_message3(msg=Message.OKSETPRIV, slot_id=1, key_type=(1+32), payload=q_and_p) 75 | 76 | # ok.set_rsa_key(1, (1+64), byte array here) #Can only send 56 bytes per packet 77 | # Slot 1 - 4 for RSA 78 | # Type 1 = 1024, Type 2 = 2048, Type 3 = 3072, Type 4 = 4096 79 | # Key Features - 80 | # if backup key = type + 128 81 | # if signature key = type + 64 82 | # if decryption key = type + 32 83 | # if authentication key = type + 16 84 | # For this example it will be a decryption key 85 | time.sleep(1.5) 86 | print(ok.read_string()) 87 | 88 | time.sleep(2) 89 | print('You should see your OnlyKey blink 3 times') 90 | print() 91 | 92 | print('Trying to read the public RSA N part 1...') 93 | ok.send_message(msg=Message.OKGETPUBKEY, payload=bytes([1,1])) #, payload=[1, 1]) 94 | time.sleep(1.5) 95 | for _ in range(10): 96 | ok_pubkey1 = ok.read_bytes(64, timeout_ms=1000) 97 | if len(ok_pubkey1) == 64: 98 | break 99 | time.sleep(1) 100 | 101 | print() 102 | 103 | print('received=', repr(ok_pubkey1)) 104 | 105 | print('Trying to read the public RSA N part 2...') 106 | for _ in range(10): 107 | ok_pubkey2 = ok.read_bytes(64, timeout_ms=1000) 108 | if len(ok_pubkey2) == 64: 109 | break 110 | time.sleep(1) 111 | 112 | print() 113 | 114 | print('received=', repr(ok_pubkey2)) 115 | 116 | if not ok_pubkey2: 117 | raise Exception('failed to set the RSA key') 118 | 119 | print('Assert that the received public N match the one generated locally') 120 | print('Local Public N=', repr(public_n)) 121 | ok_pubkey = ok_pubkey1 + ok_pubkey2 122 | assert ok_pubkey == public_n 123 | print('Ok, public N matches') 124 | print() 125 | 126 | message = 'Secret message' 127 | #h = SHA.new(message) 128 | cipher = PKCS1_v1_5.new(key) 129 | ciphertext = cipher.encrypt(message) 130 | 131 | #hex_enc_data = bin2hex(enc_data) 132 | print('encrypted payload = ', repr(ciphertext)) 133 | print() 134 | 135 | 136 | # Compute the challenge pin 137 | h = hashlib.sha256() 138 | h.update(ciphertext) 139 | d = h.digest() 140 | 141 | assert len(d) == 32 142 | 143 | def get_button(byte): 144 | ibyte = ord(byte) 145 | if ibyte < 6: 146 | return 1 147 | return ibyte % 5 + 1 148 | 149 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 150 | 151 | print('Sending the payload to the OnlyKey...') 152 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=ciphertext, slot_id=1) 153 | 154 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 155 | print('{} {} {}'.format(b1, b2, b3)) 156 | input() 157 | print('Trying to read the decrypted data from OnlyKey...') 158 | ok_decrypted = '' 159 | while ok_decrypted == '': 160 | time.sleep(0.5) 161 | ok_decrypted = ok.read_bytes(len(message), to_bytes=True) 162 | 163 | dsize = len(message) 164 | sentinel = Random.new().read(15+dsize) 165 | plaintext = cipher.decrypt(ciphertext, sentinel) 166 | 167 | print('Decrypted by OnlyKey, data=', repr(ok_decrypted)) 168 | 169 | print('Local decrypted data =', repr(plaintext)) 170 | print('Assert that the decrypted data generated locally matches the data generated on the OnlyKey') 171 | assert repr(ok_decrypted) == repr(plaintext) 172 | print('Ok, data matches') 173 | print() 174 | 175 | print('Done') 176 | -------------------------------------------------------------------------------- /tests/ecdh_secp256k1.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from binascii import hexlify 7 | import pyelliptic 8 | 9 | from onlykey import OnlyKey, Message 10 | 11 | print('Generating a new secp256k1 key pair...') 12 | 13 | # Asymmetric encryption 14 | alice = pyelliptic.ECC(curve='secp256k1') 15 | bob = pyelliptic.ECC(curve='secp256k1') 16 | 17 | bob_private_key = bob.get_privkey() 18 | alice_public_key = alice.get_pubkey() 19 | bob_public_key = bob.get_pubkey() 20 | alice_private_key = alice.get_privkey() 21 | 22 | print("Bob's private key: ", hexlify(bob_private_key)) 23 | print("Bob's public key: ", hexlify(bob_public_key)) 24 | 25 | print() 26 | print("Alices's private key: ", hexlify(alice_private_key)) 27 | print("Alices's public key: ", hexlify(alice_public_key)) 28 | print() 29 | 30 | print() 31 | print('Initialize OnlyKey client...') 32 | ok = OnlyKey() 33 | print('Done') 34 | print() 35 | 36 | time.sleep(2) 37 | 38 | ok.read_string(timeout_ms=100) 39 | empty = 'a' 40 | while not empty: 41 | empty = ok.read_string(timeout_ms=100) 42 | 43 | print('You should see your OnlyKey blink 3 times') 44 | print() 45 | 46 | print('Setting ECC private...') 47 | ok.set_ecc_key(101, (3+32), bob_private_key) 48 | # Slot 101 - 132 for ECC 49 | # Type 1 = Ed25519, Type 2 = p256r1, Type 3 = p256k1 50 | # Key Features - 51 | # if backup key = type + 128 52 | # if signature key = type + 64 53 | # if decryption key = type + 32 54 | # if authentication key = type + 16 55 | # For this example it will be a decryption key 56 | time.sleep(1.5) 57 | print(ok.read_string()) 58 | 59 | time.sleep(2) 60 | print('You should see your OnlyKey blink 3 times') 61 | print() 62 | 63 | 64 | payload = alice_public_key 65 | 66 | #We are simulating message here, according to refernce below it is a known value to both parties - unsigned char message[256]; 67 | 68 | # 69 | # // Reference - https://www.ietf.org/mail-archive/web/openpgp/current/msg00637.html 70 | # // https://fossies.org/linux/misc/gnupg-2.1.17.tar.gz/gnupg-2.1.17/g10/ecdh.c 71 | # // gcry_md_write(h, "\x00\x00\x00\x01", 4); /* counter = 1 */ 72 | # // gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */ 73 | # // gcry_md_write(h, message, message_size); /* KDF parameters */ 74 | # // This is a limitation as we have to be able to fit the entire message to decrypt 75 | # // In this way RSA seems to have an advantage? 76 | # // /* Build kdf_params. */ 77 | # //{ 78 | # //IOBUF obuf; 79 | # // 80 | # //obuf = iobuf_temp(); 81 | # ///* variable-length field 1, curve name OID */ 82 | # //err = gpg_mpi_write_nohdr (obuf, pkey[0]); 83 | # ///* fixed-length field 2 */ 84 | # //iobuf_put (obuf, PUBKEY_ALGO_ECDH); 85 | # ///* variable-length field 3, KDF params */ 86 | # //err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2])); 87 | # ///* fixed-length field 4 */ 88 | # //iobuf_write (obuf, "Anonymous Sender ", 20); 89 | # ///* fixed-length field 5, recipient fp */ 90 | # //iobuf_write (obuf, pk_fp, 20); 91 | # 92 | # 93 | # 94 | 95 | 96 | print('Payload containing ephemeral public key', repr(payload)) 97 | print() 98 | 99 | # Compute the challenge pin 100 | h = hashlib.sha256() 101 | h.update(payload) 102 | d = h.digest() 103 | 104 | assert len(d) == 32 105 | 106 | def get_button(byte): 107 | ibyte = ord(byte) 108 | if ibyte < 6: 109 | return 1 110 | return ibyte % 5 + 1 111 | 112 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 113 | 114 | print('Sending the payload to the OnlyKey...') 115 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=payload, slot_id=101) 116 | 117 | # Tim - The OnlyKey can send the code to enter but it would be better if the app generates 118 | # the code, this way in order to trick a user into approving an unauthorized signature 119 | # the app, in this case this python code would have to be hacked on the user's system. 120 | # How the OnlyKey creates the three digit code: 121 | # SHA256_CTX CRYPTO; 122 | # sha256_init(&CRYPTO); 123 | # sha256_update(&CRYPTO, large_buffer, large_data_offset); //step 1 create a sha256 hash of 124 | # sha256_final(&CRYPTO, rsa_signature); //the data to sign 125 | # if (rsa_signature[0] < 6) Challenge_button1 = '1'; //step 2 Convert first byte of hash to 126 | # else { //first button to press (remainder of byte is a base 6 number 0 - 5) 127 | # Challenge_button1 = rsa_signature[0] % 5; 128 | # Challenge_button1 = Challenge_button1 + '0' + 1; //Add '0' and 1 so number will be ASCII 1 - 6 129 | # } 130 | # if (rsa_signature[15] < 6) Challenge_button2 = '1'; //step 3 do the same with 16th byte to 131 | # else { // get Challenge_button2 132 | # Challenge_button2 = rsa_signature[15] % 5; 133 | # Challenge_button2 = Challenge_button2 + '0' + 1; 134 | #} 135 | # if (rsa_signature[31] < 6) Challenge_button3 = '1'; //step 4 do the same with 32nd byte to 136 | # else { // get Challenge_button 137 | # Challenge_button3 = rsa_signature[31] % 5; 138 | # Challenge_button3 = Challenge_button3 + '0' + 1; 139 | # } 140 | # step 5 display the code to user to enter on OnlyKey 141 | 142 | # This method prevents some malware on a users system from sending fake requests to be signed 143 | # at the same time as real requests and tricking the user into signing the wrong data 144 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 145 | print('{} {} {}'.format(b1, b2, b3)) 146 | input() 147 | shared_secret1 = alice.get_ecdh_key(bob.get_pubkey()) 148 | shared_secret2 = bob.get_ecdh_key(alice.get_pubkey()) 149 | print('Trying to read the shared secret from OnlyKey...') 150 | ok_shared_secret = '' 151 | while ok_shared_secret == '': 152 | time.sleep(0.5) 153 | ok_shared_secret = ok.read_bytes(len(shared_secret1), to_bytes=True) 154 | 155 | print('OnlyKey Shared Secret =', hexlify(ok_shared_secret)) 156 | 157 | print('Local Shared Secret1 =', hexlify(shared_secret1)) 158 | print() 159 | 160 | print('Local Shared Secret2 =', hexlify(shared_secret2)) 161 | print() 162 | 163 | print('Assert that both shared secrets match') 164 | assert repr(shared_secret1) == repr(ok_shared_secret) 165 | print('Ok, secrets match') 166 | print() 167 | 168 | 169 | print('Done') 170 | -------------------------------------------------------------------------------- /tests/ecdh_p256.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from binascii import hexlify 7 | import pyelliptic 8 | 9 | from onlykey import OnlyKey, Message 10 | 11 | print('Generating a new NIST P-256 key pair...') 12 | 13 | # Asymmetric encryption 14 | alice = pyelliptic.ECC(curve='prime256v1') 15 | bob = pyelliptic.ECC(curve='prime256v1') 16 | 17 | bob_private_key = bob.get_privkey() 18 | alice_public_key = alice.get_pubkey() 19 | bob_public_key = bob.get_pubkey() 20 | alice_private_key = alice.get_privkey() 21 | 22 | print("Bob's private key: ", hexlify(bob_private_key)) 23 | print("Bob's public key: ", hexlify(bob_public_key)) 24 | 25 | print() 26 | print("Alices's private key: ", hexlify(alice_private_key)) 27 | print("Alices's public key: ", hexlify(alice_public_key)) 28 | print() 29 | 30 | print() 31 | print('Initialize OnlyKey client...') 32 | ok = OnlyKey() 33 | print('Done') 34 | print() 35 | 36 | time.sleep(2) 37 | 38 | ok.read_string(timeout_ms=100) 39 | empty = 'a' 40 | while not empty: 41 | empty = ok.read_string(timeout_ms=100) 42 | 43 | print('You should see your OnlyKey blink 3 times') 44 | print() 45 | 46 | print('Setting ECC private...') 47 | ok.set_ecc_key(101, (2+32), bob_private_key) 48 | # Slot 101 - 132 for ECC 49 | # Type 1 = Ed25519, Type 2 = p256r1, Type 3 = p256k1 50 | # Key Features - 51 | # if backup key = type + 128 52 | # if signature key = type + 64 53 | # if decryption key = type + 32 54 | # if authentication key = type + 16 55 | # For this example it will be a decryption key 56 | time.sleep(1.5) 57 | print(ok.read_string()) 58 | 59 | time.sleep(2) 60 | print('You should see your OnlyKey blink 3 times') 61 | print() 62 | 63 | 64 | payload = alice_public_key 65 | 66 | #We are simulating message here, according to refernce below it is a known value to both parties - unsigned char message[256]; 67 | 68 | # 69 | # // Reference - https://www.ietf.org/mail-archive/web/openpgp/current/msg00637.html 70 | # // https://fossies.org/linux/misc/gnupg-2.1.17.tar.gz/gnupg-2.1.17/g10/ecdh.c 71 | # // gcry_md_write(h, "\x00\x00\x00\x01", 4); /* counter = 1 */ 72 | # // gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */ 73 | # // gcry_md_write(h, message, message_size); /* KDF parameters */ 74 | # // This is a limitation as we have to be able to fit the entire message to decrypt 75 | # // In this way RSA seems to have an advantage? 76 | # // /* Build kdf_params. */ 77 | # //{ 78 | # //IOBUF obuf; 79 | # // 80 | # //obuf = iobuf_temp(); 81 | # ///* variable-length field 1, curve name OID */ 82 | # //err = gpg_mpi_write_nohdr (obuf, pkey[0]); 83 | # ///* fixed-length field 2 */ 84 | # //iobuf_put (obuf, PUBKEY_ALGO_ECDH); 85 | # ///* variable-length field 3, KDF params */ 86 | # //err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2])); 87 | # ///* fixed-length field 4 */ 88 | # //iobuf_write (obuf, "Anonymous Sender ", 20); 89 | # ///* fixed-length field 5, recipient fp */ 90 | # //iobuf_write (obuf, pk_fp, 20); 91 | # 92 | # 93 | # 94 | 95 | 96 | print('Payload containing ephemeral public key', repr(payload)) 97 | print() 98 | 99 | # Compute the challenge pin 100 | h = hashlib.sha256() 101 | h.update(payload) 102 | d = h.digest() 103 | 104 | assert len(d) == 32 105 | 106 | def get_button(byte): 107 | ibyte = ord(byte) 108 | if ibyte < 6: 109 | return 1 110 | return ibyte % 5 + 1 111 | 112 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 113 | 114 | print('Sending the payload to the OnlyKey...') 115 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=payload, slot_id=101) 116 | 117 | # Tim - The OnlyKey can send the code to enter but it would be better if the app generates 118 | # the code, this way in order to trick a user into approving an unauthorized signature 119 | # the app, in this case this python code would have to be hacked on the user's system. 120 | # How the OnlyKey creates the three digit code: 121 | # SHA256_CTX CRYPTO; 122 | # sha256_init(&CRYPTO); 123 | # sha256_update(&CRYPTO, large_buffer, large_data_offset); //step 1 create a sha256 hash of 124 | # sha256_final(&CRYPTO, rsa_signature); //the data to sign 125 | # if (rsa_signature[0] < 6) Challenge_button1 = '1'; //step 2 Convert first byte of hash to 126 | # else { //first button to press (remainder of byte is a base 6 number 0 - 5) 127 | # Challenge_button1 = rsa_signature[0] % 5; 128 | # Challenge_button1 = Challenge_button1 + '0' + 1; //Add '0' and 1 so number will be ASCII 1 - 6 129 | # } 130 | # if (rsa_signature[15] < 6) Challenge_button2 = '1'; //step 3 do the same with 16th byte to 131 | # else { // get Challenge_button2 132 | # Challenge_button2 = rsa_signature[15] % 5; 133 | # Challenge_button2 = Challenge_button2 + '0' + 1; 134 | #} 135 | # if (rsa_signature[31] < 6) Challenge_button3 = '1'; //step 4 do the same with 32nd byte to 136 | # else { // get Challenge_button 137 | # Challenge_button3 = rsa_signature[31] % 5; 138 | # Challenge_button3 = Challenge_button3 + '0' + 1; 139 | # } 140 | # step 5 display the code to user to enter on OnlyKey 141 | 142 | # This method prevents some malware on a users system from sending fake requests to be signed 143 | # at the same time as real requests and tricking the user into signing the wrong data 144 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 145 | print('{} {} {}'.format(b1, b2, b3)) 146 | raw_input() 147 | shared_secret1 = alice.get_ecdh_key(bob.get_pubkey()) 148 | shared_secret2 = bob.get_ecdh_key(alice.get_pubkey()) 149 | print('Trying to read the shared secret from OnlyKey...') 150 | ok_shared_secret = '' 151 | while ok_shared_secret == '': 152 | time.sleep(0.5) 153 | ok_shared_secret = ok.read_bytes(len(shared_secret1), to_bytes=True) 154 | 155 | print('OnlyKey Shared Secret =', hexlify(ok_shared_secret)) 156 | 157 | print('Local Shared Secret1 =', hexlify(shared_secret1)) 158 | print() 159 | 160 | print('Local Shared Secret2 =', hexlify(shared_secret2)) 161 | print() 162 | 163 | print('Assert that both shared secrets match') 164 | print((hexlify(alice.get_ecdh_key(bob.get_pubkey())))) 165 | assert repr(shared_secret1) == repr(ok_shared_secret) 166 | print('Ok, secrets match') 167 | print() 168 | 169 | 170 | print('Done') 171 | -------------------------------------------------------------------------------- /tests/rsa_decrypt_2048.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Cipher import PKCS1_v1_5 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Hash import SHA 9 | from Crypto.Random import get_random_bytes 10 | from Crypto import Random 11 | import binascii 12 | 13 | from onlykey import OnlyKey, Message 14 | 15 | print('Generating a new rsa key pair...') 16 | random_generator = Random.new().read 17 | key = RSA.generate(2048, random_generator) #generate pub and priv key 18 | 19 | p = key.p 20 | q = key.q 21 | n = key.n 22 | 23 | binPrivKey = key.exportKey('DER') 24 | binPubKey = key.publickey().exportKey('DER') 25 | 26 | print('Done') 27 | print() 28 | print('RSA p value =', repr(p)) 29 | print('RSA q value =', repr(q)) 30 | print('RSA n value =', repr(n)) 31 | print() 32 | print('Initialize OnlyKey client...') 33 | ok = OnlyKey() 34 | print('Done') 35 | print() 36 | 37 | time.sleep(2) 38 | 39 | ok.read_string(timeout_ms=100) 40 | empty = 'a' 41 | while not empty: 42 | empty = ok.read_string(timeout_ms=100) 43 | 44 | print('You should see your OnlyKey blink 3 times') 45 | print() 46 | 47 | print('Setting RSA private...') 48 | 49 | def pack_long(n): 50 | """this conert 10045587143827198209824131064458461027107542643158086193488942239589004873324146472911535357118684101051965945865943581473431374244810144984918148150975257L 51 | to "\xbf\xcd\xce\xa0K\x93\x85}\xf0\x18\xb3\xd3L}\x14\xdb\xce0\x00uE,\x05'\xeeW\x1c\xeb\xcf\x8b\x1f\xcc\xc5\xc1\xe2\x17\xb7\xa3\xb6C\x16\xea?\xcchz\xebF1\xb7\xb1\x86\xb8\n}\x82\xebx\xce\x1b\x13\xdf\xdb\x19" 52 | it seems to be want you wanted? it's 64 bytes. 53 | """ 54 | h = '%x' % n 55 | s = ('0'*(len(h) % 2) + h).decode('hex') 56 | return s 57 | 58 | def bin2hex(binStr): 59 | return binascii.hexlify(binStr) 60 | 61 | def hex2bin(hexStr): 62 | return binascii.unhexlify(hexStr) 63 | 64 | hexPrivKey = bin2hex(binPrivKey) 65 | hexPubKey = bin2hex(binPubKey) 66 | 67 | # p and q are long ints that are no more than 1/2 the size of pubkey 68 | # I need to convert these into a single byte array put p in the first 69 | # half byte[0] of the byte array and q in the second half byte[(type*128) / 2] 70 | # send the byte array to OnlyKey splitting into 56 bytes per packet 71 | q_and_p = pack_long(q) + pack_long(p) 72 | public_n = pack_long(n) 73 | # 74 | ok.send_large_message3(msg=Message.OKSETPRIV, slot_id=1, key_type=(2+32), payload=q_and_p) 75 | 76 | # ok.set_rsa_key(1, (1+64), byte array here) #Can only send 56 bytes per packet 77 | # Slot 1 - 4 for RSA 78 | # Type 1 = 1024, Type 2 = 2048, Type 3 = 3072, Type 4 = 4096 79 | # Key Features - 80 | # if backup key = type + 128 81 | # if signature key = type + 64 82 | # if decryption key = type + 32 83 | # if authentication key = type + 16 84 | # For this example it will be a decryption key 85 | time.sleep(1.5) 86 | print(ok.read_string()) 87 | 88 | time.sleep(2) 89 | print('You should see your OnlyKey blink 3 times') 90 | print() 91 | 92 | print('Trying to read the public RSA N part 1...') 93 | ok.send_message(msg=Message.OKGETPUBKEY, payload=chr(1)) #, payload=[1, 1]) 94 | time.sleep(1.5) 95 | for _ in xrange(10): 96 | ok_pubkey1 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 97 | if len(ok_pubkey1) == 64: 98 | break 99 | time.sleep(1) 100 | 101 | print() 102 | 103 | print('received=', repr(ok_pubkey1)) 104 | 105 | print('Trying to read the public RSA N part 2...') 106 | for _ in xrange(10): 107 | ok_pubkey2 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 108 | if len(ok_pubkey2) == 64: 109 | break 110 | time.sleep(1) 111 | 112 | print() 113 | 114 | print('received=', repr(ok_pubkey2)) 115 | 116 | print('Trying to read the public RSA N part 3...') 117 | for _ in xrange(10): 118 | ok_pubkey3 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 119 | if len(ok_pubkey3) == 64: 120 | break 121 | time.sleep(1) 122 | 123 | print() 124 | 125 | print('received=', repr(ok_pubkey3)) 126 | 127 | print('Trying to read the public RSA N part 4...') 128 | for _ in xrange(10): 129 | ok_pubkey4 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 130 | if len(ok_pubkey4) == 64: 131 | break 132 | time.sleep(1) 133 | 134 | print() 135 | 136 | print('received=', repr(ok_pubkey4)) 137 | 138 | if not ok_pubkey4: 139 | raise Exception('failed to set the RSA key') 140 | 141 | print('Assert that the received public N match the one generated locally') 142 | print('Local Public N=', repr(public_n)) 143 | ok_pubkey = ok_pubkey1 + ok_pubkey2 + ok_pubkey3 + ok_pubkey4 144 | assert ok_pubkey == public_n 145 | print('Ok, public N matches') 146 | print() 147 | 148 | message = 'Secret message' 149 | #h = SHA.new(message) 150 | cipher = PKCS1_v1_5.new(key) 151 | ciphertext = cipher.encrypt(message) 152 | 153 | #hex_enc_data = bin2hex(enc_data) 154 | print('encrypted payload = ', repr(ciphertext)) 155 | print() 156 | 157 | 158 | # Compute the challenge pin 159 | h = hashlib.sha256() 160 | h.update(ciphertext) 161 | d = h.digest() 162 | 163 | assert len(d) == 32 164 | 165 | def get_button(byte): 166 | ibyte = ord(byte) 167 | if ibyte < 6: 168 | return 1 169 | return ibyte % 5 + 1 170 | 171 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 172 | 173 | print('Sending the payload to the OnlyKey...') 174 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=ciphertext, slot_id=1) 175 | 176 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 177 | print('{} {} {}'.format(b1, b2, b3)) 178 | input() 179 | print('Trying to read the decrypted data from OnlyKey...') 180 | ok_decrypted = '' 181 | while ok_decrypted == '': 182 | time.sleep(0.5) 183 | ok_decrypted = ok.read_bytes(len(message), to_bytes=True) 184 | 185 | dsize = len(message) 186 | sentinel = Random.new().read(15+dsize) 187 | plaintext = cipher.decrypt(ciphertext, sentinel) 188 | 189 | print('Decrypted by OnlyKey, data=', repr(ok_decrypted)) 190 | 191 | print('Local decrypted data =', repr(plaintext)) 192 | print('Assert that the decrypted data generated locally matches the data generated on the OnlyKey') 193 | assert repr(ok_decrypted) == repr(plaintext) 194 | print('Ok, data matches') 195 | print() 196 | 197 | print('Done') 198 | -------------------------------------------------------------------------------- /tests/ssh_auth_rsa_1024.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Signature import PKCS1_v1_5 7 | from Crypto.Hash import SHA 8 | from Crypto.PublicKey import RSA 9 | from Crypto import Random 10 | import binascii 11 | 12 | from onlykey import OnlyKey, Message 13 | 14 | print('Generating a new rsa key pair...') 15 | random_generator = Random.new().read 16 | key = RSA.generate(1024, random_generator) #generate pub and priv key 17 | 18 | p = key.p 19 | q = key.q 20 | n = key.n 21 | 22 | binPrivKey = key.exportKey('DER') 23 | binPubKey = key.publickey().exportKey('DER') 24 | #privKeyObj = RSA.importKey(binPrivKey) 25 | #pubKeyObj = RSA.importKey(binPubKey) 26 | 27 | def bin2hex(binStr): 28 | return binascii.hexlify(binStr) 29 | 30 | def hex2bin(hexStr): 31 | return binascii.unhexlify(hexStr) 32 | 33 | hexPrivKey = bin2hex(binPrivKey) 34 | hexPubKey = bin2hex(binPubKey) 35 | 36 | def pack_long(n): 37 | """this conert 10045587143827198209824131064458461027107542643158086193488942239589004873324146472911535357118684101051965945865943581473431374244810144984918148150975257L 38 | to "\xbf\xcd\xce\xa0K\x93\x85}\xf0\x18\xb3\xd3L}\x14\xdb\xce0\x00uE,\x05'\xeeW\x1c\xeb\xcf\x8b\x1f\xcc\xc5\xc1\xe2\x17\xb7\xa3\xb6C\x16\xea?\xcchz\xebF1\xb7\xb1\x86\xb8\n}\x82\xebx\xce\x1b\x13\xdf\xdb\x19" 39 | it seems to be want you wanted? it's 64 bytes. 40 | """ 41 | h = '%x' % n 42 | s = ('0'*(len(h) % 2) + h).decode('hex') 43 | return s 44 | 45 | print('Done') 46 | print() 47 | print('RSA p value =', repr(pack_long(p))) 48 | print('RSA q value =', repr(pack_long(q))) 49 | print('RSA n value =', repr(pack_long(n))) 50 | print() 51 | print('Initialize OnlyKey client...') 52 | ok = OnlyKey() 53 | print('Done') 54 | print() 55 | 56 | time.sleep(2) 57 | 58 | ok.read_string(timeout_ms=100) 59 | empty = 'a' 60 | while not empty: 61 | empty = ok.read_string(timeout_ms=100) 62 | 63 | print('You should see your OnlyKey blink 3 times') 64 | print() 65 | 66 | print('Setting SSH private...') 67 | 68 | 69 | # p and q are long ints that are no more than 1/2 the size of pubkey 70 | # I need to convert these into a single byte array put p in the first 71 | # half byte[0] of the byte array and q in the second half byte[(type*128) / 2] 72 | # send the byte array to OnlyKey splitting into 56 bytes per packet 73 | q_and_p = pack_long(p) + pack_long(q) 74 | public_n = pack_long(n) 75 | # 76 | ok.send_large_message3(msg=Message.OKSETPRIV, slot_id=1, key_type=(1+64), payload=q_and_p) 77 | 78 | # ok.set_rsa_key(1, (1+64), byte array here) #Can only send 56 bytes per packet 79 | # Slot 1 - 4 for RSA 80 | # Type 1 = 1024, Type 2 = 2048, Type 3 = 3072, Type 4 = 4096 81 | # Key Features - 82 | # if backup key = type + 128 83 | # if signature key = type + 64 84 | # if decryption key = type + 32 85 | # if authentication key = type + 16 86 | # For this example it will be a decryption key 87 | time.sleep(1.5) 88 | print(ok.read_string()) 89 | 90 | time.sleep(2) 91 | print('You should see your OnlyKey blink 3 times') 92 | print() 93 | 94 | print('Trying to read the public RSA N part 1...') 95 | ok.send_message(msg=Message.OKGETPUBKEY, payload=chr(1)) #, payload=[1, 1]) 96 | time.sleep(1.5) 97 | for _ in xrange(10): 98 | ok_pubkey1 = ok.read_bytes(64, to_bytes=True) 99 | if len(ok_pubkey1) == 64: 100 | break 101 | time.sleep(1) 102 | 103 | print() 104 | 105 | print('received=', repr(ok_pubkey1)) 106 | 107 | print('Trying to read the public RSA N part 2...') 108 | for _ in xrange(10): 109 | ok_pubkey2 = ok.read_bytes(64, to_bytes=True) 110 | if len(ok_pubkey2) == 64: 111 | break 112 | time.sleep(1) 113 | 114 | print() 115 | 116 | print('received=', repr(ok_pubkey2)) 117 | 118 | if not ok_pubkey2: 119 | raise Exception('failed to set the SSH key') 120 | 121 | print('Assert that the received public N match the one generated locally') 122 | print('Local Public N=', repr(public_n)) 123 | ok_pubkey = ok_pubkey1 + ok_pubkey2 124 | assert ok_pubkey == public_n 125 | print('Ok, public N matches') 126 | print() 127 | 128 | test_payload1 = 'message to sign' 129 | h = hashlib.sha1() 130 | h.update(test_payload1) 131 | test_payload2 = h.digest() 132 | print('test_payload=', repr(test_payload2)) 133 | print() 134 | 135 | # Compute the challenge pin 136 | h = hashlib.sha256() 137 | h.update(test_payload2) 138 | d = h.digest() 139 | 140 | assert len(d) == 32 141 | 142 | def get_button(byte): 143 | ibyte = ord(byte) 144 | if ibyte < 6: 145 | return 1 146 | return ibyte % 5 + 1 147 | 148 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 149 | 150 | print('Sending the payload to the OnlyKey...') 151 | ok.send_large_message2(msg=Message.OKSIGNCHALLENGE, payload=test_payload2, slot_id=1) 152 | 153 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 154 | print('{} {} {}'.format(b1, b2, b3)) 155 | input() 156 | print('Trying to read the signature part 1...') 157 | signature1 = '' 158 | while signature1 == '': 159 | time.sleep(0.5) 160 | signature1 = ok.read_bytes(64, to_bytes=True) 161 | 162 | print('received=', repr(signature1)) 163 | 164 | print('Trying to read the signature part 2...') 165 | signature2 = '' 166 | while signature2 == '': 167 | time.sleep(0.5) 168 | signature2 = ok.read_bytes(64, to_bytes=True) 169 | 170 | print('received=', repr(signature2)) 171 | 172 | ok_signature = signature1 + signature2 173 | print('Signed by OnlyKey, signature=', repr(ok_signature)) 174 | def bytes2int(str): 175 | return int(str.encode('hex'), 16) 176 | 177 | # I don't think this is right, need to convert signature to right format 178 | # https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA._RSAobj-class.html#verify 179 | # https://www.dlitz.net/software/pycrypto/api/current/Crypto.Signature.PKCS1_v1_5-module.html 180 | print('Length=', len(ok_signature)) 181 | 182 | h = SHA.new(test_payload1) 183 | signer = PKCS1_v1_5.new(key) 184 | signature = signer.sign(h) 185 | print('Signed locally, signature=', repr(signature)) 186 | print('Length=', len(signature)) 187 | print('local messege to sign=', repr(h.hexdigest())) 188 | verifier = PKCS1_v1_5.new(key) 189 | if verifier.verify(h, signature): 190 | print("The local signature is authentic.") 191 | else: 192 | print("The local signature is not authentic.") 193 | 194 | print('OnlyKey messege to sign=', repr(test_payload2)) 195 | verifier = PKCS1_v1_5.new(key) 196 | if verifier.verify(h, ok_signature): 197 | print("The OnlyKey signature is authentic.") 198 | else: 199 | print("The OnlyKey signature is not authentic.") 200 | 201 | 202 | 203 | 204 | 205 | print('Done') 206 | -------------------------------------------------------------------------------- /tests/ecdh_curve25519.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Cipher import AES 7 | import axolotl_curve25519 as curve 8 | #import pyelliptic 9 | 10 | from onlykey import OnlyKey, Message 11 | 12 | print('Generating a new curve25519 key pair...') 13 | randm32 = os.urandom(32) 14 | randm64 = os.urandom(64) 15 | 16 | bob_private_key = curve.generatePrivateKey(randm32) 17 | bob_public_key = curve.generatePublicKey(bob_private_key) 18 | 19 | randm32 = os.urandom(32) 20 | randm64 = os.urandom(64) 21 | 22 | alice_private_key = curve.generatePrivateKey(randm32) 23 | alice_public_key = curve.generatePublicKey(alice_private_key) 24 | print() 25 | 26 | print('bob privkey=', repr(bob_private_key)) 27 | print('bob pubkey=', repr(bob_public_key)) 28 | print('alice privkey=', repr(alice_private_key)) 29 | print('alice pubkey=', repr(alice_public_key)) 30 | print() 31 | 32 | print() 33 | print('Initialize OnlyKey client...') 34 | ok = OnlyKey() 35 | print('Done') 36 | print() 37 | 38 | time.sleep(2) 39 | 40 | ok.read_string(timeout_ms=100) 41 | empty = 'a' 42 | while not empty: 43 | empty = ok.read_string(timeout_ms=100) 44 | 45 | print('You should see your OnlyKey blink 3 times') 46 | print() 47 | 48 | print('Setting ECC private...') 49 | ok.set_ecc_key(101, (1+32), bob_private_key) 50 | # Slot 101 - 132 for ECC 51 | # Type 1 = Ed25519, Type 2 = p256r1, Type 3 = p256k1 52 | # Key Features - 53 | # if backup key = type + 128 54 | # if signature key = type + 64 55 | # if decryption key = type + 32 56 | # if authentication key = type + 16 57 | # For this example it will be a decryption key 58 | time.sleep(1.5) 59 | print(ok.read_string()) 60 | 61 | time.sleep(2) 62 | print('You should see your OnlyKey blink 3 times') 63 | print() 64 | 65 | 66 | message = 'Secret Message' 67 | counter = b"\x00\x00\x00\x01" 68 | shared_secret = curve.calculateAgreement(alice_private_key, bob_public_key) 69 | h = hashlib.sha256() 70 | h.update(counter) 71 | h.update(shared_secret) 72 | h.update(message.encode()) 73 | d = h.digest() 74 | KEK = d 75 | 76 | payload = alice_public_key 77 | 78 | #We are simulating message here, according to refernce below it is a known value to both parties - unsigned char message[256]; 79 | 80 | # 81 | # // Reference - https://www.ietf.org/mail-archive/web/openpgp/current/msg00637.html 82 | # // https://fossies.org/linux/misc/gnupg-2.1.17.tar.gz/gnupg-2.1.17/g10/ecdh.c 83 | # // gcry_md_write(h, "\x00\x00\x00\x01", 4); /* counter = 1 */ 84 | # // gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */ 85 | # // gcry_md_write(h, message, message_size); /* KDF parameters */ 86 | # // This is a limitation as we have to be able to fit the entire message to decrypt 87 | # // In this way RSA seems to have an advantage? 88 | # // /* Build kdf_params. */ 89 | # //{ 90 | # //IOBUF obuf; 91 | # // 92 | # //obuf = iobuf_temp(); 93 | # ///* variable-length field 1, curve name OID */ 94 | # //err = gpg_mpi_write_nohdr (obuf, pkey[0]); 95 | # ///* fixed-length field 2 */ 96 | # //iobuf_put (obuf, PUBKEY_ALGO_ECDH); 97 | # ///* variable-length field 3, KDF params */ 98 | # //err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2])); 99 | # ///* fixed-length field 4 */ 100 | # //iobuf_write (obuf, "Anonymous Sender ", 20); 101 | # ///* fixed-length field 5, recipient fp */ 102 | # //iobuf_write (obuf, pk_fp, 20); 103 | # 104 | # 105 | # 106 | 107 | 108 | print('Payload containing ephemeral public key', repr(payload)) 109 | print() 110 | 111 | # Compute the challenge pin 112 | h = hashlib.sha256() 113 | h.update(payload) 114 | d = h.digest() 115 | 116 | assert len(d) == 32 117 | 118 | def get_button(ibyte): 119 | if ibyte < 6: 120 | return 1 121 | return ibyte % 5 + 1 122 | 123 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 124 | 125 | print('Sending the payload to the OnlyKey...') 126 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=payload, slot_id=101) 127 | 128 | # Tim - The OnlyKey can send the code to enter but it would be better if the app generates 129 | # the code, this way in order to trick a user into approving an unauthorized signature 130 | # the app, in this case this python code would have to be hacked on the user's system. 131 | # How the OnlyKey creates the three digit code: 132 | # SHA256_CTX CRYPTO; 133 | # sha256_init(&CRYPTO); 134 | # sha256_update(&CRYPTO, large_buffer, large_data_offset); //step 1 create a sha256 hash of 135 | # sha256_final(&CRYPTO, rsa_signature); //the data to sign 136 | # if (rsa_signature[0] < 6) Challenge_button1 = '1'; //step 2 Convert first byte of hash to 137 | # else { //first button to press (remainder of byte is a base 6 number 0 - 5) 138 | # Challenge_button1 = rsa_signature[0] % 5; 139 | # Challenge_button1 = Challenge_button1 + '0' + 1; //Add '0' and 1 so number will be ASCII 1 - 6 140 | # } 141 | # if (rsa_signature[15] < 6) Challenge_button2 = '1'; //step 3 do the same with 16th byte to 142 | # else { // get Challenge_button2 143 | # Challenge_button2 = rsa_signature[15] % 5; 144 | # Challenge_button2 = Challenge_button2 + '0' + 1; 145 | #} 146 | # if (rsa_signature[31] < 6) Challenge_button3 = '1'; //step 4 do the same with 32nd byte to 147 | # else { // get Challenge_button 148 | # Challenge_button3 = rsa_signature[31] % 5; 149 | # Challenge_button3 = Challenge_button3 + '0' + 1; 150 | # } 151 | # step 5 display the code to user to enter on OnlyKey 152 | 153 | # This method prevents some malware on a users system from sending fake requests to be signed 154 | # at the same time as real requests and tricking the user into signing the wrong data 155 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 156 | print('{} {} {}'.format(b1, b2, b3)) 157 | input() 158 | print('Trying to read the shared secret from OnlyKey...') 159 | ok_shared_secret = '' 160 | while ok_shared_secret == '': 161 | time.sleep(0.5) 162 | ok_shared_secret = ok.read_bytes(64, to_bytes=True) 163 | 164 | print('OnlyKey Shared Secret =', repr(ok_shared_secret)) 165 | 166 | print('Local Shared Secret =', repr(ok_shared_secret)) 167 | print() 168 | print('Assert that both shared secrets match') 169 | assert repr(shared_secret) == repr(ok_shared_secret) 170 | print('Ok, secrets match') 171 | print() 172 | 173 | #print 'Trying to read the KEK from OnlyKey...' 174 | #ok_KEK = '' 175 | #while ok_KEK == '': 176 | # time.sleep(0.5) 177 | # ok_KEK = ok.read_bytes(len(KEK), to_bytes=True) 178 | 179 | #print 'OnlyKey KEK =', repr(ok_KEK) 180 | 181 | print('Local KEK =', repr(KEK)) 182 | print() 183 | #print 'Assert that both KEKs match' 184 | #assert repr(KEK) == repr(ok_KEK) 185 | #print 'Ok, KEKs match' 186 | #print 187 | 188 | print('Done') 189 | -------------------------------------------------------------------------------- /tests/rsa_decrypt_3072.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Cipher import PKCS1_v1_5 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Hash import SHA 9 | from Crypto.Random import get_random_bytes 10 | from Crypto import Random 11 | import binascii 12 | 13 | from onlykey import OnlyKey, Message 14 | 15 | print('Generating a new rsa key pair...') 16 | random_generator = Random.new().read 17 | key = RSA.generate(3072, random_generator) #generate pub and priv key 18 | 19 | p = key.p 20 | q = key.q 21 | n = key.n 22 | 23 | binPrivKey = key.exportKey('DER') 24 | binPubKey = key.publickey().exportKey('DER') 25 | 26 | print('Done') 27 | print() 28 | print('RSA p value =', repr(p)) 29 | print('RSA q value =', repr(q)) 30 | print('RSA n value =', repr(n)) 31 | print() 32 | print('Initialize OnlyKey client...') 33 | ok = OnlyKey() 34 | print('Done') 35 | print() 36 | 37 | time.sleep(2) 38 | 39 | ok.read_string(timeout_ms=100) 40 | empty = 'a' 41 | while not empty: 42 | empty = ok.read_string(timeout_ms=100) 43 | 44 | print('You should see your OnlyKey blink 3 times') 45 | print() 46 | 47 | print('Setting RSA private...') 48 | 49 | def pack_long(n): 50 | """this conert 10045587143827198209824131064458461027107542643158086193488942239589004873324146472911535357118684101051965945865943581473431374244810144984918148150975257L 51 | to "\xbf\xcd\xce\xa0K\x93\x85}\xf0\x18\xb3\xd3L}\x14\xdb\xce0\x00uE,\x05'\xeeW\x1c\xeb\xcf\x8b\x1f\xcc\xc5\xc1\xe2\x17\xb7\xa3\xb6C\x16\xea?\xcchz\xebF1\xb7\xb1\x86\xb8\n}\x82\xebx\xce\x1b\x13\xdf\xdb\x19" 52 | it seems to be want you wanted? it's 64 bytes. 53 | """ 54 | h = '%x' % n 55 | s = bytes.fromhex('0'*(len(h) % 2) + h) 56 | return s 57 | 58 | def bin2hex(binStr): 59 | return binascii.hexlify(binStr) 60 | 61 | def hex2bin(hexStr): 62 | return binascii.unhexlify(hexStr) 63 | 64 | hexPrivKey = bin2hex(binPrivKey) 65 | hexPubKey = bin2hex(binPubKey) 66 | 67 | # p and q are long ints that are no more than 1/2 the size of pubkey 68 | # I need to convert these into a single byte array put p in the first 69 | # half byte[0] of the byte array and q in the second half byte[(type*128) / 2] 70 | # send the byte array to OnlyKey splitting into 56 bytes per packet 71 | q_and_p = pack_long(q) + pack_long(p) 72 | public_n = pack_long(n) 73 | # 74 | ok.send_large_message3(msg=Message.OKSETPRIV, slot_id=1, key_type=(3+32), payload=q_and_p) 75 | 76 | # ok.set_rsa_key(1, (1+64), byte array here) #Can only send 56 bytes per packet 77 | # Slot 1 - 4 for RSA 78 | # Type 1 = 1024, Type 2 = 2048, Type 3 = 3072, Type 4 = 4096 79 | # Key Features - 80 | # if backup key = type + 128 81 | # if signature key = type + 64 82 | # if decryption key = type + 32 83 | # if authentication key = type + 16 84 | # For this example it will be a decryption key 85 | time.sleep(1.5) 86 | print(ok.read_string()) 87 | 88 | time.sleep(2) 89 | print('You should see your OnlyKey blink 3 times') 90 | print() 91 | 92 | print('Trying to read the public RSA N part 1...') 93 | ok.send_message(msg=Message.OKGETPUBKEY, payload=chr(1)) #, payload=[1, 1]) 94 | time.sleep(1.5) 95 | for _ in xrange(10): 96 | ok_pubkey1 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 97 | if len(ok_pubkey1) == 64: 98 | break 99 | time.sleep(1) 100 | 101 | print() 102 | 103 | print('received=', repr(ok_pubkey1)) 104 | 105 | print('Trying to read the public RSA N part 2...') 106 | for _ in xrange(10): 107 | ok_pubkey2 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 108 | if len(ok_pubkey2) == 64: 109 | break 110 | time.sleep(1) 111 | 112 | print() 113 | 114 | print('received=', repr(ok_pubkey2)) 115 | 116 | print('Trying to read the public RSA N part 3...') 117 | for _ in xrange(10): 118 | ok_pubkey3 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 119 | if len(ok_pubkey3) == 64: 120 | break 121 | time.sleep(1) 122 | 123 | print() 124 | 125 | print('received=', repr(ok_pubkey3)) 126 | 127 | print('Trying to read the public RSA N part 4...') 128 | for _ in xrange(10): 129 | ok_pubkey4 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 130 | if len(ok_pubkey4) == 64: 131 | break 132 | time.sleep(1) 133 | 134 | print() 135 | 136 | print('received=', repr(ok_pubkey4)) 137 | 138 | print('Trying to read the public RSA N part 5...') 139 | for _ in xrange(10): 140 | ok_pubkey5 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 141 | if len(ok_pubkey5) == 64: 142 | break 143 | time.sleep(1) 144 | 145 | print() 146 | 147 | print('received=', repr(ok_pubkey5)) 148 | 149 | print('Trying to read the public RSA N part 6...') 150 | for _ in xrange(10): 151 | ok_pubkey6 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 152 | if len(ok_pubkey6) == 64: 153 | break 154 | time.sleep(1) 155 | 156 | print() 157 | 158 | print('received=', repr(ok_pubkey6)) 159 | 160 | if not ok_pubkey6: 161 | raise Exception('failed to set the RSA key') 162 | 163 | print('Assert that the received public N match the one generated locally') 164 | print('Local Public N=', repr(public_n)) 165 | ok_pubkey = ok_pubkey1 + ok_pubkey2 + ok_pubkey3 + ok_pubkey4 + ok_pubkey5 + ok_pubkey6 166 | assert ok_pubkey == public_n 167 | print('Ok, public N matches') 168 | print() 169 | 170 | message = 'Secret message' 171 | #h = SHA.new(message) 172 | cipher = PKCS1_v1_5.new(key) 173 | ciphertext = cipher.encrypt(message) 174 | 175 | #hex_enc_data = bin2hex(enc_data) 176 | print('encrypted payload = ', repr(ciphertext)) 177 | print() 178 | 179 | 180 | # Compute the challenge pin 181 | h = hashlib.sha256() 182 | h.update(ciphertext) 183 | d = h.digest() 184 | 185 | assert len(d) == 32 186 | 187 | def get_button(byte): 188 | ibyte = ord(byte) 189 | if ibyte < 6: 190 | return 1 191 | return ibyte % 5 + 1 192 | 193 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 194 | 195 | print('Sending the payload to the OnlyKey...') 196 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=ciphertext, slot_id=1) 197 | 198 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 199 | print('{} {} {}'.format(b1, b2, b3)) 200 | input() 201 | print('Trying to read the decrypted data from OnlyKey...') 202 | ok_decrypted = '' 203 | while ok_decrypted == '': 204 | time.sleep(0.5) 205 | ok_decrypted = ok.read_bytes(len(message), to_bytes=True) 206 | 207 | dsize = len(message) 208 | sentinel = Random.new().read(15+dsize) 209 | plaintext = cipher.decrypt(ciphertext, sentinel) 210 | 211 | print('Decrypted by OnlyKey, data=', repr(ok_decrypted)) 212 | 213 | print('Local decrypted data =', repr(plaintext)) 214 | print('Assert that the decrypted data generated locally matches the data generated on the OnlyKey') 215 | assert repr(ok_decrypted) == repr(plaintext) 216 | print('Ok, data matches') 217 | print() 218 | 219 | print('Done') 220 | -------------------------------------------------------------------------------- /tests/test_priv.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | Version: Mailvelope v1.7.1 3 | Comment: https://www.mailvelope.com 4 | 5 | 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 113 | 2B82fzd4 114 | =SVo5 115 | -----END PGP PRIVATE KEY BLOCK----- 116 | -------------------------------------------------------------------------------- /tests/rsa_decrypt_4096.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import hashlib 3 | import time 4 | import os 5 | 6 | from Crypto.Cipher import PKCS1_v1_5 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Hash import SHA 9 | from Crypto.Random import get_random_bytes 10 | from Crypto import Random 11 | import binascii 12 | 13 | from onlykey import OnlyKey, Message 14 | 15 | print('Generating a new rsa key pair...') 16 | random_generator = Random.new().read 17 | key = RSA.generate(4096, random_generator) #generate pub and priv key 18 | 19 | p = key.p 20 | q = key.q 21 | n = key.n 22 | 23 | binPrivKey = key.exportKey('DER') 24 | binPubKey = key.publickey().exportKey('DER') 25 | 26 | print('Done') 27 | print() 28 | print('RSA p value =', repr(p)) 29 | print('RSA q value =', repr(q)) 30 | print('RSA n value =', repr(n)) 31 | print() 32 | print('Initialize OnlyKey client...') 33 | ok = OnlyKey() 34 | print('Done') 35 | print() 36 | 37 | time.sleep(2) 38 | 39 | ok.read_string(timeout_ms=100) 40 | empty = 'a' 41 | while not empty: 42 | empty = ok.read_string(timeout_ms=100) 43 | 44 | print('You should see your OnlyKey blink 3 times') 45 | print() 46 | 47 | print('Setting RSA private...') 48 | 49 | def pack_long(n): 50 | """this conert 10045587143827198209824131064458461027107542643158086193488942239589004873324146472911535357118684101051965945865943581473431374244810144984918148150975257L 51 | to "\xbf\xcd\xce\xa0K\x93\x85}\xf0\x18\xb3\xd3L}\x14\xdb\xce0\x00uE,\x05'\xeeW\x1c\xeb\xcf\x8b\x1f\xcc\xc5\xc1\xe2\x17\xb7\xa3\xb6C\x16\xea?\xcchz\xebF1\xb7\xb1\x86\xb8\n}\x82\xebx\xce\x1b\x13\xdf\xdb\x19" 52 | it seems to be want you wanted? it's 64 bytes. 53 | """ 54 | h = '%x' % n 55 | s = ('0'*(len(h) % 2) + h).decode('hex') 56 | return s 57 | 58 | def bin2hex(binStr): 59 | return binascii.hexlify(binStr) 60 | 61 | def hex2bin(hexStr): 62 | return binascii.unhexlify(hexStr) 63 | 64 | hexPrivKey = bin2hex(binPrivKey) 65 | hexPubKey = bin2hex(binPubKey) 66 | 67 | # p and q are long ints that are no more than 1/2 the size of pubkey 68 | # I need to convert these into a single byte array put p in the first 69 | # half byte[0] of the byte array and q in the second half byte[(type*128) / 2] 70 | # send the byte array to OnlyKey splitting into 56 bytes per packet 71 | q_and_p = pack_long(q) + pack_long(p) 72 | public_n = pack_long(n) 73 | # 74 | ok.send_large_message3(msg=Message.OKSETPRIV, slot_id=1, key_type=(4+32), payload=q_and_p) 75 | 76 | # ok.set_rsa_key(1, (1+64), byte array here) #Can only send 56 bytes per packet 77 | # Slot 1 - 4 for RSA 78 | # Type 1 = 1024, Type 2 = 2048, Type 3 = 3072, Type 4 = 4096 79 | # Key Features - 80 | # if backup key = type + 128 81 | # if signature key = type + 64 82 | # if decryption key = type + 32 83 | # if authentication key = type + 16 84 | # For this example it will be a decryption key 85 | time.sleep(1.5) 86 | print(ok.read_string()) 87 | 88 | time.sleep(2) 89 | print('You should see your OnlyKey blink 3 times') 90 | print() 91 | 92 | print('Trying to read the public RSA N part 1...') 93 | ok.send_message(msg=Message.OKGETPUBKEY, payload=chr(1)) #, payload=[1, 1]) 94 | time.sleep(1.5) 95 | for _ in xrange(10): 96 | ok_pubkey1 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 97 | if len(ok_pubkey1) == 64: 98 | break 99 | time.sleep(1) 100 | 101 | print() 102 | 103 | print('received=', repr(ok_pubkey1)) 104 | 105 | print('Trying to read the public RSA N part 2...') 106 | for _ in xrange(10): 107 | ok_pubkey2 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 108 | if len(ok_pubkey2) == 64: 109 | break 110 | time.sleep(1) 111 | 112 | print() 113 | 114 | print('received=', repr(ok_pubkey2)) 115 | 116 | print('Trying to read the public RSA N part 3...') 117 | for _ in xrange(10): 118 | ok_pubkey3 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 119 | if len(ok_pubkey3) == 64: 120 | break 121 | time.sleep(1) 122 | 123 | print() 124 | 125 | print('received=', repr(ok_pubkey3)) 126 | 127 | print('Trying to read the public RSA N part 4...') 128 | for _ in xrange(10): 129 | ok_pubkey4 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 130 | if len(ok_pubkey4) == 64: 131 | break 132 | time.sleep(1) 133 | 134 | print() 135 | 136 | print('received=', repr(ok_pubkey4)) 137 | 138 | print('Trying to read the public RSA N part 5...') 139 | for _ in xrange(10): 140 | ok_pubkey5 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 141 | if len(ok_pubkey5) == 64: 142 | break 143 | time.sleep(1) 144 | 145 | print() 146 | 147 | print('received=', repr(ok_pubkey5)) 148 | 149 | print('Trying to read the public RSA N part 6...') 150 | for _ in xrange(10): 151 | ok_pubkey6 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 152 | if len(ok_pubkey6) == 64: 153 | break 154 | time.sleep(1) 155 | 156 | print() 157 | 158 | print('received=', repr(ok_pubkey6)) 159 | 160 | print('Trying to read the public RSA N part 7...') 161 | for _ in xrange(10): 162 | ok_pubkey7 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 163 | if len(ok_pubkey7) == 64: 164 | break 165 | time.sleep(1) 166 | 167 | print() 168 | 169 | print('received=', repr(ok_pubkey7)) 170 | 171 | print('Trying to read the public RSA N part 8...') 172 | for _ in xrange(10): 173 | ok_pubkey8 = ok.read_bytes(64, to_bytes=True, timeout_ms=1000) 174 | if len(ok_pubkey8) == 64: 175 | break 176 | time.sleep(1) 177 | 178 | print() 179 | 180 | print('received=', repr(ok_pubkey8)) 181 | 182 | if not ok_pubkey8: 183 | raise Exception('failed to set the RSA key') 184 | 185 | print('Assert that the received public N match the one generated locally') 186 | print('Local Public N=', repr(public_n)) 187 | ok_pubkey = ok_pubkey1 + ok_pubkey2 + ok_pubkey3 + ok_pubkey4 + ok_pubkey5 + ok_pubkey6 + ok_pubkey7 + ok_pubkey8 188 | assert ok_pubkey == public_n 189 | print('Ok, public N matches') 190 | print() 191 | 192 | message = 'Secret message' 193 | #h = SHA.new(message) 194 | cipher = PKCS1_v1_5.new(key) 195 | ciphertext = cipher.encrypt(message) 196 | 197 | #hex_enc_data = bin2hex(enc_data) 198 | print('encrypted payload = ', repr(ciphertext)) 199 | print() 200 | 201 | 202 | # Compute the challenge pin 203 | h = hashlib.sha256() 204 | h.update(ciphertext) 205 | d = h.digest() 206 | 207 | assert len(d) == 32 208 | 209 | def get_button(byte): 210 | ibyte = ord(byte) 211 | if ibyte < 6: 212 | return 1 213 | return ibyte % 5 + 1 214 | 215 | b1, b2, b3 = get_button(d[0]), get_button(d[15]), get_button(d[31]) 216 | 217 | print('Sending the payload to the OnlyKey...') 218 | ok.send_large_message2(msg=Message.OKDECRYPT, payload=ciphertext, slot_id=1) 219 | 220 | print('Please enter the 3 digit challenge code on OnlyKey (and press ENTER if necessary)') 221 | print('{} {} {}'.format(b1, b2, b3)) 222 | input() 223 | print('Trying to read the decrypted data from OnlyKey...') 224 | ok_decrypted = '' 225 | while ok_decrypted == '': 226 | time.sleep(0.5) 227 | ok_decrypted = ok.read_bytes(len(message), to_bytes=True) 228 | 229 | dsize = len(message) 230 | sentinel = Random.new().read(15+dsize) 231 | plaintext = cipher.decrypt(ciphertext, sentinel) 232 | 233 | print('Decrypted by OnlyKey, data=', repr(ok_decrypted)) 234 | 235 | print('Local decrypted data =', repr(plaintext)) 236 | print('Assert that the decrypted data generated locally matches the data generated on the OnlyKey') 237 | assert repr(ok_decrypted) == repr(plaintext) 238 | print('Ok, data matches') 239 | print() 240 | 241 | print('Done') 242 | -------------------------------------------------------------------------------- /tests/PGP_message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import hashlib 3 | import time 4 | import os 5 | import sys 6 | 7 | #from progressbar import ProgressBar, AnimatedMarker, Timer, Bar, Percentage, Widget 8 | 9 | import pgpy 10 | from pgpy import PGPKey 11 | from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm 12 | from pgpy.packet.fields import RSAPub,MPI,RSAPriv 13 | from pgpy.packet.packets import PubKeyV4,PrivKeyV4 14 | 15 | from datetime import timedelta 16 | 17 | import binascii 18 | 19 | from onlykey import OnlyKey, Message 20 | 21 | ok = OnlyKey() 22 | 23 | def custRSAPub(n,e): 24 | res = RSAPriv() 25 | res.n = MPI(n) 26 | res.e = MPI(e) 27 | return res 28 | 29 | def custPubKeyV4(custkey): 30 | res = PrivKeyV4() 31 | res.pkalg = PubKeyAlgorithm.RSAEncryptOrSign 32 | res.keymaterial = custkey 33 | res.update_hlen() 34 | return res 35 | 36 | def rsatogpg(e,N,name,**idargs): 37 | """ 38 | :param e,N: RSA parameters as Python integers or longints 39 | :param name: Identity name 40 | :param idargs: PGP Identity parameters, such as comment,email 41 | :return: PGPy pubkey object 42 | """ 43 | rsakey = custPubKeyV4(custRSAPub(N,e)) 44 | pgpkey = pgpy.PGPKey() 45 | pgpkey._key = rsakey 46 | 47 | uid = pgpy.PGPUID.new(name, **idargs) 48 | uid._parent = pgpkey 49 | pgpkey._uids.append(uid) 50 | return pgpkey 51 | 52 | def makekey(): 53 | n = ok.getpub() 54 | if len(n) == 128: 55 | priv_key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 1024) 56 | if len(n) == 256: 57 | priv_key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 2048) 58 | if len(n) == 384: 59 | priv_key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 3072) 60 | if len(n) == 512: 61 | priv_key = pgpy.PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096) 62 | #uid = pgpy.PGPUID.new('Abraham Lincoln', comment='Honest Abe', email='abraham.lincoln@whitehouse.gov') 63 | #priv_key.add_uid(uid, usage={KeyFlags.Sign}, hashes=[HashAlgorithm.SHA512, HashAlgorithm.SHA256], 64 | # compression=[CompressionAlgorithm.BZ2, CompressionAlgorithm.Uncompressed], 65 | # key_expires=timedelta(days=365)) 66 | #p = n[:(len(n)/2)] 67 | #q = n[(len(n)/2):] 68 | n = n.hex() 69 | N = int(n, 16) 70 | #p = p.encode("HEX") 71 | #p = long(p, 16) 72 | #q = q.encode("HEX") 73 | #q = long(q, 16) 74 | e = int('10001', 16) 75 | #pub = rsatogpg(e,N,p,q,'Nikola Tesla') 76 | #rsakey = custPubKeyV4(custRSAPub(N,e)) 77 | #priv_key._key = rsakey 78 | return priv_key 79 | 80 | 81 | # we can start by generating a primary key. For this example, we'll use RSA, but it could be DSA or ECDSA as well 82 | #with open('test_priv.asc', 'rb') as f: 83 | # t = f.read().replace('\r', '') 84 | 85 | #priv_key, _ = PGPKey.from_blob(t) 86 | priv_key = pgpy.PGPKey() 87 | # we now have some key material, but our new key doesn't have a user ID yet, and therefore is not yet usable! 88 | #with priv_key.unlock("test"): 89 | # uid = pgpy.PGPUID.new('Abraham Lincoln', comment='Honest Abe', email='abraham.lincoln@whitehouse.gov') 90 | # now we must add the new user id to the key. We'll need to specify all of our preferences at this point 91 | # because PGPy doesn't have any built-in key preference defaults at this time 92 | # this example is similar to GnuPG 2.1.x defaults, with no expiration or preferred keyserver 93 | #priv_key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage}, 94 | # hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224], 95 | # ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128], 96 | # compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed]) 97 | 98 | print() 99 | print('Do you want to sign or decrypt a message?') 100 | print('s = sign, d = decrypt') 101 | print() 102 | 103 | action = input() 104 | 105 | print() 106 | print('Enter RSA key slot number to use (1 - 4) or enter 0 to list key labels') 107 | print() 108 | 109 | slot = int(input()) 110 | ok.slot(slot) 111 | 112 | while slot== 0: 113 | ok.displaykeylabels() 114 | print() 115 | print('Enter slot number to use (1 - 4) or enter 0 to list key labels') 116 | print() 117 | slot = int(input()) 118 | ok.slot(slot) 119 | 120 | 121 | # PGPMessage will automatically determine if this is a cleartext message or not 122 | # message_from_file = pgpy.PGPMessage.from_file("path/to/a/message") 123 | if action == 's': 124 | priv_key = makekey() 125 | uid = pgpy.PGPUID.new('Nikola Tesla') 126 | #uid._parent = priv_key 127 | priv_key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage}, 128 | hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224], 129 | ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128], 130 | compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed], 131 | key_expires=timedelta(days=365)) 132 | 133 | print() 134 | print('Do you want to sign a text message or add signature to a PGP Message?') 135 | print('t = text message, p = PGP Message') 136 | print() 137 | action2 = input() 138 | if action2 == 't': 139 | print() 140 | print('Type or paste the text message, press return to go to new line, and then press Ctrl+D or Ctrl+Z (Windows only)') 141 | print() 142 | msg_blob = sys.stdin.read() 143 | message_from_blob = priv_key.sign(msg_blob) 144 | print('Encoded Signed Message =') 145 | print('-----BEGIN PGP SIGNED MESSAGE-----') 146 | print('Hash: SHA256') 147 | print() 148 | if action2 == 'p': 149 | print() 150 | print('Paste OpenPGP Message, press return to go to new line, and then press Ctrl+D or Ctrl+Z (Windows only)') 151 | print() 152 | msg_blob = sys.stdin.read() 153 | message_from_blob = pgpy.PGPMessage.from_blob(msg_blob) 154 | print() 155 | print('Message=', repr(message_from_blob)) 156 | message_from_blob |= priv_key.sign2(message_from_blob) 157 | print('Encoded Signed Message =') 158 | #print 159 | #sign_bytes = message_from_blob.__bytes__() 160 | #print 'Message Bytes=', repr(sign_bytes) 161 | #print 162 | sign_text = str(message_from_blob) 163 | print(msg_blob, sign_text) 164 | print() 165 | #print 'Decoded Signed Message =', str(sign_bytes) 166 | if action == 'd': 167 | #pub_key = makepub() 168 | print() 169 | print('Paste OpenPGP Message, press return to go to new line, and then press Ctrl+D or Ctrl+Z (Windows only)') 170 | print() 171 | msg_blob = sys.stdin.read() 172 | message_from_blob = pgpy.PGPMessage.from_blob(msg_blob) 173 | print() 174 | print('Message=', repr(message_from_blob)) 175 | #with priv_key.unlock("test"): 176 | decrypted_message = priv_key.decrypt(message_from_blob) 177 | #decrypted_message2 = priv_key.parse(decrypted_message) 178 | #decrypted_message3 = priv_key.bytes_to_text(message_from_blob) 179 | print() 180 | dec_bytes = decrypted_message.__bytes__() 181 | print('Decoded Decrypted Message =', str(dec_bytes)) 182 | print() 183 | dec_text = str(decrypted_message) 184 | print('Encoded Decrypted Message =') 185 | print(dec_text) 186 | 187 | 188 | 189 | print('Done') 190 | -------------------------------------------------------------------------------- /scripts/PGPparseprivate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os 3 | import sys 4 | import pgpy 5 | from Crypto.Util.number import long_to_bytes 6 | 7 | # This python script can parse the private keys out of OpenPGP keys (ed25519 or RSA). 8 | # Replace the passphrase with your OpenPGP passphrase. 9 | rootkey_passphrase = "test" 10 | # If you use a different passphrase for subkeys (such as rootless subkeys) replace 11 | # the passphrase below with your OpenPGP subkey passphrase 12 | subkey_passphrase = "example subkey passphrase" 13 | 14 | # Replace this with your ascii armored OpenPGP key 15 | rootkey_ascii_armor = """\ 16 | -----BEGIN PGP PRIVATE KEY BLOCK----- 17 | Version: Mailvelope v4.4.1 18 | Comment: https://www.mailvelope.com 19 | 20 | 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 128 | tvTu13Yy 129 | =pOOJ 130 | -----END PGP PRIVATE KEY BLOCK----- 131 | """ 132 | 133 | (rootkey, _) = pgpy.PGPKey.from_blob(rootkey_ascii_armor) 134 | 135 | # Or if you would prefer, import key as a file like this 136 | # (rootkey, _) = pgpy.PGPKey.from_file('./testkey.asc') 137 | 138 | # Run the script and raw keys will be displayed. Only run this on a 139 | # secure trusted system. 140 | 141 | assert rootkey.is_protected 142 | assert rootkey.is_unlocked is False 143 | 144 | try: 145 | print('Load these raw key values to OnlyKey by using the OnlyKey App --> Advanced -> Add Private Key') 146 | with rootkey.unlock(rootkey_passphrase): 147 | # rootkey is now unlocked 148 | assert rootkey.is_unlocked 149 | print('rootkey is now unlocked') 150 | print('rootkey type %s', rootkey._key._pkalg) 151 | if 'RSA' in rootkey._key._pkalg._name_: 152 | print('rootkey value:') 153 | #Parse rsa pgp key 154 | primary_keyp = long_to_bytes(rootkey._key.keymaterial.p) 155 | primary_keyq = long_to_bytes(rootkey._key.keymaterial.q) 156 | print(("".join(["%02x" % c for c in primary_keyp])) + ("".join(["%02x" % c for c in primary_keyq]))) 157 | print('rootkey size =', (len(primary_keyp)+len(primary_keyq))*8, 'bits') 158 | print('subkey values:') 159 | for subkey, value in rootkey._children.items(): 160 | print('subkey id', subkey) 161 | sub_keyp = long_to_bytes(value._key.keymaterial.p) 162 | sub_keyq = long_to_bytes(value._key.keymaterial.q) 163 | print('subkey value') 164 | print(("".join(["%02x" % c for c in sub_keyp])) + ("".join(["%02x" % c for c in sub_keyq]))) 165 | print('subkey size =', (len(primary_keyp)+len(primary_keyq))*8, 'bits') 166 | else: 167 | print('rootkey value:') 168 | #Parse ed25519 pgp key 169 | primary_key = long_to_bytes(rootkey._key.keymaterial.s) 170 | print("".join(["%02x" % c for c in primary_key])) 171 | print('subkey values:') 172 | for subkey, value in rootkey._children.items(): 173 | print('subkey id', subkey) 174 | sub_key = long_to_bytes(value._key.keymaterial.s) 175 | print('subkey value') 176 | print("".join(["%02x" % c for c in sub_key])) 177 | 178 | except: 179 | print('Unlocking root key failed, attempting rootless subkey unlock.') 180 | try: 181 | print('subkey key values:') 182 | for subkey, value in rootkey._children.items(): 183 | assert value.is_protected 184 | assert value.is_unlocked is False 185 | with value.unlock(subkey_passphrase): 186 | # subkey is now unlocked 187 | assert value.is_unlocked 188 | print('subkey is now unlocked') 189 | print('subkey id', subkey) 190 | if 'RSA' in subkey._key._pkalg._name_: 191 | sub_keyp = long_to_bytes(value._key.keymaterial.p) 192 | sub_keyq = long_to_bytes(value._key.keymaterial.q) 193 | print('subkey value') 194 | print(("".join(["%02x" % c for c in sub_keyp])) + ("".join(["%02x" % c for c in sub_keyq]))) 195 | else: 196 | sub_key = long_to_bytes(value._key.keymaterial.s) 197 | print('subkey value') 198 | print("".join(["%02x" % c for c in sub_key])) 199 | # subkey is no longer unlocked 200 | assert value.is_unlocked is False 201 | 202 | except: 203 | print('Unlocking failed') 204 | 205 | # rootkey is no longer unlocked 206 | assert rootkey.is_unlocked is False 207 | 208 | -------------------------------------------------------------------------------- /scripts/onlykey-cli-gpg-add-keys.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Parse the private keys out of OpenPGP keys (ed25519 or RSA) and add them to OnlyKey with keygrip labels. 4 | 5 | It will extract and set keys and subkeys in your OnlyKey. Only run this on a secure trusted system. 6 | """ 7 | import sys 8 | import argparse 9 | import fileinput 10 | import getpass 11 | import pgpy 12 | import subprocess 13 | import atexit 14 | import time 15 | from onlykey.client import OnlyKey, MessageField 16 | from Crypto.Util.number import long_to_bytes 17 | 18 | algorithmnames = {0x0: "RSA", 0x01: "RSA", 0x02: "RSA", 0x03: "RSA", 0x10: "ElGamal", 19 | 0x11: "DSA", 0x12: "ECDH", 0x13: "ECDSA", 0x14: "ElGammal", 20 | 0x15: "DiffieHellman", 0x16: "EdDSA"} 21 | 22 | only_key = OnlyKey() 23 | 24 | 25 | class KeyDetails: 26 | def __init__(self): 27 | self.type = "" 28 | self.keygrip = "" 29 | self.keyid = "" 30 | self.curvetype = "" 31 | self.keylength = "" 32 | self.algorithm = "" 33 | self.trust = "" 34 | self.keyvalue = None 35 | 36 | def __repr__(self): 37 | return f"keyid: {self.keyid}, type: {self.type}, algorithm: {self.algorithm}, keylength {self.keylength}" 38 | 39 | def get_keygrips_by_keyid_from_blob(keyblob): 40 | retval = subprocess.run(['gpg', '--keyid', 'long', '--with-keygrip', 41 | '--with-colons', '--import-options', 'show-only', 42 | '--import', '-'], stdout=subprocess.PIPE, 43 | input=keyblob.encode("ascii")) 44 | lines = retval.stdout.split(b'\n') 45 | keys = {} 46 | currentKey = None 47 | disabled = True 48 | for line in lines: 49 | values = line.split(b':') 50 | if len(values) < 2: 51 | continue 52 | if len(values) >= 12 and values[11] == b'D': 53 | disabled = True 54 | continue 55 | if values[0] in [b'pub', b'sub', b'sec', b'ssb'] and values[1] in [b'f', b'u', b'-']: 56 | disabled = False 57 | if disabled is True: 58 | continue 59 | 60 | 61 | if values[0] in [b'pub', b'sub', b'sec', b'ssb']: 62 | currentKey = KeyDetails() 63 | keys[values[4]] = currentKey 64 | if values[0] in(b'pub', 'sec'): 65 | currentKey.type = b's' 66 | else: 67 | currentKey.type = values[11] 68 | currentKey.trust = values[1] 69 | currentKey.curvetype = values[16] 70 | currentKey.keyid = values[4] 71 | currentKey.keylength = values[2] 72 | currentKey.algorithm = int(values[3]) 73 | if currentKey.algorithm in algorithmnames: 74 | currentKey.algorithmname = algorithmnames[currentKey.algorithm] 75 | else: 76 | currentKey.algorithmname = "Unknown" 77 | elif values[0] == b'grp': 78 | currentKey.keygrip = values[9] 79 | currentKey.okkeygrip = values[9][:16] 80 | elif values[0] == b'fpr': 81 | currentKey.fingerprint = values[9] 82 | return keys 83 | 84 | 85 | def set_key_only_key(keyslots, key): 86 | if key.type == b'e': 87 | key_features = 'd' 88 | elif key.type in (b's', b'scESC'): 89 | key_features = 's' 90 | else: 91 | key_features = key["type"] 92 | 93 | for i in keyslots: 94 | if i.label != b'': 95 | continue 96 | if key.algorithmname == "RSA": 97 | if i.targetslot > 100: 98 | continue 99 | print(f"targetslot - {i.targetslot}, label - {i.label}") 100 | if key.keylength == b'4096': 101 | key_type = '4' 102 | elif key.keylength == b'2048': 103 | key_type = '2' 104 | print(f"only_key.setkey({i.targetslot}, {key_type}, {key_features}, {key.keyvalue})") 105 | print(f"only_key.setslot({i.number}, {MessageField.LABEL}, {key.okkeygrip.decode('utf-8')})") 106 | only_key.setkey(i.targetslot, key_type, key_features, key.keyvalue) 107 | only_key.setslot(i.number, MessageField.LABEL, key.okkeygrip.decode('utf-8')) 108 | return 109 | elif key.algorithmname in ("ECDH", "EdDSA", "ECDSA"): 110 | if i.targetslot <= 100: 111 | continue 112 | if key.curvetype in [b'ed25519', b'cv25519']: 113 | print(f"only_key.setkey({i.targetslot}, 'x', '{key_features}', '{key.keyvalue}')") 114 | only_key.setkey(i.targetslot, 'x', key_features, key.keyvalue) 115 | elif key.curvetype in [b'nistp256']: 116 | print(f"only_key.setkey({i.targetslot}, 'n', '{key_features}', '{key.keyvalue}')") 117 | only_key.setkey(i.targetslot, 'n', key_features, key.keyvalue) 118 | elif key.curvetype in [b'secp256k1']: 119 | print(f"only_key.setkey({i.targetslot}, 's', '{key_features}', '{key.keyvalue}')") 120 | only_key.setkey(i.targetslot, 's', key_features, key.keyvalue) 121 | else: 122 | raise "Error unsupported curve" 123 | print(f"only_key.setslot({i.number}, {MessageField.LABEL}, {key.okkeygrip.decode('utf-8')})") 124 | only_key.setslot(i.number, MessageField.LABEL, key.okkeygrip.decode('utf-8')) 125 | return 126 | else: 127 | print("algorithm - %s is unsupported" % (key.algorithmname)) 128 | 129 | 130 | def get_key_type(key:pgpy.PGPKey): 131 | """ 132 | Get the key's type. 133 | 134 | Parameters 135 | ---------- 136 | key : pgpy.PGPKey 137 | The key from which to get the type. 138 | 139 | Returns 140 | ------- 141 | str 142 | The type of the key. One of RSA, DSA, ElGamal, ECDSA, EdDSA or ECDH. 143 | 144 | """ 145 | if isinstance(key._key.keymaterial, pgpy.packet.fields.RSAPriv): 146 | return 'RSA' 147 | elif isinstance(key._key.keymaterial, pgpy.packet.fields.DSAPriv): 148 | return 'DSA' 149 | elif isinstance(key._key.keymaterial, pgpy.packet.fields.ElGPriv): 150 | return 'ElGamal' 151 | elif isinstance(key._key.keymaterial, pgpy.packet.fields.ECDSAPriv): 152 | return 'ECDSA' 153 | elif isinstance(key._key.keymaterial, pgpy.packet.fields.EdDSAPriv): 154 | return 'EdDSA' 155 | elif isinstance(key._key.keymaterial, pgpy.packet.fields.ECDHPriv): 156 | return 'ECDH' 157 | return '' 158 | 159 | 160 | def get_key_value(key:pgpy.PGPKey): 161 | """ 162 | Get the private key's value and size. 163 | 164 | Parameters 165 | ---------- 166 | key : pgpy.PGPKey 167 | The private key. 168 | 169 | Raises 170 | ------ 171 | NotImplementedError 172 | Key type not supported for DSA and ElGamal. 173 | 174 | Returns 175 | ------- 176 | value : str 177 | hex string representing the raw private key. 178 | size : int 179 | The size of the raw private key in bits. 180 | 181 | """ 182 | key_type = get_key_type(key) 183 | if key_type == 'RSA': 184 | p = long_to_bytes(key._key.keymaterial.p) 185 | q = long_to_bytes(key._key.keymaterial.q) 186 | value = "".join([f"{c:02x}" for c in p]) + "".join([f"{c:02x}" for c in q]) 187 | size = (len(p) + len(q)) * 8 188 | elif key_type in ('ECDSA', 'EdDSA', 'ECDH'): 189 | s = long_to_bytes(key._key.keymaterial.s) 190 | value = "".join([f"{c:02x}" for c in s]) 191 | size = len(s)*8 192 | else: 193 | raise NotImplementedError(f"Get value from {key_type} key is not " 194 | f"supported") 195 | return (value, size) 196 | 197 | 198 | def get_key_flags(key:pgpy.PGPKey): 199 | """ 200 | Get the key's usage flags. 201 | 202 | Parameters 203 | ---------- 204 | key : pgpy.PGPKey 205 | The key. 206 | 207 | Returns 208 | ------- 209 | str 210 | Usage flags of the key. 211 | Flags: 212 | C : Certification 213 | 214 | S : Signature 215 | 216 | E : Encryption 217 | 218 | A : Authentication 219 | 220 | """ 221 | flags = [] 222 | strs = {pgpy.constants.KeyFlags.Certify : 'C', 223 | pgpy.constants.KeyFlags.Sign : 'S', 224 | pgpy.constants.KeyFlags.EncryptCommunications : 'E', 225 | pgpy.constants.KeyFlags.Authentication : 'A'} 226 | for sig in key.self_signatures: 227 | if not sig.is_expired: 228 | flags += sig.key_flags 229 | return "".join(strs.get(flag, '') for flag in flags) 230 | 231 | def get_key_slots(): 232 | keylabels = only_key.getkeylabels() 233 | for i in keylabels: 234 | if i.label == "ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ": 235 | i.label = "" 236 | if i.number + 72 > 100: 237 | i.targetslot = i.number + 72 238 | else: 239 | i.targetslot = i.number - 24 240 | i.label = i.label.encode('ascii') 241 | return keylabels 242 | 243 | 244 | def Run(): 245 | BLACK = "\033[0;30m" 246 | RED = "\033[0;31m" 247 | GREEN = "\033[0;32m" 248 | BLUE = "\033[0;34m" 249 | END = "\033[0m" 250 | 251 | 252 | parser = argparse.ArgumentParser( 253 | description='Extract secret subkeys from a OpenPGP key.\n\n' 254 | 'This script will display and set the raw private keys and subkeys on your OnlyKey.\n' 255 | 'Only run this on a secure trusted system.', 256 | epilog='''Extract and load keys onto OnlyKey example: 257 | gpg --export-secret-keys -a keyid | ./onlykey-cli-gpg-add-keys - 258 | ./onlykey-cli-gpg-add-keys ~/mykey.asc --no-expired 259 | Extract and display for loading in the OnlyKey Desktop App example: 260 | ./onlykey-cli-gpg-add-keys ~/mykey.asc -d 261 | ''', 262 | formatter_class=argparse.RawDescriptionHelpFormatter) 263 | parser.add_argument('keyfile', type=str, 264 | help="path to the secret PEM-encoded key file, or " 265 | "'-' for stdin.'") 266 | parser.add_argument('-d', '--display', action='store_true', 267 | help='display only, extracted keys shown for loading in the OnlyKey Desktop App') 268 | parser.add_argument('--no-expired', action='store_true', 269 | help='do not show expired subkeys') 270 | parser.add_argument('--no-colors', action='store_true', 271 | help='do not output with colors. Usefull for piping ' 272 | 'output and use in scripts.') 273 | parser.add_argument('-p', '--passphrase', type=str, 274 | help="the passphrase of the key. Don't forget bash's " 275 | "history keeps everything !") 276 | 277 | args = parser.parse_args() 278 | 279 | if args.no_colors: 280 | BLACK = "" 281 | RED = "" 282 | GREEN = "" 283 | BLUE = "" 284 | END = "" 285 | 286 | # Parse input - either a file or stdin - for private key block 287 | armored_key = None 288 | with fileinput.input(files=args.keyfile) as keyfile: 289 | for line in keyfile: 290 | if line == "-----BEGIN PGP PRIVATE KEY BLOCK-----\n": 291 | armored_key = line 292 | elif line == "-----END PGP PRIVATE KEY BLOCK-----\n": 293 | armored_key += line 294 | break 295 | elif armored_key is not None: 296 | armored_key += line 297 | primary_key, _ = pgpy.PGPKey.from_blob(armored_key) 298 | keygrip_by_id = get_keygrips_by_keyid_from_blob(armored_key) 299 | 300 | try: 301 | password = args.passphrase if args.passphrase else getpass.getpass( 302 | "Enter key password: ") 303 | if primary_key.is_protected is True and primary_key._key.keymaterial.encbytes == b'': 304 | print("No secret primary key") 305 | elif primary_key.is_protected is True: 306 | with primary_key.unlock(password): 307 | # primary_key is now unlocked 308 | assert primary_key.is_unlocked 309 | print('primary key is now unlocked') 310 | print('Load these raw key values to OnlyKey by using the ' 311 | 'OnlyKey App --> Advanced -> Add Private Key') 312 | key_value, key_size = get_key_value(primary_key) 313 | print(f'primary key id: {primary_key.fingerprint.keyid}') 314 | print(f'primary key type: {get_key_type(primary_key)}') 315 | print(f'primary key usage: {get_key_flags(primary_key)}') 316 | if args.display: 317 | print(f'{GREEN}primary key value:{END} {key_value}') 318 | print(f'primary key size: {key_size} bits') 319 | keygrip_by_id[primary_key.fingerprint.keyid.encode('ascii')].keyvalue = key_value 320 | elif primary_key.is_primary is True: 321 | # primary_key is not password protected 322 | print('Load these raw key values to OnlyKey by using the ' 323 | 'OnlyKey App --> Advanced -> Add Private Key') 324 | key_value, key_size = get_key_value(primary_key) 325 | print(f'primary key id: {primary_key.fingerprint.keyid}') 326 | print(f'primary key type: {get_key_type(primary_key)}') 327 | print(f'primary key usage: {get_key_flags(primary_key)}') 328 | if args.display: 329 | print(f'{GREEN}primary key value:{END} {key_value}') 330 | print(f'primary key size: {key_size} bits') 331 | keygrip_by_id[primary_key.fingerprint.keyid.encode('ascii')].keyvalue = key_value 332 | 333 | print("Extracting subkeys...") 334 | 335 | for key_id, subkey in primary_key.subkeys.items(): 336 | with subkey.unlock(password): 337 | assert subkey.is_unlocked 338 | if args.no_expired and subkey.is_expired: 339 | continue 340 | print(f'subkey id: {key_id}') 341 | print(f'subkey type: {get_key_type(subkey)}') 342 | print(f'subkey usage: {get_key_flags(subkey)}') 343 | if subkey.is_expired: 344 | print(f'{RED}/!\ subkey has expired !{END}') 345 | key_value, key_size = get_key_value(subkey) 346 | if args.display: 347 | print(f'{GREEN}subkey value:{END} {key_value}') 348 | print(f'subkey size: {key_size} bits') 349 | print() 350 | keygrip_by_id[key_id.encode('ascii')].keyvalue = key_value 351 | 352 | keyslots = get_key_slots() 353 | tocreate = [] 354 | found = [] 355 | noprivatekey = [] 356 | for k, v in keygrip_by_id.items(): 357 | keyexists = False 358 | keygrip = keygrip_by_id[k].okkeygrip 359 | 360 | for slot in keyslots: 361 | if slot.label == keygrip: 362 | keyexists = True 363 | print(f'Key id {k.decode("utf-8")} is already loaded in OnlyKey: {slot}') 364 | if keyexists is False and v.keyvalue is not None: 365 | tocreate.append(v) 366 | elif keyexists is False: 367 | noprivatekey.append(v) 368 | else: 369 | found.append(v) 370 | print("Keys without a private key:") 371 | print(noprivatekey) 372 | print("Keys already loaded:") 373 | print(found) 374 | if args.display is False: 375 | print("Keys to create:") 376 | print(tocreate) 377 | for i in tocreate: 378 | set_key_only_key(keyslots, i) 379 | time.sleep(2.0) 380 | keyslots = get_key_slots() 381 | print("Keyslots:") 382 | print(keyslots) 383 | 384 | except pgpy.errors.PGPDecryptionError: 385 | print("Wrong password") 386 | sys.exit(1) 387 | 388 | # primary_key is no longer unlocked 389 | if primary_key.is_protected is True: 390 | assert primary_key.is_unlocked is False 391 | 392 | 393 | def exit_handler(): 394 | only_key._hid.close() 395 | 396 | 397 | if __name__ == '__main__': 398 | try: 399 | atexit.register(exit_handler) 400 | Run() 401 | except EOFError: 402 | only_key._hid.close() 403 | print() 404 | print('Bye!') 405 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # onlykey-cli 2 | 3 | OnlyKey-cli - A command line interface to the OnlyKey (Similar functionality to [OnlyKey App](https://docs.crp.to/app.html)) that can be used for configuration, scripting, and testing. 4 | 5 | ## Installation 6 | 7 | ### Windows Stand-Alone EXE 8 | No install is required. Download and run the EXE to open OnlyKey CLI interactive mode or run directly from command line like this: 9 | ``` 10 | C:\ onlykey-cli.exe getlabels 11 | ``` 12 | 13 | [Download here](https://github.com/trustcrypto/python-onlykey/releases/download/v1.2.5/onlykey-cli.exe) 14 | 15 | ### Windows Install with dependencies 16 | 1) Python 3.8 and pip3 are required. To setup a Python environment on Windows we recommend Anaconda [https://www.anaconda.com/download/#windows](https://www.anaconda.com/download/#windows) 17 | 18 | 2) From an administrator command prompt run: 19 | ``` 20 | pip3 install hidapi==0.9.0 onlykey 21 | ``` 22 | 23 | You should see a message showing where the executable is installed. This is usually c:\python39\scripts\onlykey-cli.exe 24 | 25 | ### MacOS Install with dependencies 26 | Python 3.8 and pip3 are required. To setup a Python environment on MacOS we recommend Anaconda [https://www.anaconda.com/download/#macos](https://www.anaconda.com/download/#macos) 27 | ``` 28 | $ brew install libusb 29 | $ pip3 install onlykey 30 | ``` 31 | 32 | ### Linux/BSD Install with dependencies 33 | 34 | In order for non-root users in Linux to be able to communicate with OnlyKey a udev rule must be created as described [here](https://docs.crp.to/linux). 35 | 36 | #### Ubuntu Install with dependencies 37 | ``` 38 | $ sudo apt update && sudo apt upgrade 39 | $ sudo apt install python3-pip python3-tk libusb-1.0-0-dev libudev-dev 40 | $ pip3 install onlykey 41 | $ wget https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules 42 | $ sudo cp 49-onlykey.rules /etc/udev/rules.d/ 43 | $ sudo udevadm control --reload-rules && udevadm trigger 44 | ``` 45 | 46 | #### Debian Install with dependencies 47 | ``` 48 | $ sudo apt update && sudo apt upgrade 49 | $ sudo apt install python3-pip python3-tk libusb-1.0-0-dev libudev-dev 50 | $ pip3 install onlykey 51 | $ wget https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules 52 | $ sudo cp 49-onlykey.rules /etc/udev/rules.d/ 53 | $ sudo udevadm control --reload-rules && udevadm trigger 54 | ``` 55 | 56 | #### RedHat Install with dependencies 57 | ``` 58 | $ yum update 59 | $ yum install python3-pip python3-devel python3-tk libusb-devel libudev-devel \ 60 | gcc redhat-rpm-config 61 | $ pip3 install onlykey 62 | $ wget https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules 63 | $ sudo cp 49-onlykey.rules /etc/udev/rules.d/ 64 | $ sudo udevadm control --reload-rules && udevadm trigger 65 | ``` 66 | 67 | #### Fedora Install with dependencies 68 | ``` 69 | $ dnf install python3-pip python3-devel python3-tkinter libusb-devel libudev-devel \ 70 | gcc redhat-rpm-config 71 | $ pip3 install onlykey 72 | $ wget https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules 73 | $ sudo cp 49-onlykey.rules /etc/udev/rules.d/ 74 | $ sudo udevadm control --reload-rules && udevadm trigger 75 | ``` 76 | 77 | #### OpenSUSE Install with dependencies 78 | ``` 79 | $ zypper install python3-pip python3-devel python3-tk libusb-1_0-devel libudev-devel 80 | $ pip3 install onlykey 81 | $ wget https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules 82 | $ sudo cp 49-onlykey.rules /etc/udev/rules.d/ 83 | $ sudo udevadm control --reload-rules && udevadm trigger 84 | ``` 85 | 86 | #### Arch Linux Install with dependencies 87 | ``` 88 | $ sudo pacman -Syu git python3-setuptools python3 libusb python3-pip 89 | $ pip3 install onlykey 90 | $ wget https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules 91 | $ sudo cp 49-onlykey.rules /etc/udev/rules.d/ 92 | $ sudo udevadm control --reload-rules && udevadm trigger 93 | ``` 94 | 95 | #### FreeBSD Install with dependencies 96 | 97 | See forum thread [here](https://groups.google.com/d/msg/onlykey/CEYwdXjB508/MCe14p0gAwAJ) 98 | 99 | ## QuickStart 100 | 101 | Usage: onlykey-cli [OPTIONS] 102 | 103 | ### Setup Options 104 | 105 | #### init 106 | A command line tool for setting PIN on OnlyKey (Initial Configuration) 107 | 108 | ### General Options 109 | 110 | #### version 111 | Displays the version of the app 112 | 113 | #### fwversion 114 | Displays the version of the OnlyKey firmware 115 | 116 | #### wink 117 | OnlyKey flashes blue (winks), may be used for visual confirmation of connectivity 118 | 119 | #### getlabels 120 | Returns slot labels 121 | 122 | #### settime 123 | A command for setting time on OnlyKey, time is needed for TOTP (Google Authenticator) 124 | 125 | #### getkeylabels 126 | Returns key labels for RSA keys 1-4 and ECC keys 1-16 127 | 128 | #### rng [type] 129 | Access OnlyKey TRNG to generate random numbers: 130 | - [type] must be one of the following: 131 | - hexbytes - Output hex encoded random bytes. Default 8 bytes; Maximum 255 bytes. Specify number of bytes to return with --count i.e. 'onlykey-cli rng hexbytes --count 32' 132 | - feedkernel - Feed random bytes to /dev/random. 133 | 134 | ### OnlyKey Preferences Options 135 | 136 | #### idletimeout [num] 137 | OnlyKey locks after ideletimeout is reached (1 – 255 minutes; default = 30; 0 to disable). [More info](https://docs.crp.to/usersguide.html#configurable-inactivity-lockout-period) 138 | 139 | #### wipemode [num] 140 | Configure how the OnlyKey responds to 141 | a factory reset. WARNING - Setting to Full Wipe mode cannot be changed. 142 | 1 = Sensitive Data Only (default); 2 = Full Wipe (recommended for plausible deniability users) Entire device is wiped. Firmware must be reloaded. [More info](https://docs.crp.to/usersguide.html#configurable-wipe-mode) 143 | 144 | #### keylayout [num] 145 | Set keyboard layout 146 | - 1 - USA_ENGLISH (Default) 147 | - 2 - CANADIAN_FRENCH 148 | - 3 - CANADIAN_MULTILINGUAL 149 | - 4 - DANISH 150 | - 5 - FINNISH 151 | - 6 - FRENCH 152 | - 7 - FRENCH_BELGIAN 153 | - 8 - FRENCH_SWISS 154 | - 9 - GERMAN 155 | - 10 - GERMAN_MAC 156 | - 11 - GERMAN_SWISS 157 | - 12 - ICELANDIC 158 | - 13 - IRISH 159 | - 14 - ITALIAN 160 | - 15 - NORWEGIAN 161 | - 16 - PORTUGUESE 162 | - 17 - PORTUGUESE_BRAZILIAN 163 | - 18 - SPANISH 164 | - 19 - SPANISH_LATIN_AMERICA 165 | - 20 - SWEDISH 166 | - 21 - TURKISH 167 | - 22 - UNITED_KINGDOM 168 | - 23 - US_INTERNATIONAL 169 | - 24 - CZECH 170 | - 25 - SERBIAN_LATIN_ONLY 171 | - 26 - HUNGARIAN 172 | - 27 - DANISH MAC 173 | - 28 - US_DVORAK 174 | 175 | [More info](https://docs.crp.to/usersguide.html#configurable-keyboard-layouts) 176 | 177 | #### keytypespeed [num] 178 | 1 = slowest; 10 = fastest [7 = default] 179 | [More info](https://docs.crp.to/usersguide.html#configurable-keyboard-type-speed) 180 | 181 | #### ledbrightness [num] 182 | 1 = dimmest; 10 = brightest [8 = default] 183 | [More info](https://docs.crp.to/usersguide.html#configurable-led-brightness) 184 | 185 | #### touchsense [num] 186 | Change the OnlyKey's button touch sensitivity. 187 | WARNING: Setting button's touch sensitivity lower than 5 is not recommended as this could result in inadvertent button press. 188 | 2 = highest sensitivity; 100 = lowest sensitivity [12 = default] 189 | 190 | #### 2ndprofilemode [num] 191 | Set during init (Initial Configuration) to set 2nd profile type 1 = standard (default); 2 = plausible deniability 192 | 193 | #### storedkeymode [num] 194 | Enable or disable challenge for stored keys (SSH/PGP) 195 | 0 = Challenge Code Required (default); 1 = Button Press Required 196 | [More info](https://docs.crp.to/usersguide.html#stored-challenge-mode) 197 | 198 | #### derivedkeymode [num] 199 | Enable or disable challenge for stored keys (SSH/PGP) 200 | 0 = Challenge Code Required (default); 1 = Button Press Required 201 | [More info](https://docs.crp.to/usersguide.html#derived-challenge-mode) 202 | 203 | #### hmackeymode [num] 204 | Enable or disable button press for HMAC challenge-response 205 | 0 = Button Press Required (default); 1 = Button Press Not Required. 206 | [More info](https://docs.crp.to/usersguide.html#hmac-mode) 207 | 208 | #### backupkeymode [num] 209 | 1 = Lock backup key so this may not be changed on device 210 | WARNING - Once set to "Locked" this cannot be changed unless a factory reset occurs. 211 | [More info](https://docs.crp.to/usersguide.html#backup-key-mode) 212 | 213 | #### sysadminmode 214 | Enable or disable challenge for stored keys (SSH/PGP) 215 | 0 = Challenge Code Required (default); 1 = Button Press Required 216 | [More info](https://docs.crp.to/usersguide.html#derived-challenge-mode) 217 | 218 | #### lockbutton 219 | One of the buttons on OnlyKey can be configured as a lock button. 220 | 0 = Disable lockbutton; 1-6 = The selected button 221 | [More info](https://docs.crp.to/usersguide.html#configurable-lock-button) 222 | 223 | ### Slot Config Options 224 | 225 | #### setslot [id] [type] [value] 226 | - [id] must be slot number 1a - 6b 227 | - [type] must be one of the following: 228 | - label - set slots (1a - 6b) to have a descriptive label i.e. My Google Acct 229 | - url - URL to login page 230 | - delay1 - set a 0 - 9 second delay 231 | - addchar1 - Additional character before username 1 for TAB, 0 to clear 232 | - username - Username to login 233 | - addchar2 - Additional character after username 1 for TAB, 2 for RETURN 234 | - delay2 - set a 0 - 9 second delay 235 | - password - Password to login 236 | - addchar3 - Additional character after password 1 for TAB, 2 for RETURN 237 | - delay3 - set a 0 - 9 second delay 238 | - addchar4 - Additional character before OTP 1 for TAB 239 | - 2fa - type of two factor authentication 240 | - g - Google Authenticator 241 | - y - Yubico OTP 242 | - u - U2F 243 | - totpkey - Google Authenticator key 244 | - addchar5 - Additional character after OTP 2 for RETURN 245 | 246 | #### wipeslot [id] 247 | - [id] must be slot number 1a - 6b 248 | 249 | ### Key Config Options 250 | 251 | #### setkey [key slot] [type] [features] [hex key] 252 | Sets raw private keys and key labels, to set PEM format keys use the OnlyKey App 253 | - [key slot] must be key number RSA1 - RSA4, ECC1 - ECC16, HMAC1 - HMAC2 254 | - [type] must be one of the following: 255 | - label - set to have a descriptive key label i.e. My GPG signing key 256 | - x - X25519 Key Type (32 bytes) 257 | - n - NIST256P1 Key Type (32 bytes) 258 | - s - SECP256K1 Key Type (32 bytes) 259 | - 2 - RSA Key Type 2048bits (256 bytes) 260 | - 4 - RSA Key Type 4096bits (512 bytes) 261 | - h - HMAC Key Type (20 bytes) 262 | - [features] must be one of the following: 263 | - s - Use for signing 264 | - d - Use for decryption 265 | - b - Use for encryption/decryption of backups 266 | - For setting keys see examples [here](https://docs.crp.to/command-line.html#writing-private-keys-and-passwords). 267 | 268 | #### genkey [key slot] [type] [features] 269 | Generates random private key on device 270 | - [key slot] must be key number ECC1 - ECC16 (only ECC keys supported) 271 | - [type] must be one of the following: 272 | - x - X25519 Key Type (32 bytes) 273 | - n - NIST256P1 Key Type (32 bytes) 274 | - s - SECP256K1 Key Type (32 bytes) 275 | - [features] must be one of the following: 276 | - s - Use for signing 277 | - d - Use for decryption 278 | - b - Use for encryption/decryption of backups 279 | - For generating key see example [here](https://docs.crp.to/command-line.html#writing-private-keys-and-passwords). 280 | 281 | #### wipekey [key id] 282 | Erases key stored at [key id] 283 | - [key id] must be key number RSA1 - RSA4, ECC1 - ECC16, HMAC1 - HMAC2 284 | 285 | ### FIDO2 Config Options 286 | 287 | #### ping 288 | Sends a FIDO2 transaction to the device, which immediately echoes the same data back. This command is defined to be a uniform function for debugging, latency and performance measurements (CTAPHID_PING). 289 | 290 | #### set-pin 291 | Set new FIDO PIN, this is the PIN entered via keyboard and used for FIDO2 register/login (not the OnlyKey PIN entered on device). 292 | 293 | #### change-pin 294 | Change FIDO PIN, this is the PIN entered via keyboard and used for FIDO2 register/login (not the OnlyKey PIN entered on device, to change that PIN use the OnlyKey Desktop App). 295 | 296 | #### credential [operation] [credential ID] 297 | - [operation] must be one of the following: 298 | - info - Display number of existing resident keys and remaining space. 299 | - ls - List resident keys. 300 | - rm [credential ID] - Remove resident keys, [example here](https://docs.crp.to/command-line.html#list-and-remove-fido2-resident-key). 301 | 302 | #### reset 303 | Reset wipes all FIDO U2F and FIDO2 credentials!!! It is highly recommended to backup device prior to reset. 304 | 305 | ### Running Command Options 306 | 307 | You can run commands in two ways: 308 | 309 | #### 1) Directly in terminal 310 | 311 | Like this: 312 | 313 | ``` 314 | $ onlykey-cli getlabels 315 | 316 | Slot 1a: 317 | Slot 1b: 318 | 319 | Slot 2a: 320 | Slot 2b: 321 | 322 | Slot 3a: 323 | Slot 3b: 324 | 325 | Slot 4a: 326 | Slot 4b: 327 | 328 | Slot 5a: 329 | Slot 5b: 330 | 331 | Slot 6a: 332 | Slot 6b: 333 | 334 | $ onlykey-cli setslot 1a label ok 335 | Successfully set Label 336 | $ onlykey-cli getlabels 337 | 338 | Slot 1a: ok 339 | Slot 1b: 340 | 341 | Slot 2a: 342 | Slot 2b: 343 | 344 | Slot 3a: 345 | Slot 3b: 346 | 347 | Slot 4a: 348 | Slot 4b: 349 | 350 | Slot 5a: 351 | Slot 5b: 352 | 353 | Slot 6a: 354 | Slot 6b: 355 | 356 | ``` 357 | 358 | #### 2) Interactive Mode 359 | 360 | Or you can run commands in an interactive shell like this: 361 | 362 | ``` 363 | $ onlykey-cli 364 | OnlyKey CLI v1.2.10 365 | Press the right arrow to insert the suggestion. 366 | Press Control-C to retry. Control-D to exit. 367 | 368 | OnlyKey> getlabels 369 | 370 | Slot 1a: 371 | Slot 1b: 372 | 373 | Slot 2a: 374 | Slot 2b: 375 | 376 | Slot 3a: 377 | Slot 3b: 378 | 379 | Slot 4a: 380 | Slot 4b: 381 | 382 | Slot 5a: 383 | Slot 5b: 384 | 385 | Slot 6a: 386 | Slot 6b: 387 | 388 | OnlyKey> setslot 1a label ok 389 | 390 | Successfully set Label 391 | 392 | OnlyKey> getlabels 393 | 394 | Slot 1a: ok 395 | Slot 1b: 396 | 397 | Slot 2a: 398 | Slot 2b: 399 | 400 | Slot 3a: 401 | Slot 3b: 402 | 403 | Slot 4a: 404 | Slot 4b: 405 | 406 | Slot 5a: 407 | Slot 5b: 408 | 409 | Slot 6a: 410 | Slot 6b: 411 | 412 | OnlyKey> setslot 1a url accounts.google.com 413 | 414 | Successfully set URL 415 | 416 | OnlyKey> setslot 1a addchar1 2 417 | 418 | Successfully set Character1 419 | 420 | OnlyKey> setslot 1a delay1 2 421 | 422 | Successfully set Delay1 423 | 424 | OnlyKey> setslot 1a username onlykey.1234 425 | 426 | Successfully set Username 427 | 428 | OnlyKey> setslot 1a addchar2 2 429 | 430 | Successfully set Character2 431 | 432 | OnlyKey> setslot 1a delay2 2 433 | 434 | Successfully set Delay2 435 | 436 | OnlyKey> setslot 1a password 437 | 438 | Type Control-T to toggle password visible. 439 | Password: ********* 440 | Successfully set Password 441 | 442 | OnlyKey> setslot 1a addchar3 2 443 | 444 | Successfully set Character3 445 | 446 | OnlyKey> setslot 1a delay3 2 447 | 448 | Successfully set Delay3 449 | 450 | OnlyKey> setslot 1a 2fa g 451 | 452 | Successfully set 2FA Type 453 | 454 | OnlyKey> setslot 1a totpkey 455 | 456 | Type Control-T to toggle password visible. 457 | Password: ******************************** 458 | Successfully set TOTP Key 459 | 460 | OnlyKey> setslot 1a addchar4 2 461 | 462 | Successfully set Character4 463 | 464 | OnlyKey> 465 | 466 | Bye! 467 | ``` 468 | 469 | ## Examples 470 | 471 | ### Writing Private Keys and Passwords 472 | 473 | Keys/passwords are masked when entered and should only be set from interactive mode and not directly from terminal. Entering directly from terminal is not secure as command history is stored. 474 | 475 | 476 | **Setkey Examples** 477 | 478 | To set key a device must first be put into config mode. 479 | 480 | 481 | **Set HMAC key 1 to a custom value** 482 | 483 | $ onlykey-cli 484 | 485 | OnlyKey> setkey HMAC1 h 486 | 487 | Type Control-T to toggle password visible. 488 | Password/Key: **************************************** 489 | 490 | Successfully set ECC Key 491 | 492 | *HMAC key must be 20 bytes, h is HMAC type* 493 | 494 | 495 | **Set HMAC key 2 to a custom value** 496 | 497 | $ onlykey-cli 498 | 499 | OnlyKey> setkey HMAC2 h 500 | 501 | Type Control-T to toggle password visible. 502 | Password/Key: **************************************** 503 | 504 | Successfully set ECC Key 505 | 506 | *HMAC key must be 20 bytes, h is HMAC type* 507 | 508 | 509 | **Set ECC key in slot ECC1 to a custom value (Slots ECC1-ECC16 are available for ECC keys. Supported ECC curves X25519(x), NIST256P1(n), SECP256K1(s))** 510 | 511 | $ onlykey-cli 512 | 513 | OnlyKey> setkey ECC1 x 514 | 515 | Type Control-T to toggle password visible. 516 | Password/Key: ************************************************************* 517 | 518 | Successfully set ECC Key 519 | 520 | *ECC key must be 32 bytes, x is X25519 type* 521 | 522 | **Genkey Examples** 523 | 524 | To set key a device must first be put into config mode. 525 | 526 | **Generate ECC key in slot ECC1 to a custom value (Slots ECC1-ECC16 are available for ECC keys. Supported ECC curves X25519(x), NIST256P1(n), SECP256K1(s))** 527 | 528 | $ onlykey-cli 529 | 530 | OnlyKey> genkey ECC1 x 531 | 532 | Successfully set ECC Key 533 | 534 | 535 | ### Scripting Example 536 | 537 | 538 | **Set time on OnlyKey (required for TOTP)** 539 | 540 | $ onlykey-cli settime 541 | 542 | This can be added to scripts such as the UDEV rule to automatically set time when device is inserted into USB port. See example [here](https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/49-onlykey.rules) 543 | 544 | 545 | **Scripted provisioning of an OnlyKey slots and keys can be done by creating a script that sets multiple values on OnlyKey** 546 | 547 | ### List and Remove FIDO2 Resident Key 548 | 549 | List current resident keys: 550 | 551 | ``` 552 | onlykey-cli credential ls 553 | ``` 554 | ![](https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/images/cli-cred-ls.png) 555 | 556 | Remove a resident key by credential ID 557 | 558 | ``` 559 | onlykey-cli credential rm eu7LPIjTNwIJt2Ws9LWJlXkiNKaueSEEGteZM2MT/lZtEuYo49V6deCiIRMb6EDC29XG13nBL60+Yx+6hxSUYS1uxX9+AA== 560 | ``` 561 | 562 | Once removed, list current resident keys to verify: 563 | 564 | ![](https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/master/images/cli-cred-ls2.png) 565 | 566 | ## Source 567 | 568 | [OnlyKey CLI on Github](https://github.com/trustcrypto/python-onlykey) 569 | -------------------------------------------------------------------------------- /onlykey/client.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import print_function 3 | from builtins import input 4 | from builtins import chr 5 | from builtins import range 6 | from builtins import object 7 | import logging 8 | import time 9 | import binascii 10 | import hashlib 11 | import os 12 | import codecs 13 | 14 | import hid 15 | from aenum import Enum 16 | from sys import platform 17 | 18 | log = logging.getLogger(__name__) 19 | 20 | DEVICE_IDS = [ 21 | (0x16C0, 0x0486), # OnlyKey 22 | (0x1d50, 0x60fc), # OnlyKey 23 | ] 24 | 25 | if os.name== 'nt': 26 | MAX_INPUT_REPORT_SIZE = 65 27 | MATX_OUTPUT_REPORT_SIZE = 65 28 | MESSAGE_HEADER = [0, 255, 255, 255, 255] 29 | else: 30 | MAX_INPUT_REPORT_SIZE = 64 31 | MATX_OUTPUT_REPORT_SIZE = 64 32 | MESSAGE_HEADER = [255, 255, 255, 255] 33 | 34 | MAX_FEATURE_REPORTS = 0 35 | MAX_LARGE_PAYLOAD_SIZE = 58 # 64 - <4 bytes header> - <1 byte message> - <1 byte size|0xFF if max> 36 | 37 | 38 | SLOTS_NAME= { 39 | 0: '0', 40 | 1: '1a', 41 | 2: '2a', 42 | 3: '3a', 43 | 4: '4a', 44 | 5: '5a', 45 | 6: '6a', 46 | 7: '1b', 47 | 8: '2b', 48 | 9: '3b', 49 | 10: '4b', 50 | 11: '5b', 51 | 12: '6b', 52 | 25: 'RSA Key 1', 53 | 26: 'RSA Key 2', 54 | 27: 'RSA Key 3', 55 | 28: 'RSA Key 4', 56 | 29: 'ECC Key 1', 57 | 30: 'ECC Key 2', 58 | 31: 'ECC Key 3', 59 | 32: 'ECC Key 4', 60 | 33: 'ECC Key 5', 61 | 34: 'ECC Key 6', 62 | 35: 'ECC Key 7', 63 | 36: 'ECC Key 8', 64 | 37: 'ECC Key 9', 65 | 38: 'ECC Key 10', 66 | 39: 'ECC Key 11', 67 | 40: 'ECC Key 12', 68 | 41: 'ECC Key 13', 69 | 42: 'ECC Key 14', 70 | 43: 'ECC Key 15', 71 | 44: 'ECC Key 16', 72 | 45: 'ECC Key 17', 73 | 46: 'ECC Key 18', 74 | 47: 'ECC Key 19', 75 | 48: 'ECC Key 20', 76 | 49: 'ECC Key 21', 77 | 50: 'ECC Key 22', 78 | 51: 'ECC Key 23', 79 | 52: 'ECC Key 24', 80 | 53: 'ECC Key 25', 81 | 54: 'ECC Key 26', 82 | 55: 'ECC Key 27', 83 | 56: 'ECC Key 28', 84 | 57: 'ECC Key 29', 85 | 58: 'ECC Key 30', 86 | 59: 'ECC Key 31', 87 | 60: 'ECC Key 32', 88 | } 89 | 90 | SLOTS_NAME_DUO= { 91 | 1: 'Green 1a', 92 | 2: 'Green 2a', 93 | 3: 'Green 3a', 94 | 4: 'Green 1b', 95 | 5: 'Green 2b', 96 | 6: 'Green 3b', 97 | 7: 'Blue 1a', 98 | 8: 'Blue 2a', 99 | 9: 'Blue 3a', 100 | 10: 'Blue 1b', 101 | 11: 'Blue 2b', 102 | 12: 'Blue 3b', 103 | 13: 'Yellow 1a', 104 | 14: 'Yellow 2a', 105 | 15: 'Yellow 3a', 106 | 16: 'Yellow 1b', 107 | 17: 'Yellow 2b', 108 | 18: 'Yellow 3b', 109 | 19: 'Purple 1a', 110 | 20: 'Purple 2a', 111 | 21: 'Purple 3a', 112 | 22: 'Purple 1b', 113 | 23: 'Purple 2b', 114 | 24: 'Purple 3b', 115 | 25: 'RSA Key 1', 116 | 26: 'RSA Key 2', 117 | 27: 'RSA Key 3', 118 | 28: 'RSA Key 4', 119 | 29: 'ECC Key 1', 120 | 30: 'ECC Key 2', 121 | 31: 'ECC Key 3', 122 | 32: 'ECC Key 4', 123 | 33: 'ECC Key 5', 124 | 34: 'ECC Key 6', 125 | 35: 'ECC Key 7', 126 | 36: 'ECC Key 8', 127 | 37: 'ECC Key 9', 128 | 38: 'ECC Key 10', 129 | 39: 'ECC Key 11', 130 | 40: 'ECC Key 12', 131 | 41: 'ECC Key 13', 132 | 42: 'ECC Key 14', 133 | 43: 'ECC Key 15', 134 | 44: 'ECC Key 16', 135 | 45: 'ECC Key 17', 136 | 46: 'ECC Key 18', 137 | 47: 'ECC Key 19', 138 | 48: 'ECC Key 20', 139 | 49: 'ECC Key 21', 140 | 50: 'ECC Key 22', 141 | 51: 'ECC Key 23', 142 | 52: 'ECC Key 24', 143 | 53: 'ECC Key 25', 144 | 54: 'ECC Key 26', 145 | 55: 'ECC Key 27', 146 | 56: 'ECC Key 28', 147 | 57: 'ECC Key 29', 148 | 58: 'ECC Key 30', 149 | 59: 'ECC Key 31', 150 | 60: 'ECC Key 32', 151 | } 152 | 153 | 154 | class Message(Enum): 155 | OKSETPIN = 225 # 0xE1 156 | OKSETSDPIN = 226 # 0xE2 157 | OKSETPDPIN = 227 # 0xE3 158 | OKSETTIME = 228 # 0xE4 159 | OKGETLABELS = 229 # 0xE5 160 | OKSETSLOT = 230 # 0xE6 161 | OKWIPESLOT = 231 # 0xE7 162 | OKSETU2FPRIV = 232 # 0xE8 163 | OKWIPEU2FPRIV = 233 # 0xE9 164 | OKSETU2FCERT = 234 # 0xEA 165 | OKWIPEU2FCERT = 235 # 0xEB 166 | OKGETPUBKEY = 236 167 | OKSIGN = 237 168 | OKWIPEPRIV = 238 169 | OKSETPRIV = 239 170 | OKDECRYPT = 240 171 | OKRESTORE = 241 172 | 173 | 174 | class MessageField(Enum): 175 | LABEL = 1 176 | URL = 15 177 | DELAY1 = 17 178 | NEXTKEY4 = 18 179 | USERNAME = 2 180 | NEXTKEY1 = 16 181 | NEXTKEY2 = 3 182 | DELAY2 = 4 183 | PASSWORD = 5 184 | NEXTKEY3 = 6 185 | DELAY3 = 7 186 | NEXTKEY5 = 19 187 | TFATYPE = 8 188 | TOTPKEY = 9 189 | YUBIAUTH = 10 190 | IDLETIMEOUT = 11 191 | WIPEMODE = 12 192 | KEYTYPESPEED = 13 193 | KEYLAYOUT = 14 194 | LEDBRIGHTNESS = 24 195 | LOCKBUTTON = 25 196 | HMACMODE = 26 197 | SYSADMINMODE = 27 198 | SECPROFILEMODE = 23 199 | PGPCHALENGEMODE = 22 200 | SSHCHALENGEMODE = 21 201 | BACKUPMODE = 20 202 | TOUCHSENSE = 28 203 | 204 | class KeyTypeEnum(Enum): 205 | ED22519 = 1 206 | P256 = 2 207 | SECP256K1 = 3 208 | 209 | class OnlyKeyUnavailableException(Exception): 210 | """Exception raised when the connection to the OnlyKey failed.""" 211 | pass 212 | 213 | 214 | class Slot(object): 215 | def __init__(self, num, label=''): 216 | self.number = num 217 | self.label = label 218 | self.name = SLOTS_NAME[num] 219 | 220 | def __repr__(self): 221 | return ''.format(self.name, self.label) 222 | 223 | def to_str(self): 224 | return 'Slot {}: {}'.format(self.name, self.label or '') 225 | 226 | class Slotduo(object): 227 | def __init__(self, num, label=''): 228 | self.number = num 229 | self.label = label 230 | self.name = SLOTS_NAME_DUO[num] 231 | 232 | def __repr__(self): 233 | return ''.format(self.name, self.label) 234 | 235 | def to_str(self): 236 | return 'Slot {}: {}'.format(self.name, self.label or '') 237 | 238 | class OnlyKey(object): 239 | def __init__(self, connect=True): 240 | 241 | if connect: 242 | tries = 5 243 | while tries > 0: 244 | try: 245 | self._connect() 246 | logging.debug('connected') 247 | return 248 | except Exception as E: 249 | e = E 250 | log.debug('connect failed, trying again in 1 second...') 251 | time.sleep(1.5) 252 | tries -= 1 253 | 254 | raise e 255 | 256 | def _connect(self): 257 | try: 258 | for d in hid.enumerate(0, 0): 259 | vendor_id = d['vendor_id'] 260 | product_id = d['product_id'] 261 | serial_number = d['serial_number'] 262 | interface_number = d['interface_number'] 263 | usage_page = d['usage_page'] 264 | self.path = d['path'] 265 | 266 | if (vendor_id, product_id) in DEVICE_IDS: 267 | if serial_number == '1000000000': 268 | if usage_page == 0xffab or interface_number == 2: 269 | self._hid = hid.device() 270 | self._hid.open_path(self.path) 271 | self._hid.set_nonblocking(True) 272 | else: 273 | if usage_page == 0xf1d0 or interface_number == 1: 274 | self._hid = hid.device() 275 | self._hid.open_path(self.path) 276 | self._hid.set_nonblocking(True) 277 | 278 | except: 279 | log.exception('failed to connect') 280 | raise OnlyKeyUnavailableException() 281 | 282 | def close(self): 283 | return self._hid.close() 284 | 285 | def initialized(self): 286 | return self.read_string() == 'INITIALIZED' 287 | 288 | def set_time(self, timestamp): 289 | # Hex format without leading 0x 290 | current_epoch_time = format(int(timestamp), 'x') 291 | # pad with zeros for even digits 292 | current_epoch_time = current_epoch_time.zfill(len(current_epoch_time) + len(current_epoch_time) % 2) 293 | payload = [int(current_epoch_time[i: i+2], 16) for i in range(0, len(current_epoch_time), 2)] 294 | self.send_message(msg=Message.OKSETTIME, payload=payload) 295 | 296 | def send_message(self, payload=None, msg=None, slot_id=None, message_field=None, from_ascii=False): 297 | """Send a message.""" 298 | logging.debug('preparing payload for writing') 299 | # Initialize an empty message with the header 300 | raw_bytes = bytearray(MESSAGE_HEADER) 301 | 302 | # Append the message type (must be `Message` enum value) 303 | if msg: 304 | logging.debug('msg=%s', msg.name) 305 | raw_bytes.append(msg.value) 306 | 307 | # Append the slot ID if needed 308 | if slot_id: 309 | logging.debug('slot_id=%s', slot_id) 310 | if slot_id == 99: 311 | slot_id = 0 312 | raw_bytes.append(slot_id) 313 | 314 | # Append the message field (must be a `MessageField` enum value) 315 | if message_field: 316 | logging.debug('slot_field=%s', message_field.name) 317 | raw_bytes.append(message_field.value) 318 | 319 | # Append the raw payload, expect a string or a list of int 320 | if payload: 321 | if isinstance(payload, (str, str)): 322 | logging.debug('payload="%s"', payload) 323 | if from_ascii==True: 324 | raw_bytes.extend(str.encode(payload)) 325 | else: 326 | raw_bytes.extend(bytearray.fromhex(payload)) 327 | elif isinstance(payload, list) or isinstance(payload, bytearray): 328 | logging.debug('payload=%s', payload) 329 | raw_bytes.extend(payload) 330 | elif isinstance(payload, int): 331 | logging.debug('payload=%d', payload) 332 | raw_bytes.append(payload) 333 | else: 334 | raise Exception('`payload` must be either `str` or `list`') 335 | 336 | # Pad the ouput with 0s 337 | while len(raw_bytes) < MAX_INPUT_REPORT_SIZE: 338 | raw_bytes.append(0) 339 | 340 | # Send the message 341 | logging.debug('sending message ') 342 | self._hid.write(raw_bytes) 343 | 344 | def send_large_message(self, payload=None, msg=None, slot_id=chr(101)): 345 | """Wrapper for sending large message (larger than 58 bytes) in batch in a transparent way.""" 346 | if not msg: 347 | raise Exception("Missing msg") 348 | 349 | # Split the payload in multiple chunks 350 | chunks = [payload[x:x+MAX_LARGE_PAYLOAD_SIZE] for x in range(0, len(payload), 58)] 351 | for chunk in chunks: 352 | # print chunk 353 | # print [ord(c) for c in chunk] 354 | current_payload = [255] # 255 means that it's not the last payload 355 | # If it's less than the max size, set explicitely the size 356 | if len(chunk) < 58: 357 | current_payload = [len(chunk)] 358 | # Append the actual payload 359 | if isinstance(chunk, list): 360 | current_payload.extend(chunk) 361 | else: 362 | for c in chunk: 363 | current_payload.append(ord(c)) 364 | 365 | self.send_message(payload=current_payload, msg=msg) 366 | 367 | return 368 | 369 | 370 | def send_large_message2(self, payload=None, msg=None, slot_id=101): 371 | """Wrapper for sending large message (larger than 58 bytes) in batch in a transparent way.""" 372 | if not msg: 373 | raise Exception("Missing msg") 374 | 375 | # Split the payload in multiple chunks 376 | chunks = [payload[x:x+MAX_LARGE_PAYLOAD_SIZE-1] for x in range(0, len(payload), 57)] 377 | for chunk in chunks: 378 | # print chunk 379 | # print [ord(c) for c in chunk] 380 | current_payload = [slot_id, 255] # 255 means that it's not the last payload 381 | # If it's less than the max size, set explicitely the size 382 | if len(chunk) < 57: 383 | current_payload = [slot_id, len(chunk)] 384 | 385 | # Append the actual payload 386 | if isinstance(chunk, list): 387 | current_payload.extend(chunk) 388 | else: 389 | for c in chunk: 390 | current_payload.append(c) 391 | 392 | self.send_message(payload=current_payload, msg=msg) 393 | return 394 | 395 | def read_bytes(self, n=64, to_bytes=False, timeout_ms=100): 396 | """Read n bytes and return an array of uint8 (int).""" 397 | out = self._hid.read(n, timeout_ms=timeout_ms) 398 | logging.debug('read="%s"', ''.join([chr(c) for c in out])) 399 | outstr = bytearray(out) 400 | logging.debug('outstring="%s"', outstr) 401 | if outstr.decode(errors="ignore").find("UNINITIALIZED") != -1: 402 | raise RuntimeError('No PIN set, You must set a PIN first') 403 | elif outstr.decode(errors="ignore").find("INITIALIZED") != -1: 404 | raise RuntimeError('OnlyKey is locked, enter PIN to unlock') 405 | elif outstr.decode(errors="ignore").find("Error incorrect challenge was entered") != -1: 406 | raise RuntimeError('Error incorrect challenge was entered') 407 | elif outstr.decode(errors="ignore").find("No PIN set, You must set a PIN first") != -1: 408 | raise RuntimeError('Error OnlyKey must be configured first') 409 | elif outstr.decode(errors="ignore").find("Timeout occured while waiting for confirmation on OnlyKey") != -1: 410 | raise RuntimeError('Timeout occured while waiting for confirmation on OnlyKey') 411 | elif outstr.decode(errors="ignore").find("Error key not set as signature key") != -1: 412 | raise RuntimeError('Error key not set as signature key') 413 | elif outstr.decode(errors="ignore").find("Error key not set as decryption key") != -1: 414 | raise RuntimeError('Error key not set as decryption key') 415 | elif outstr.decode(errors="ignore").find("Error with RSA data to sign invalid size") != -1: 416 | raise RuntimeError('Error with RSA data to sign invalid size') 417 | elif outstr.decode(errors="ignore").find("Error with RSA signing") != -1: 418 | raise RuntimeError('Error with RSA signing') 419 | elif outstr.decode(errors="ignore").find("Error with RSA data to decrypt invalid size") != -1: 420 | raise RuntimeError('Error with RSA data to decrypt invalid size') 421 | elif outstr.decode(errors="ignore").find("Error with RSA decryption") != -1: 422 | raise RuntimeError('Error with RSA decryption') 423 | elif outstr.decode(errors="ignore").find("Error no key set in this slot") != -1: 424 | raise RuntimeError('Error no key set in this slot') 425 | 426 | if to_bytes: 427 | # Returns the bytes a string if requested 428 | return bytes(out) 429 | 430 | # Returns the raw list 431 | return out 432 | 433 | def read_string(self, timeout_ms=100): 434 | """Read an ASCII string.""" 435 | return ''.join([chr(item) for item in self.read_bytes(MAX_INPUT_REPORT_SIZE, timeout_ms=timeout_ms) if item != 0]) 436 | 437 | 438 | def read_chunk(self, timeout_ms=100): 439 | return self.read_bytes(MAX_INPUT_REPORT_SIZE, timeout_ms=timeout_ms) 440 | 441 | def getlabels(self): 442 | """Fetch the list of `Slot` from the OnlyKey. 443 | 444 | No need to read messages. 445 | """ 446 | self.send_message(msg=Message.OKGETLABELS) 447 | time.sleep(0.5) 448 | slots = [] 449 | for _ in range(12): 450 | data = self.read_string().split('|') 451 | slot_number = ord(data[0]) 452 | if slot_number >= 16: 453 | slot_number = slot_number - 6 454 | if 1 <= slot_number <= 12: 455 | slots.append(Slot(slot_number, label=data[1])) 456 | return slots 457 | 458 | def getduolabels(self): 459 | """Fetch the list of `Slot` from the OnlyKey. 460 | 461 | No need to read messages. 462 | """ 463 | self.send_message(msg=Message.OKGETLABELS) 464 | time.sleep(0.5) 465 | slots = [] 466 | for _ in range(24): 467 | data = self.read_string().split('|') 468 | slot_number = ord(data[0]) 469 | if slot_number >= 16: 470 | slot_number = slot_number - 6 471 | if 1 <= slot_number <= 24: 472 | slots.append(Slotduo(slot_number, label=data[1])) 473 | return slots 474 | 475 | def getkeylabels(self): 476 | """Fetch the list of `Keys` from the OnlyKey. 477 | 478 | No need to read messages. 479 | """ 480 | self.send_message(msg=Message.OKGETLABELS, slot_id=107) 481 | time.sleep(0) 482 | slots = [] 483 | for _ in range(20): 484 | data = self.read_string().split('|') 485 | slot_number = ord(data[0]) 486 | if 25 <= slot_number <= 44: 487 | slots.append(Slot(slot_number, label=data[1])) 488 | 489 | return slots 490 | 491 | def displaykeylabels(self): 492 | global slot 493 | time.sleep(2) 494 | 495 | self.read_string(timeout_ms=100) 496 | empty = 'a' 497 | while not empty: 498 | empty = self.read_string(timeout_ms=100) 499 | 500 | time.sleep(1) 501 | print('You should see your OnlyKey blink 3 times') 502 | print() 503 | 504 | tmp = {} 505 | for slot in self.getkeylabels(): 506 | tmp[slot.name] = slot 507 | slots = iter(['RSA Key 1', 'RSA Key 2', 'RSA Key 3', 'RSA Key 4', 'ECC Key 1', 'ECC Key 2', 'ECC Key 3', 'ECC Key 4', 'ECC Key 5', 'ECC Key 6', 'ECC Key 7', 'ECC Key 8', 'ECC Key 9', 'ECC Key 10', 'ECC Key 11', 'ECC Key 12', 'ECC Key 13', 'ECC Key 14', 'ECC Key 15', 'ECC Key 16', 'ECC Key 17', 'ECC Key 18', 'ECC Key 19', 'ECC Key 20', 'ECC Key 21', 'ECC Key 22', 'ECC Key 23', 'ECC Key 24', 'ECC Key 25', 'ECC Key 26', 'ECC Key 27', 'ECC Key 28', 'ECC Key 29']) 508 | for slot_name in slots: 509 | print(tmp[slot_name].to_str()) 510 | 511 | def setslot(self, slot_number, message_field, value): 512 | """Set a slot field to the given value.""" 513 | self.send_message(msg=Message.OKSETSLOT, slot_id=slot_number, message_field=message_field, payload=value, from_ascii=True) 514 | print(self.read_string()) 515 | 516 | def wipeslot(self, slot_number): 517 | """Wipe all the fields for the given slot.""" 518 | self.send_message(msg=Message.OKWIPESLOT, slot_id=slot_number) 519 | for _ in range(8): 520 | print(self.read_string()) 521 | 522 | def setkey(self, slot_number, key_type, key_features, value): 523 | # slot 131-132 Reserved 524 | # slot 129-130 HMAC Keys 525 | # slot 101-116 ECC Keys 526 | # slot 1-4 RSA Keys 527 | # set key type 528 | if key_type == 'x': 529 | key_type = '1' 530 | elif key_type == 'n': 531 | key_type = '2' 532 | elif key_type == 's': 533 | key_type = '3' 534 | elif key_type == 'h': 535 | key_type = '9' 536 | # set key features 537 | if key_features == 'd': 538 | key_type = int(key_type) + 32 # Decrypt flag 539 | elif key_features == 's': 540 | key_type = int(key_type) + 64 # Sign flag 541 | elif key_features == 'b': 542 | key_type = int(key_type) + 32 # Decrypt flag 543 | key_type = int(key_type) + 128 # Backup flag 544 | else: 545 | key_type = int(key_type) 546 | logging.debug('SETTING KEY IN SLOT:', slot_number) 547 | logging.debug('TO TYPE:', key_type) 548 | logging.debug('KEY:', value) 549 | if slot_number >= 1 and slot_number <= 4: 550 | if key_type & 0xf == 2: # RSA 2048 551 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[:114]) 552 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[114:228]) 553 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[228:342]) 554 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[342:456]) 555 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[456:512]) 556 | elif key_type & 0xf == 4: # RSA 4096 557 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[:114]) 558 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[114:228]) 559 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[228:342]) 560 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[342:456]) 561 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[456:570]) 562 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[570:684]) 563 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[684:798]) 564 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[798:912]) 565 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value[912:1024]) 566 | else: 567 | self.send_message(msg=Message.OKSETPRIV, slot_id=slot_number, payload=format(key_type, 'x')+value) 568 | time.sleep(1) 569 | print(self.read_string()) 570 | 571 | def wipekey(self, slot_number): 572 | logging.debug('WIPING KEY IN SLOT:', slot_number) 573 | self.send_message(msg=Message.OKWIPEPRIV, slot_id=slot_number, payload='00') 574 | time.sleep(1) 575 | print(self.read_string()) 576 | if slot_number > 100: 577 | self.send_message(msg=Message.OKSETSLOT, slot_id=slot_number-100+28, message_field=MessageField.LABEL, payload="", from_ascii=True) 578 | elif slot_number > 0: 579 | self.send_message(msg=Message.OKSETSLOT, slot_id=slot_number+24, message_field=MessageField.LABEL, payload="", from_ascii=True) 580 | time.sleep(1) 581 | print(self.read_string()) 582 | 583 | def slot(self, slot): 584 | global slotnum 585 | slotnum = slot 586 | 587 | def loadprivate(self, rootkey_ascii_armor, rootkey_passphrase): 588 | # This python script can parse the private keys out of OpenPGP keys (ed25519 or RSA). 589 | # Replace the passphrase with your OpenPGP passphrase. 590 | (rootkey, _) = pgpy.PGPKey.from_blob(rootkey_ascii_armor) 591 | 592 | # Todo load keys onto OnlyKey after parsed 593 | 594 | assert rootkey.is_protected 595 | assert rootkey.is_unlocked is False 596 | 597 | try: 598 | with rootkey.unlock(rootkey_passphrase): 599 | # rootkey is now unlocked 600 | assert rootkey.is_unlocked 601 | print('rootkey is now unlocked') 602 | print('rootkey type %s', rootkey._key._pkalg) 603 | if 'RSA' in rootkey._key._pkalg._name_: 604 | print('rootkey value:') 605 | #Parse rsa pgp key 606 | primary_keyp = long_to_bytes(rootkey._key.keymaterial.p) 607 | primary_keyq = long_to_bytes(rootkey._key.keymaterial.q) 608 | print(("".join(["%02x" % c for c in primary_keyp])) + ("".join(["%02x" % c for c in primary_keyq]))) 609 | print('rootkey size =', (len(primary_keyp)+len(primary_keyq))*8, 'bits') 610 | print('subkey values:') 611 | for subkey, value in rootkey._children.items(): 612 | print('subkey id', subkey) 613 | sub_keyp = long_to_bytes(value._key.keymaterial.p) 614 | sub_keyq = long_to_bytes(value._key.keymaterial.q) 615 | print('subkey value') 616 | print(("".join(["%02x" % c for c in sub_keyp])) + ("".join(["%02x" % c for c in sub_keyq]))) 617 | print('subkey size =', (len(primary_keyp)+len(primary_keyq))*8, 'bits') 618 | else: 619 | print('rootkey value:') 620 | #Parse ed25519 pgp key 621 | primary_key = long_to_bytes(rootkey._key.keymaterial.s) 622 | print("".join(["%02x" % c for c in primary_key])) 623 | print('subkey values:') 624 | for subkey, value in rootkey._children.items(): 625 | print('subkey id', subkey) 626 | sub_key = long_to_bytes(value._key.keymaterial.s) 627 | print('subkey value') 628 | print("".join(["%02x" % c for c in sub_key])) 629 | 630 | except: 631 | print('Unlocking failed') 632 | 633 | # rootkey is no longer unlocked 634 | assert rootkey.is_unlocked is False 635 | 636 | def encrypt(self, slot): 637 | print('Unavailable command') 638 | 639 | def decrypt(self, slot): 640 | print('Unavailable command') 641 | 642 | def sign(self, slot): 643 | print('Unavailable command') 644 | 645 | def verify(self, slot): 646 | print('Unavailable command') -------------------------------------------------------------------------------- /onlykey/cli.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import unicode_literals, print_function 3 | from __future__ import absolute_import 4 | 5 | from builtins import input 6 | from builtins import next 7 | from builtins import range 8 | import base64 9 | import binascii 10 | import time 11 | import logging 12 | import atexit 13 | import os 14 | import sys 15 | import solo 16 | import solo.operations 17 | from solo.cli.key import key 18 | from solo.cli.monitor import monitor 19 | from solo.cli.program import program 20 | 21 | from prompt_toolkit import prompt 22 | from prompt_toolkit import Application 23 | from prompt_toolkit.key_binding import KeyBindings 24 | from prompt_toolkit.keys import Keys 25 | from prompt_toolkit.filters import Condition 26 | import nacl.signing 27 | 28 | from .client import OnlyKey, Message, MessageField 29 | 30 | only_key = OnlyKey() 31 | 32 | def cli(): 33 | 34 | logging.basicConfig(level=logging.DEBUG) 35 | 36 | # Control-T handling 37 | hidden = [True] # Nonlocal 38 | key_bindings = KeyBindings() 39 | 40 | @key_bindings.add('c-t') 41 | def _(event): 42 | ' When Control-T has been pressed, toggle visibility. ' 43 | hidden[0] = not hidden[0] 44 | 45 | def prompt_pass(): 46 | print('Type Control-T to toggle password visible.') 47 | password = prompt('Password/Key: ', 48 | is_password=Condition(lambda: hidden[0]), 49 | key_bindings=key_bindings) 50 | return password 51 | 52 | def prompt_key(): 53 | print('Type Control-T to toggle key visible.') 54 | key = prompt('Key: ', 55 | is_password=Condition(lambda: hidden[0]), 56 | key_bindings=key_bindings) 57 | return key 58 | 59 | def prompt_pin(): 60 | print('Press any key when finished entering PIN') 61 | return 62 | 63 | if len(sys.argv) > 1: 64 | if sys.argv[1] == 'settime': 65 | only_key.set_time(time.time()) 66 | print(only_key.read_string()) 67 | elif sys.argv[1] == 'init': 68 | while 1: 69 | if only_key.read_string(timeout_ms=500) != 'UNINITIALIZED': 70 | break 71 | for msg in [Message.OKSETPIN]: 72 | only_key.send_message(msg=msg) 73 | print(only_key.read_string()) 74 | print () 75 | input('Press the Enter key once you are done') 76 | only_key.send_message(msg=msg) 77 | print(only_key.read_string()) 78 | only_key.send_message(msg=msg) 79 | print(only_key.read_string()) 80 | print () 81 | input('Press the Enter key once you are done') 82 | only_key.send_message(msg=msg) 83 | time.sleep(1.5) 84 | print(only_key.read_string()) 85 | print () 86 | for msg in [Message.OKSETPDPIN]: 87 | only_key.send_message(msg=msg) 88 | print(only_key.read_string() + ' for second profile') 89 | print () 90 | input('Press the Enter key once you are done') 91 | only_key.send_message(msg=msg) 92 | print(only_key.read_string() + ' for second profile') 93 | only_key.send_message(msg=msg) 94 | print(only_key.read_string()) 95 | print () 96 | input('Press the Enter key once you are done') 97 | only_key.send_message(msg=msg) 98 | time.sleep(1.5) 99 | print(only_key.read_string()) 100 | print () 101 | for msg in [Message.OKSETSDPIN]: 102 | only_key.send_message(msg=msg) 103 | print(only_key.read_string()) 104 | print () 105 | input('Press the Enter key once you are done') 106 | only_key.send_message(msg=msg) 107 | print(only_key.read_string()) 108 | only_key.send_message(msg=msg) 109 | print(only_key.read_string()) 110 | print () 111 | input('Press the Enter key once you are done') 112 | only_key.send_message(msg=msg) 113 | time.sleep(1.5) 114 | print(only_key.read_string()) 115 | print () 116 | elif sys.argv[1] == 'getlabels': 117 | tmp = {} 118 | only_key.set_time(time.time()) 119 | okversion = only_key.read_string() 120 | if okversion[19] == 'c': 121 | for slot in only_key.getlabels(): 122 | tmp[slot.name] = slot 123 | slots = iter(['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']) 124 | for slot_name in slots: 125 | print(tmp[slot_name].to_str().replace('ÿ'," ")) 126 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 127 | print() 128 | else: 129 | for slot in only_key.getduolabels(): 130 | tmp[slot.name] = slot 131 | slots = iter(['Green 1a', 'Green 2a', 'Green 3a', 'Green 1b', 'Green 2b', 'Green 3b', 'Blue 1a', 'Blue 2a', 'Blue 3a', 'Blue 1b', 'Blue 2b', 'Blue 3b', 'Yellow 1a', 'Yellow 2a', 'Yellow 3a', 'Yellow 1b', 'Yellow 2b', 'Yellow 3b', 'Purple 1a', 'Purple 2a', 'Purple 3a', 'Purple 1b', 'Purple 2b', 'Purple 3b']) 132 | for slot_name in slots: 133 | print(tmp[slot_name].to_str().replace('ÿ'," ")) 134 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 135 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 136 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 137 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 138 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 139 | print() 140 | elif sys.argv[1] == 'getkeylabels': 141 | tmp = {} 142 | for slot in only_key.getkeylabels(): 143 | tmp[slot.name] = slot 144 | slots = iter(['RSA Key 1', 'RSA Key 2', 'RSA Key 3', 'RSA Key 4', 'ECC Key 1', 'ECC Key 2', 'ECC Key 3', 'ECC Key 4', 'ECC Key 5', 'ECC Key 6', 'ECC Key 7', 'ECC Key 8', 'ECC Key 9', 'ECC Key 10', 'ECC Key 11', 'ECC Key 12', 'ECC Key 13', 'ECC Key 14', 'ECC Key 15', 'ECC Key 16']) 145 | for slot_name in slots: 146 | print(tmp[slot_name].to_str().replace('ÿ'," ")) 147 | elif sys.argv[1] == 'setslot': 148 | try: 149 | if sys.argv[2] == '1a': 150 | slot_id = 1 151 | elif sys.argv[2] == '2a': 152 | slot_id = 2 153 | elif sys.argv[2] == '3a': 154 | slot_id = 3 155 | elif sys.argv[2] == '4a': 156 | slot_id = 4 157 | elif sys.argv[2] == '5a': 158 | slot_id = 5 159 | elif sys.argv[2] == '6a': 160 | slot_id = 6 161 | elif sys.argv[2] == '1b': 162 | slot_id = 7 163 | elif sys.argv[2] == '2b': 164 | slot_id = 8 165 | elif sys.argv[2] == '3b': 166 | slot_id = 9 167 | elif sys.argv[2] == '4b': 168 | slot_id = 10 169 | elif sys.argv[2] == '5b': 170 | slot_id = 11 171 | elif sys.argv[2] == '6b': 172 | slot_id = 12 173 | elif sys.argv[2] == 'green1a': 174 | slot_id = 1 175 | elif sys.argv[2] == 'green2a': 176 | slot_id = 2 177 | elif sys.argv[2] == 'green3a': 178 | slot_id = 3 179 | elif sys.argv[2] == 'green1b': 180 | slot_id = 4 181 | elif sys.argv[2] == 'green2b': 182 | slot_id = 5 183 | elif sys.argv[2] == 'green3b': 184 | slot_id = 6 185 | elif sys.argv[2] == 'blue1a': 186 | slot_id = 7 187 | elif sys.argv[2] == 'blue2a': 188 | slot_id = 8 189 | elif sys.argv[2] == 'blue3a': 190 | slot_id = 9 191 | elif sys.argv[2] == 'blue1b': 192 | slot_id = 10 193 | elif sys.argv[2] == 'blue2b': 194 | slot_id = 11 195 | elif sys.argv[2] == 'blue3b': 196 | slot_id = 12 197 | elif sys.argv[2] == 'yellow1a': 198 | slot_id = 13 199 | elif sys.argv[2] == 'yellow2a': 200 | slot_id = 14 201 | elif sys.argv[2] == 'yellow3a': 202 | slot_id = 15 203 | elif sys.argv[2] == 'yellow1b': 204 | slot_id = 16 205 | elif sys.argv[2] == 'yellow2b': 206 | slot_id = 17 207 | elif sys.argv[2] == 'yellow3b': 208 | slot_id = 18 209 | elif sys.argv[2] == 'purple1a': 210 | slot_id = 19 211 | elif sys.argv[2] == 'purple2a': 212 | slot_id = 20 213 | elif sys.argv[2] == 'purple3a': 214 | slot_id = 21 215 | elif sys.argv[2] == 'purple1b': 216 | slot_id = 22 217 | elif sys.argv[2] == 'purple2b': 218 | slot_id = 23 219 | elif sys.argv[2] == 'purple3b': 220 | slot_id = 24 221 | else: 222 | slot_id = int(sys.argv[2]) 223 | except: 224 | print("setslot [id] [type] [value]") 225 | print("[id] must be a valid slot number") 226 | return 227 | 228 | if sys.argv[3] == 'label': 229 | only_key.setslot(slot_id, MessageField.LABEL, sys.argv[4]) 230 | elif sys.argv[3] == 'ecckeylabel': 231 | only_key.setslot(slot_id+28, MessageField.LABEL, sys.argv[4]) 232 | elif sys.argv[3] == 'rsakeylabel': 233 | only_key.setslot(slot_id+24, MessageField.LABEL, sys.argv[4]) 234 | elif sys.argv[3] == 'url': 235 | only_key.setslot(slot_id, MessageField.URL, sys.argv[4]) 236 | elif sys.argv[3] == 'addchar2': 237 | only_key.setslot(slot_id, MessageField.NEXTKEY1, sys.argv[4]) 238 | elif sys.argv[3] == 'delay1': 239 | only_key.setslot(slot_id, MessageField.DELAY1, sys.argv[4]) 240 | elif sys.argv[3] == 'username': 241 | only_key.setslot(slot_id, MessageField.USERNAME, sys.argv[4]) 242 | elif sys.argv[3] == 'addchar3': 243 | only_key.setslot(slot_id, MessageField.NEXTKEY2, sys.argv[4]) 244 | elif sys.argv[3] == 'delay2': 245 | only_key.setslot(slot_id, MessageField.DELAY2, sys.argv[4]) 246 | elif sys.argv[3] == 'password': 247 | password = prompt_pass() 248 | only_key.setslot(slot_id, MessageField.PASSWORD, password) 249 | elif sys.argv[3] == 'addchar5': 250 | only_key.setslot(slot_id, MessageField.NEXTKEY3, sys.argv[4]) 251 | elif sys.argv[3] == 'delay3': 252 | only_key.setslot(slot_id, MessageField.DELAY3, sys.argv[4]) 253 | elif sys.argv[3] == '2fa': 254 | only_key.setslot(slot_id, MessageField.TFATYPE, sys.argv[4]) 255 | elif sys.argv[3] == 'gkey': 256 | totpkey = prompt_key() 257 | totpkey = base64.b32decode("".join(totpkey.split()).upper()) 258 | totpkey = binascii.hexlify(totpkey) 259 | # pad with zeros for even digits 260 | totpkey = totpkey.zfill(len(totpkey) + len(totpkey) % 2) 261 | payload = [int(totpkey[i: i+2], 16) for i in range(0, len(totpkey), 2)] 262 | only_key.setslot(slot_id, MessageField.TOTPKEY, payload) 263 | elif sys.argv[3] == 'totpkey': 264 | totpkey = prompt_key() 265 | only_key.setslot(slot_id, MessageField.TOTPKEY, totpkey) 266 | elif sys.argv[3] == 'addchar1': 267 | only_key.setslot(slot_id, MessageField.NEXTKEY4, sys.argv[4]) 268 | elif sys.argv[3] == 'addchar4': 269 | only_key.setslot(slot_id, MessageField.NEXTKEY5, sys.argv[4]) 270 | elif sys.argv[3] == 'typespeed': 271 | only_key.setslot(slot_id, MessageField.KEYTYPESPEED, int(sys.argv[4])) 272 | else: 273 | print("setslot [id] [type] [value]") 274 | print("[type] must be ['label', 'ecckeylabel', 'rsakeylabel', 'url', 'addchar1', 'delay1', 'username', 'addchar2', 'delay2', 'password', 'addchar3', 'delay3', '2fa', 'totpkey', 'addchar4', 'addchar5', 'typespeed']") 275 | return 276 | elif sys.argv[1] == 'wipeslot': 277 | try: 278 | if sys.argv[2] == '1a': 279 | slot_id = 1 280 | elif sys.argv[2] == '2a': 281 | slot_id = 2 282 | elif sys.argv[2] == '3a': 283 | slot_id = 3 284 | elif sys.argv[2] == '4a': 285 | slot_id = 4 286 | elif sys.argv[2] == '5a': 287 | slot_id = 5 288 | elif sys.argv[2] == '6a': 289 | slot_id = 6 290 | elif sys.argv[2] == '1b': 291 | slot_id = 7 292 | elif sys.argv[2] == '2b': 293 | slot_id = 8 294 | elif sys.argv[2] == '3b': 295 | slot_id = 9 296 | elif sys.argv[2] == '4b': 297 | slot_id = 10 298 | elif sys.argv[2] == '5b': 299 | slot_id = 11 300 | elif sys.argv[2] == '6b': 301 | slot_id = 12 302 | elif sys.argv[2] == 'green1a': 303 | slot_id = 1 304 | elif sys.argv[2] == 'green2a': 305 | slot_id = 2 306 | elif sys.argv[2] == 'green3a': 307 | slot_id = 3 308 | elif sys.argv[2] == 'green1b': 309 | slot_id = 4 310 | elif sys.argv[2] == 'green2b': 311 | slot_id = 5 312 | elif sys.argv[2] == 'green3b': 313 | slot_id = 6 314 | elif sys.argv[2] == 'blue1a': 315 | slot_id = 7 316 | elif sys.argv[2] == 'blue2a': 317 | slot_id = 8 318 | elif sys.argv[2] == 'blue3a': 319 | slot_id = 9 320 | elif sys.argv[2] == 'blue1b': 321 | slot_id = 10 322 | elif sys.argv[2] == 'blue2b': 323 | slot_id = 11 324 | elif sys.argv[2] == 'blue3b': 325 | slot_id = 12 326 | elif sys.argv[2] == 'yellow1a': 327 | slot_id = 13 328 | elif sys.argv[2] == 'yellow2a': 329 | slot_id = 14 330 | elif sys.argv[2] == 'yellow3a': 331 | slot_id = 15 332 | elif sys.argv[2] == 'yellow1b': 333 | slot_id = 16 334 | elif sys.argv[2] == 'yellow2b': 335 | slot_id = 17 336 | elif sys.argv[2] == 'yellow3b': 337 | slot_id = 18 338 | elif sys.argv[2] == 'purple1a': 339 | slot_id = 19 340 | elif sys.argv[2] == 'purple2a': 341 | slot_id = 20 342 | elif sys.argv[2] == 'purple3a': 343 | slot_id = 21 344 | elif sys.argv[2] == 'purple1b': 345 | slot_id = 22 346 | elif sys.argv[2] == 'purple2b': 347 | slot_id = 23 348 | elif sys.argv[2] == 'purple3b': 349 | slot_id = 24 350 | else: 351 | slot_id = int(sys.argv[2]) 352 | except: 353 | print("wipeslot [id]") 354 | print("[id] must be a valid slot number") 355 | return 356 | only_key.wipeslot(slot_id) 357 | elif sys.argv[1] == 'setkey' or sys.argv[1] == 'genkey': 358 | try: 359 | slot_id = 0 360 | if sys.argv[2] == 'RSA1': 361 | slot_id = 1 362 | elif sys.argv[2] == 'RSA2': 363 | slot_id = 2 364 | elif sys.argv[2] == 'RSA3': 365 | slot_id = 3 366 | elif sys.argv[2] == 'RSA4': 367 | slot_id = 4 368 | elif sys.argv[2] == 'ECC1': 369 | slot_id = 101 370 | elif sys.argv[2] == 'ECC2': 371 | slot_id = 102 372 | elif sys.argv[2] == 'ECC3': 373 | slot_id = 103 374 | elif sys.argv[2] == 'ECC4': 375 | slot_id = 104 376 | elif sys.argv[2] == 'ECC5': 377 | slot_id = 105 378 | elif sys.argv[2] == 'ECC6': 379 | slot_id = 106 380 | elif sys.argv[2] == 'ECC7': 381 | slot_id = 107 382 | elif sys.argv[2] == 'ECC8': 383 | slot_id = 108 384 | elif sys.argv[2] == 'ECC9': 385 | slot_id = 109 386 | elif sys.argv[2] == 'ECC10': 387 | slot_id = 110 388 | elif sys.argv[2] == 'ECC11': 389 | slot_id = 111 390 | elif sys.argv[2] == 'ECC12': 391 | slot_id = 112 392 | elif sys.argv[2] == 'ECC13': 393 | slot_id = 113 394 | elif sys.argv[2] == 'ECC14': 395 | slot_id = 114 396 | elif sys.argv[2] == 'ECC15': 397 | slot_id = 115 398 | elif sys.argv[2] == 'ECC16': 399 | slot_id = 116 400 | elif sys.argv[2] == 'HMAC1': 401 | slot_id = 130 402 | elif sys.argv[2] == 'HMAC2': 403 | slot_id = 129 404 | if (sys.argv[1]=='genkey'): 405 | if (slot_id > 100 and (sys.argv[3] == 'x' or sys.argv[3] == 'n' or sys.argv[3] == 's')): 406 | only_key.setkey(slot_id, sys.argv[3], sys.argv[4], 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') 407 | else: 408 | print('Input error. See available commands with examples here https://docs.crp.to/command-line.html') 409 | elif (sys.argv[3]=='label'): 410 | if slot_id > 100: 411 | slot_id = slot_id - 72 412 | elif slot_id >= 1: 413 | slot_id = slot_id + 24 414 | only_key.setslot(slot_id, MessageField.LABEL, sys.argv[4]) 415 | else: 416 | only_key.setkey(slot_id, sys.argv[3], sys.argv[4], sys.argv[5]) 417 | except: 418 | print(sys.exc_info()[0]) 419 | print('Input error. See available commands with examples here https://docs.crp.to/command-line.html') 420 | return 421 | elif sys.argv[1] == 'wipekey': 422 | try: 423 | if sys.argv[2] == 'RSA1': 424 | slot_id = 1 425 | elif sys.argv[2] == 'RSA2': 426 | slot_id = 2 427 | elif sys.argv[2] == 'RSA3': 428 | slot_id = 3 429 | elif sys.argv[2] == 'RSA4': 430 | slot_id = 4 431 | elif sys.argv[2] == 'ECC1': 432 | slot_id = 101 433 | elif sys.argv[2] == 'ECC2': 434 | slot_id = 102 435 | elif sys.argv[2] == 'ECC3': 436 | slot_id = 103 437 | elif sys.argv[2] == 'ECC4': 438 | slot_id = 104 439 | elif sys.argv[2] == 'ECC5': 440 | slot_id = 105 441 | elif sys.argv[2] == 'ECC6': 442 | slot_id = 106 443 | elif sys.argv[2] == 'ECC7': 444 | slot_id = 107 445 | elif sys.argv[2] == 'ECC8': 446 | slot_id = 108 447 | elif sys.argv[2] == 'ECC9': 448 | slot_id = 109 449 | elif sys.argv[2] == 'ECC10': 450 | slot_id = 110 451 | elif sys.argv[2] == 'ECC11': 452 | slot_id = 111 453 | elif sys.argv[2] == 'ECC12': 454 | slot_id = 112 455 | elif sys.argv[2] == 'ECC13': 456 | slot_id = 113 457 | elif sys.argv[2] == 'ECC14': 458 | slot_id = 114 459 | elif sys.argv[2] == 'ECC15': 460 | slot_id = 115 461 | elif sys.argv[2] == 'ECC16': 462 | slot_id = 116 463 | elif sys.argv[2] == 'HMAC1': 464 | slot_id = 130 465 | elif sys.argv[2] == 'HMAC2': 466 | slot_id = 129 467 | except: 468 | print("wipekey [key id] [type]") 469 | print("[key id] must be a supported key number") 470 | return 471 | only_key.wipekey(slot_id) 472 | elif sys.argv[1] == 'idletimeout': 473 | only_key.setslot(1, MessageField.IDLETIMEOUT, int(sys.argv[2])) 474 | elif sys.argv[1] == 'wipemode': 475 | only_key.setslot(1, MessageField.WIPEMODE, int(sys.argv[2])) 476 | elif sys.argv[1] == 'keytypespeed': 477 | only_key.setslot(99, MessageField.KEYTYPESPEED, int(sys.argv[2])) 478 | elif sys.argv[1] == 'ledbrightness': 479 | only_key.setslot(1, MessageField.LEDBRIGHTNESS, int(sys.argv[2])) 480 | elif sys.argv[1] == 'touchsense': 481 | only_key.setslot(1, MessageField.TOUCHSENSE, int(sys.argv[2])) 482 | elif sys.argv[1] == '2ndprofilemode': 483 | only_key.setslot(1, MessageField.SECPROFILEMODE, int(sys.argv[2])) 484 | elif sys.argv[1] == 'storedkeymode': 485 | only_key.setslot(1, MessageField.PGPCHALENGEMODE, int(sys.argv[2])) 486 | elif sys.argv[1] == 'derivedkeymode': 487 | only_key.setslot(1, MessageField.SSHCHALENGEMODE, int(sys.argv[2])) 488 | elif sys.argv[1] == 'backupkeymode': 489 | only_key.setslot(1, MessageField.BACKUPMODE, int(sys.argv[2])) 490 | elif sys.argv[1] == 'keylayout': 491 | only_key.setslot(1, MessageField.KEYLAYOUT, int(sys.argv[2])) 492 | elif sys.argv[1] == 'sysadminmode': 493 | only_key.setslot(1, MessageField.SYSADMINMODE, int(sys.argv[2])) 494 | elif sys.argv[1] == 'lockbutton': 495 | only_key.setslot(1, MessageField.LOCKBUTTON, int(sys.argv[2])) 496 | elif sys.argv[1] == 'hmackeymode': 497 | only_key.setslot(1, MessageField.HMACMODE, int(sys.argv[2])) 498 | elif sys.argv[1] == 'version': 499 | print('OnlyKey CLI v1.2.10') 500 | elif sys.argv[1] == 'fwversion': 501 | only_key.set_time(time.time()) 502 | okversion = only_key.read_string() 503 | print(okversion[8:]) 504 | elif sys.argv[1] == 'change-pin': 505 | if len(sys.argv) > 2: 506 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 507 | return 508 | solo.cli.key() 509 | elif sys.argv[1] == 'credential': 510 | if len(sys.argv) > 4 or len(sys.argv) < 3: 511 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 512 | return 513 | if sys.argv[2] == 'info' or sys.argv[2] == 'ls' or sys.argv[2] == 'rm': 514 | if len(sys.argv) == 4 and sys.argv[2] != 'rm': 515 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 516 | return 517 | if len(sys.argv) == 4 and sys.argv[3] == '--help': 518 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 519 | return 520 | solo.cli.key() 521 | else: 522 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 523 | elif sys.argv[1] == 'ping': 524 | if len(sys.argv) > 2: 525 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 526 | return 527 | solo.cli.key() 528 | elif sys.argv[1] == 'reset': 529 | if len(sys.argv) > 2: 530 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 531 | return 532 | solo.cli.key() 533 | elif sys.argv[1] == 'rng': 534 | if len(sys.argv) > 5 or len(sys.argv) < 3 or len(sys.argv) == 4: 535 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 536 | return 537 | if len(sys.argv) > 2: 538 | if sys.argv[2] != 'hexbytes' and sys.argv[2] != 'feedkernel': 539 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 540 | return 541 | if len(sys.argv) > 4: 542 | if sys.argv[3] != '--count' or len(sys.argv) != 5: 543 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 544 | return 545 | if len(sys.argv) == 5 and sys.argv[4] == '--help': 546 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 547 | return 548 | solo.cli.key() 549 | elif sys.argv[1] == 'set-pin': 550 | if len(sys.argv) > 2: 551 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 552 | return 553 | solo.cli.key() 554 | elif sys.argv[1] == 'wink': 555 | if len(sys.argv) > 2: 556 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 557 | return 558 | solo.cli.key() 559 | elif sys.argv[1] == '--help': 560 | print('See available command options here https://docs.crp.to/command-line.html') 561 | return 562 | elif sys.argv[1] == '-h': 563 | print('See available command options here https://docs.crp.to/command-line.html') 564 | return 565 | elif sys.argv[1] == 'help': 566 | print('See available command options here https://docs.crp.to/command-line.html') 567 | return 568 | elif sys.argv[1]: 569 | print('Command not found. See available commands here https://docs.crp.to/command-line.html') 570 | print() 571 | 572 | 573 | else: 574 | 575 | # Print help. 576 | print('OnlyKey CLI v1.2.10') 577 | print('Control-D to exit.') 578 | print() 579 | 580 | def mprompt(): 581 | return prompt('OnlyKey> ') 582 | 583 | nexte = mprompt 584 | 585 | while 1: 586 | sys.argv = [sys.argv[0]] 587 | print() 588 | raw = nexte() 589 | print() 590 | data = raw.split() 591 | if not len(data): 592 | data.append('NULL') 593 | # nexte = prompt_pass 594 | if data[0] == "settime": 595 | only_key.set_time(time.time()) 596 | print(only_key.read_string()) 597 | elif data[0] == "init": 598 | while 1: 599 | if only_key.read_string(timeout_ms=500) != 'UNINITIALIZED': 600 | break 601 | for msg in [Message.OKSETPIN]: 602 | only_key.send_message(msg=msg) 603 | print(only_key.read_string()) 604 | print() 605 | input('Press the Enter key once you are done') 606 | only_key.send_message(msg=msg) 607 | print(only_key.read_string()) 608 | only_key.send_message(msg=msg) 609 | print(only_key.read_string()) 610 | print() 611 | input('Press the Enter key once you are done') 612 | only_key.send_message(msg=msg) 613 | time.sleep(1.5) 614 | print(only_key.read_string()) 615 | print() 616 | for msg in [Message.OKSETPDPIN]: 617 | only_key.send_message(msg=msg) 618 | print(only_key.read_string() + ' for second profile') 619 | print() 620 | input('Press the Enter key once you are done') 621 | only_key.send_message(msg=msg) 622 | print(only_key.read_string() + ' for second profile') 623 | only_key.send_message(msg=msg) 624 | print(only_key.read_string()) 625 | print () 626 | input('Press the Enter key once you are done') 627 | only_key.send_message(msg=msg) 628 | time.sleep(1.5) 629 | print(only_key.read_string()) 630 | print() 631 | for msg in [Message.OKSETSDPIN]: 632 | only_key.send_message(msg=msg) 633 | print(only_key.read_string()) 634 | print() 635 | input('Press the Enter key once you are done') 636 | only_key.send_message(msg=msg) 637 | print(only_key.read_string()) 638 | only_key.send_message(msg=msg) 639 | print(only_key.read_string()) 640 | print() 641 | input('Press the Enter key once you are done') 642 | only_key.send_message(msg=msg) 643 | time.sleep(1.5) 644 | print(only_key.read_string()) 645 | print() 646 | elif data[0] == 'getlabels': 647 | tmp = {} 648 | only_key.set_time(time.time()) 649 | okversion = only_key.read_string() 650 | if okversion[19] == 'c': 651 | for slot in only_key.getlabels(): 652 | tmp[slot.name] = slot 653 | slots = iter(['1a', '1b', '2a', '2b', '3a', '3b', '4a', '4b', '5a', '5b', '6a', '6b']) 654 | for slot_name in slots: 655 | print(tmp[slot_name].to_str().replace('ÿ'," ")) 656 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 657 | print() 658 | else: 659 | for slot in only_key.getduolabels(): 660 | tmp[slot.name] = slot 661 | slots = iter(['Green 1a', 'Green 2a', 'Green 3a', 'Green 1b', 'Green 2b', 'Green 3b', 'Blue 1a', 'Blue 2a', 'Blue 3a', 'Blue 1b', 'Blue 2b', 'Blue 3b', 'Yellow 1a', 'Yellow 2a', 'Yellow 3a', 'Yellow 1b', 'Yellow 2b', 'Yellow 3b', 'Purple 1a', 'Purple 2a', 'Purple 3a', 'Purple 1b', 'Purple 2b', 'Purple 3b']) 662 | for slot_name in slots: 663 | print(tmp[slot_name].to_str().replace('ÿ'," ")) 664 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 665 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 666 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 667 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 668 | print(tmp[next(slots)].to_str().replace('ÿ'," ")) 669 | print() 670 | elif data[0] == 'getkeylabels': 671 | tmp = {} 672 | for slot in only_key.getkeylabels(): 673 | tmp[slot.name] = slot 674 | slots = iter(['RSA Key 1', 'RSA Key 2', 'RSA Key 3', 'RSA Key 4', 'ECC Key 1', 'ECC Key 2', 'ECC Key 3', 'ECC Key 4', 'ECC Key 5', 'ECC Key 6', 'ECC Key 7', 'ECC Key 8', 'ECC Key 9', 'ECC Key 10', 'ECC Key 11', 'ECC Key 12', 'ECC Key 13', 'ECC Key 14', 'ECC Key 15', 'ECC Key 16']) 675 | for slot_name in slots: 676 | print(tmp[slot_name].to_str().replace('ÿ'," ")) 677 | elif data[0] == 'setslot': 678 | try: 679 | if data[1] == '1a': 680 | slot_id = 1 681 | elif data[1] == '2a': 682 | slot_id = 2 683 | elif data[1] == '3a': 684 | slot_id = 3 685 | elif data[1] == '4a': 686 | slot_id = 4 687 | elif data[1] == '5a': 688 | slot_id = 5 689 | elif data[1] == '6a': 690 | slot_id = 6 691 | elif data[1] == '1b': 692 | slot_id = 7 693 | elif data[1] == '2b': 694 | slot_id = 8 695 | elif data[1] == '3b': 696 | slot_id = 9 697 | elif data[1] == '4b': 698 | slot_id = 10 699 | elif data[1] == '5b': 700 | slot_id = 11 701 | elif data[1] == '6b': 702 | slot_id = 12 703 | elif data[1] == 'green1a': 704 | slot_id = 1 705 | elif data[1] == 'green2a': 706 | slot_id = 2 707 | elif data[1] == 'green3a': 708 | slot_id = 3 709 | elif data[1] == 'green1b': 710 | slot_id = 4 711 | elif data[1] == 'green2b': 712 | slot_id = 5 713 | elif data[1] == 'green3b': 714 | slot_id = 6 715 | elif data[1] == 'blue1a': 716 | slot_id = 7 717 | elif data[1] == 'blue2a': 718 | slot_id = 8 719 | elif data[1] == 'blue3a': 720 | slot_id = 9 721 | elif data[1] == 'blue1b': 722 | slot_id = 10 723 | elif data[1] == 'blue2b': 724 | slot_id = 11 725 | elif data[1] == 'blue3b': 726 | slot_id = 12 727 | elif data[1] == 'yellow1a': 728 | slot_id = 13 729 | elif data[1] == 'yellow2a': 730 | slot_id = 14 731 | elif data[1] == 'yellow3a': 732 | slot_id = 15 733 | elif data[1] == 'yellow1b': 734 | slot_id = 16 735 | elif data[1] == 'yellow2b': 736 | slot_id = 17 737 | elif data[1] == 'yellow3b': 738 | slot_id = 18 739 | elif data[1] == 'purple1a': 740 | slot_id = 19 741 | elif data[1] == 'purple2a': 742 | slot_id = 20 743 | elif data[1] == 'purple3a': 744 | slot_id = 21 745 | elif data[1] == 'purple1b': 746 | slot_id = 22 747 | elif data[1] == 'purple2b': 748 | slot_id = 23 749 | elif data[1] == 'purple3b': 750 | slot_id = 24 751 | else: 752 | slot_id = int(data[1]) 753 | except: 754 | print("setslot [id] [type] [value]") 755 | print("[id] must be a valid slot number") 756 | continue 757 | if data[2] == 'label': 758 | only_key.setslot(slot_id, MessageField.LABEL, data[3]) 759 | elif data[2] == 'ecckeylabel': 760 | only_key.setslot(slot_id+28, MessageField.LABEL, data[3]) 761 | elif data[2] == 'rsakeylabel': 762 | only_key.setslot(slot_id+24, MessageField.LABEL, data[3]) 763 | elif data[2] == 'url': 764 | only_key.setslot(slot_id, MessageField.URL, data[3]) 765 | elif data[2] == 'addchar2': 766 | only_key.setslot(slot_id, MessageField.NEXTKEY1, data[3]) 767 | elif data[2] == 'delay1': 768 | only_key.setslot(slot_id, MessageField.DELAY1, data[3]) 769 | elif data[2] == 'username': 770 | only_key.setslot(slot_id, MessageField.USERNAME, data[3]) 771 | elif data[2] == 'addchar3': 772 | only_key.setslot(slot_id, MessageField.NEXTKEY2, data[3]) 773 | elif data[2] == 'delay2': 774 | only_key.setslot(slot_id, MessageField.DELAY2, data[3]) 775 | elif data[2] == 'password': 776 | password = prompt_pass() 777 | only_key.setslot(slot_id, MessageField.PASSWORD, password) 778 | elif data[2] == 'addchar5': 779 | only_key.setslot(slot_id, MessageField.NEXTKEY3, data[3]) 780 | elif data[2] == 'delay3': 781 | only_key.setslot(slot_id, MessageField.DELAY3, data[3]) 782 | elif data[2] == '2fa': 783 | only_key.setslot(slot_id, MessageField.TFATYPE, data[3]) 784 | elif data[2] == 'gkey': 785 | totpkey = prompt_key() 786 | totpkey = base64.b32decode("".join(totpkey.split()).upper()) 787 | totpkey = binascii.hexlify(totpkey) 788 | # pad with zeros for even digits 789 | totpkey = totpkey.zfill(len(totpkey) + len(totpkey) % 2) 790 | payload = [int(totpkey[i: i+2], 16) for i in range(0, len(totpkey), 2)] 791 | only_key.setslot(slot_id, MessageField.TOTPKEY, payload) 792 | elif data[2] == 'totpkey': 793 | totpkey = prompt_key() 794 | only_key.setslot(slot_id, MessageField.TOTPKEY, totpkey) 795 | elif data[2] == 'addchar1': 796 | only_key.setslot(slot_id, MessageField.NEXTKEY3, data[3]) 797 | elif data[2] == 'addchar4': 798 | only_key.setslot(slot_id, MessageField.NEXTKEY3, data[3]) 799 | elif data[2] == 'typespeed': 800 | only_key.setslot(slot_id, MessageField.KEYTYPESPEED, int(data[3])) 801 | else: 802 | print("setslot [id] [type] [value]") 803 | print("[type] must be ['label', 'ecckeylabel', 'rsakeylabel', 'url', 'addchar1', 'delay1', 'username', 'addchar2', 'delay2', 'password', 'addchar3', 'delay3', '2fa', 'totpkey', 'addchar4', 'addchar5', 'typespeed']") 804 | continue 805 | elif data[0] == 'wipeslot': 806 | try: 807 | if data[1] == '1a': 808 | slot_id = 1 809 | elif data[1] == '2a': 810 | slot_id = 2 811 | elif data[1] == '3a': 812 | slot_id = 3 813 | elif data[1] == '4a': 814 | slot_id = 4 815 | elif data[1] == '5a': 816 | slot_id = 5 817 | elif data[1] == '6a': 818 | slot_id = 6 819 | elif data[1] == '1b': 820 | slot_id = 7 821 | elif data[1] == '2b': 822 | slot_id = 8 823 | elif data[1] == '3b': 824 | slot_id = 9 825 | elif data[1] == '4b': 826 | slot_id = 10 827 | elif data[1] == '5b': 828 | slot_id = 11 829 | elif data[1] == '6b': 830 | slot_id = 12 831 | elif data[1] == 'green1a': 832 | slot_id = 1 833 | elif data[1] == 'green2a': 834 | slot_id = 2 835 | elif data[1] == 'green3a': 836 | slot_id = 3 837 | elif data[1] == 'green1b': 838 | slot_id = 4 839 | elif data[1] == 'green2b': 840 | slot_id = 5 841 | elif data[1] == 'green3b': 842 | slot_id = 6 843 | elif data[1] == 'blue1a': 844 | slot_id = 7 845 | elif data[1] == 'blue2a': 846 | slot_id = 8 847 | elif data[1] == 'blue3a': 848 | slot_id = 9 849 | elif data[1] == 'blue1b': 850 | slot_id = 10 851 | elif data[1] == 'blue2b': 852 | slot_id = 11 853 | elif data[1] == 'blue3b': 854 | slot_id = 12 855 | elif data[1] == 'yellow1a': 856 | slot_id = 13 857 | elif data[1] == 'yellow2a': 858 | slot_id = 14 859 | elif data[1] == 'yellow3a': 860 | slot_id = 15 861 | elif data[1] == 'yellow1b': 862 | slot_id = 16 863 | elif data[1] == 'yellow2b': 864 | slot_id = 17 865 | elif data[1] == 'yellow3b': 866 | slot_id = 18 867 | elif data[1] == 'purple1a': 868 | slot_id = 19 869 | elif data[1] == 'purple2a': 870 | slot_id = 20 871 | elif data[1] == 'purple3a': 872 | slot_id = 21 873 | elif data[1] == 'purple1b': 874 | slot_id = 22 875 | elif data[1] == 'purple2b': 876 | slot_id = 23 877 | elif data[1] == 'purple3b': 878 | slot_id = 24 879 | else: 880 | slot_id = int(data[1]) 881 | except: 882 | print("wipeslot [id]") 883 | print("[id] must be a valid slot number") 884 | continue 885 | only_key.wipeslot(slot_id) 886 | elif data[0] == 'setkey' or data[0] == 'genkey': 887 | try: 888 | if data[1] == 'RSA1': 889 | slot_id = 1 890 | elif data[1] == 'RSA2': 891 | slot_id = 2 892 | elif data[1] == 'RSA3': 893 | slot_id = 3 894 | elif data[1] == 'RSA4': 895 | slot_id = 4 896 | elif data[1] == 'ECC1': 897 | slot_id = 101 898 | elif data[1] == 'ECC2': 899 | slot_id = 102 900 | elif data[1] == 'ECC3': 901 | slot_id = 103 902 | elif data[1] == 'ECC4': 903 | slot_id = 104 904 | elif data[1] == 'ECC5': 905 | slot_id = 105 906 | elif data[1] == 'ECC6': 907 | slot_id = 106 908 | elif data[1] == 'ECC7': 909 | slot_id = 107 910 | elif data[1] == 'ECC8': 911 | slot_id = 108 912 | elif data[1] == 'ECC9': 913 | slot_id = 109 914 | elif data[1] == 'ECC10': 915 | slot_id = 110 916 | elif data[1] == 'ECC11': 917 | slot_id = 111 918 | elif data[1] == 'ECC12': 919 | slot_id = 112 920 | elif data[1] == 'ECC13': 921 | slot_id = 113 922 | elif data[1] == 'ECC14': 923 | slot_id = 114 924 | elif data[1] == 'ECC15': 925 | slot_id = 115 926 | elif data[1] == 'ECC16': 927 | slot_id = 116 928 | elif data[1] == 'HMAC1': 929 | slot_id = 130 930 | elif data[1] == 'HMAC2': 931 | slot_id = 129 932 | except: 933 | print("setkey [key id] [type] [features]") 934 | print("[key id] must be a supported key number") 935 | continue 936 | try: 937 | if (data[0]=='genkey'): 938 | if (slot_id > 100 and (data[2] == 'x' or data[2] == 'n' or data[2] == 's')): 939 | only_key.setkey(slot_id, data[2], data[3], 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') 940 | else: 941 | print('Input error. See available commands with examples here https://docs.crp.to/command-line.html') 942 | elif (data[2]=='label'): 943 | if slot_id > 100: 944 | slot_id = slot_id - 72 945 | elif slot_id >= 1: 946 | slot_id = slot_id + 24 947 | only_key.setslot(slot_id, MessageField.LABEL, data[3]) 948 | else: 949 | key = prompt_pass() 950 | only_key.setkey(slot_id, data[2], data[3], key) 951 | except: 952 | print(sys.exc_info()[0]) 953 | print('Input error. See available commands with examples here https://docs.crp.to/command-line.html') 954 | continue 955 | elif data[0] == 'wipekey': 956 | try: 957 | if data[1] == 'RSA1': 958 | slot_id = 1 959 | elif data[1] == 'RSA2': 960 | slot_id = 2 961 | elif data[1] == 'RSA3': 962 | slot_id = 3 963 | elif data[1] == 'RSA4': 964 | slot_id = 4 965 | elif data[1] == 'ECC1': 966 | slot_id = 101 967 | elif data[1] == 'ECC2': 968 | slot_id = 102 969 | elif data[1] == 'ECC3': 970 | slot_id = 103 971 | elif data[1] == 'ECC4': 972 | slot_id = 104 973 | elif data[1] == 'ECC5': 974 | slot_id = 105 975 | elif data[1] == 'ECC6': 976 | slot_id = 106 977 | elif data[1] == 'ECC7': 978 | slot_id = 107 979 | elif data[1] == 'ECC8': 980 | slot_id = 108 981 | elif data[1] == 'ECC9': 982 | slot_id = 109 983 | elif data[1] == 'ECC10': 984 | slot_id = 110 985 | elif data[1] == 'ECC11': 986 | slot_id = 111 987 | elif data[1] == 'ECC12': 988 | slot_id = 112 989 | elif data[1] == 'ECC13': 990 | slot_id = 113 991 | elif data[1] == 'ECC14': 992 | slot_id = 114 993 | elif data[1] == 'ECC15': 994 | slot_id = 115 995 | elif data[1] == 'ECC16': 996 | slot_id = 116 997 | elif data[1] == 'HMAC1': 998 | slot_id = 130 999 | elif data[1] == 'HMAC2': 1000 | slot_id = 129 1001 | except: 1002 | print("wipekey [key id] [type]") 1003 | print("[key id] must be a supported key number") 1004 | continue 1005 | try: 1006 | only_key.wipekey(slot_id) 1007 | except: 1008 | continue 1009 | elif data[0] == 'idletimeout': 1010 | try: 1011 | only_key.setslot(1, MessageField.IDLETIMEOUT, int(data[1])) 1012 | except: 1013 | continue 1014 | elif data[0] == 'wipemode': 1015 | try: 1016 | only_key.setslot(1, MessageField.WIPEMODE, int(data[1])) 1017 | except: 1018 | continue 1019 | elif data[0] == 'keytypespeed': 1020 | try: 1021 | only_key.setslot(99, MessageField.KEYTYPESPEED, int(data[1])) 1022 | except: 1023 | continue 1024 | elif data[0] == 'ledbrightness': 1025 | try: 1026 | only_key.setslot(1, MessageField.LEDBRIGHTNESS, int(data[1])) 1027 | except: 1028 | continue 1029 | elif data[0] == 'touchsense': 1030 | try: 1031 | only_key.setslot(1, MessageField.TOUCHSENSE, int(data[1])) 1032 | except: 1033 | continue 1034 | elif data[0] == 'storedkeymode': 1035 | try: 1036 | only_key.setslot(1, MessageField.PGPCHALENGEMODE, int(data[1])) 1037 | except: 1038 | continue 1039 | elif data[0] == 'derivedkeymode': 1040 | try: 1041 | only_key.setslot(1, MessageField.SSHCHALENGEMODE, int(data[1])) 1042 | except: 1043 | continue 1044 | elif data[0] == 'backupkeymode': 1045 | try: 1046 | only_key.setslot(1, MessageField.BACKUPMODE, int(data[1])) 1047 | except: 1048 | continue 1049 | elif data[0] == '2ndprofilemode': 1050 | try: 1051 | only_key.setslot(1, MessageField.SECPROFILEMODE, int(data[1])) 1052 | except: 1053 | continue 1054 | elif data[0] == 'keylayout': 1055 | try: 1056 | only_key.setslot(1, MessageField.KEYLAYOUT, int(data[1])) 1057 | except: 1058 | continue 1059 | elif data[0] == 'sysadminmode': 1060 | try: 1061 | only_key.setslot(1, MessageField.SYSADMINMODE, int(data[1])) 1062 | except: 1063 | continue 1064 | elif data[0] == 'lockbutton': 1065 | try: 1066 | only_key.setslot(1, MessageField.LOCKBUTTON, int(data[1])) 1067 | except: 1068 | continue 1069 | elif data[0] == 'hmackeymode': 1070 | try: 1071 | only_key.setslot(1, MessageField.HMACMODE, int(data[1])) 1072 | except: 1073 | continue 1074 | elif data[0] == 'version': 1075 | try: 1076 | print('OnlyKey CLI v1.2.10') 1077 | except: 1078 | continue 1079 | elif data[0] == 'fwversion': 1080 | try: 1081 | only_key.set_time(time.time()) 1082 | okversion = only_key.read_string() 1083 | print(okversion[8:]) 1084 | except: 1085 | continue 1086 | elif data[0] == 'change-pin': 1087 | try: 1088 | sys.argv.append(data[0]) 1089 | if len(data) > 1: 1090 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 1091 | continue 1092 | solo.cli.key() 1093 | except: 1094 | continue 1095 | elif data[0] == 'credential': 1096 | try: 1097 | sys.argv.append(data[0]) 1098 | if len(data) > 3 or len(data) < 2: 1099 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 1100 | continue 1101 | if data[1] == 'info' or data[1] == 'ls' or data[1] == 'rm': 1102 | sys.argv.append(data[1]) 1103 | if len(data) == 3 and data[1] == 'rm' and data[2] != '--help': 1104 | sys.argv.append(data[2]) 1105 | solo.cli.key() 1106 | else: 1107 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 1108 | except: 1109 | continue 1110 | elif data[0] == 'ping': 1111 | try: 1112 | sys.argv.append(data[0]) 1113 | if len(data) > 1: 1114 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 1115 | continue 1116 | solo.cli.key() 1117 | except: 1118 | continue 1119 | elif data[0] == 'reset': 1120 | try: 1121 | sys.argv.append(data[0]) 1122 | if len(data) > 1: 1123 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 1124 | continue 1125 | solo.cli.key() 1126 | except: 1127 | continue 1128 | elif data[0] == 'rng': 1129 | try: 1130 | if len(data) > 4 or len(data) < 2: 1131 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 1132 | continue 1133 | sys.argv.append(data[0]) 1134 | sys.argv.append(data[1]) 1135 | if len(data) > 1: 1136 | if data[1] != 'hexbytes' and data[1] != 'feedkernel': 1137 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 1138 | continue 1139 | if len(data) > 2: 1140 | sys.argv.append(data[2]) 1141 | if data[2] != '--count': 1142 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 1143 | continue 1144 | if len(data) > 3: 1145 | sys.argv.append(data[3]) 1146 | solo.cli.key() 1147 | except: 1148 | continue 1149 | elif data[0] == 'set-pin': 1150 | try: 1151 | sys.argv.append(data[0]) 1152 | if len(data) > 1: 1153 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 1154 | continue 1155 | solo.cli.key() 1156 | except: 1157 | continue 1158 | elif data[0] == 'wink': 1159 | try: 1160 | sys.argv.append(data[0]) 1161 | if len(data) > 1: 1162 | print('Extra option not available. See available command options here https://docs.crp.to/command-line.html') 1163 | continue 1164 | solo.cli.key() 1165 | except: 1166 | continue 1167 | elif data[0] == '--help': 1168 | try: 1169 | print('See available command options here https://docs.crp.to/command-line.html') 1170 | except: 1171 | continue 1172 | elif data[0] == '-h': 1173 | try: 1174 | print('See available command options here https://docs.crp.to/command-line.html') 1175 | except: 1176 | continue 1177 | elif data[0] == 'help': 1178 | try: 1179 | print('See available command options here https://docs.crp.to/command-line.html') 1180 | except: 1181 | continue 1182 | elif data[0] == 'exit': 1183 | return 1184 | elif data[0] == 'quit': 1185 | return 1186 | elif data[0]: 1187 | try: 1188 | print('Option not found. See available command options here https://docs.crp.to/command-line.html') 1189 | continue 1190 | except: 1191 | continue 1192 | 1193 | def main(): 1194 | try: 1195 | atexit.register(exit_handler) 1196 | cli() 1197 | except EOFError: 1198 | only_key._hid.close() 1199 | print() 1200 | print('Bye!') 1201 | pass 1202 | 1203 | def exit_handler(): 1204 | only_key._hid.close() 1205 | --------------------------------------------------------------------------------