trustcrypto / OnlyKey-Firmware

The OnlyKey Firmware runs on the OnlyKey itself and provides the core functionality of OnlyKey.
https://docs.crp.to/firmware.html
212 stars 40 forks source link

OK_DECRYPT has "cooldown" period #74

Closed M-Pixel closed 5 years ago

M-Pixel commented 6 years ago

I built a small application using the API, and I've run into a bit of an issue with using OKDECRYPT.

I can send OKDECRYPT with slot ID (RSA 4) and a chunked 512 byte RSA4096-encrypted payload, and then successfully read the decrypted key. I stop reading as soon as 0s are encountered in a returned chunk.

The problem comes when I try to do anything after that. If I try to decrypt a second key too soon after, I get ERROR DEVICE LOCKED. At one point, I also had the code arranged so that the first thing that happened after the first key decryption was OKGETLABELS, and I observed that if this happens too soon after OKDECRYPT, the first label contains the tail end of the key that was previously decrypted. This leads me to believe that the OnlyKey is taking a long time to zero-out some buffers, and doesn't properly wait until this is done before processing new commands.

Is there a way for me to check whether or not the key is actually ready to receive the next command, or will resolving this issue require some modification to the firmware? The amount of time needed has been inconsistent - sometimes 10 seconds was enough, sometimes not. Currently, the most reliable way I've found is to wait 30 whole seconds. That's no good from a developer's maintainability perspective, nor from a user's usability perspective.

onlykey commented 6 years ago

@M-Pixel I am not sure if I am clear what you are describing here. The term key is usually used to describe a private or public key, here I am not sure what key means. I can provide some more information and examples on the API and if you want to share your code I will take a look.

The communication channel of the web api is described here - https://docs.crp.to/webcrypt.html#communication-channel-overview-advanced

If you are sending directly to the OnlyKey not via the web api communication is different. The supported message types are here - https://github.com/trustcrypto/libraries/blob/master/onlykey/okcore.h All USB packets are 64 bytes, so if you are sending chunked 512 bytes these are broken into 64 byte packets and sent to OnlyKey. They are reconstructed in the function void process_packets, if you say are sending RSA4096 encrypted data its going to be 512 bytes and if you send more or less an error will display. If you only send some of the data there is a wipedata() that is called every 5 seconds that clears out any incomplete processing of packets.

For development you will want to install the debug version of OnlyKey firmware, this displays useful messages like you can verify that the OnlyKey receives the data you expect it to receive. There is pre-compiled debug firmware here - https://github.com/trustcrypto/OnlyKey-Firmware/blob/master/OnlyKey_Beta/Debug%20Firmware/OnlyKey_Beta.cpp.hex

M-Pixel commented 6 years ago

Thanks for the advice, and offering to take a look. I hope that this discussion provides a good starting point for others who want to start developing apps on top of OnlyKey.

By "key" I mean "shared key" (the very thing that RSA is intended to encrypt and decrypt). Though I've also tested with "hello world" type strings and gotten the same behavior. In fact, "key" could mean "private key" here. It doesn't matter what it means, it's something that was encrypted that I am (successfully) decrypting.

I am using direct USB HID - I translated your Python library to C# nearly verbatim, and added features like key-label retrieval by reading the firmware source code (some documentation would be nice - ASCII 'k' to retrieve key labels is cute by the way, though a 0-based LabelType enum or SlotId would probably have been more appropriate and maintainable).

In code form, here's the problem I'm trying to describe:

Symptom 1

var helloWorld = onlyKey.DecryptString(SlotId.RsaKey4, helloWorldEncrypted, PromptForPinEntryCallback, NotifyPinEnteredCallback).TrimEnd();

Thread.Sleep(1000);

var labels = onlyKey.GetKeyLabels();
Console.WriteLine(labels[SlotId.RsaKey1]); // "   lo world" BAD BAD BAD!
Console.WriteLine(labels[SlotId.RsaKey2]); // ""

versus

var helloWorld = onlyKey.DecryptString(SlotId.RsaKey4, helloWorldEncrypted, PromptForPinEntryCallback, NotifyPinEnteredCallback).TrimEnd();

Thread.Sleep(30000); // this is the only part that's different

var labels = onlyKey.GetKeyLabels();
Console.WriteLine(labels[SlotId.RsaKey1]); // "" GOOD!
Console.WriteLine(labels[SlotId.RsaKey2]); // ""

Symptom 2

var helloWorld = onlyKey.DecryptString(SlotId.RsaKey4, helloWorldEncrypted, PromptForPinEntryCallback, NotifyPinEnteredCallback).TrimEnd();

Thread.Sleep(1000);

var fooBar = onlyKey.DecryptString(SlotId.RsaKey4, fooBarEncrypted, PromptForPinEntryCallback, NotifyPinEnteredCallback).TrimEnd();
Console.WriteLine(fooBar); // "ERROR DEVICE LOCKED"

versus

var helloWorld = onlyKey.DecryptString(SlotId.RsaKey4, helloWorldEncrypted, PromptForPinEntryCallback, NotifyPinEnteredCallback).TrimEnd();

Thread.Sleep(30000); // this is the only part that's different

var fooBar = onlyKey.DecryptString(SlotId.RsaKey4, fooBarEncrypted, PromptForPinEntryCallback, NotifyPinEnteredCallback).TrimEnd();
Console.WriteLine(fooBar); // "foo bar"

Function Definitions

Not necessary to read, but in case you're curious to see exactly what's going on behind the scenes.

public string DecryptString(SlotId slotId, byte[] encrypted, Action<int[]> displayChallengePin, Action onEnteredPin = null)
{
    var bytes = Decrypt(slotId, encrypted, displayChallengePin, onEnteredPin);

    // If there are 0s at the end, shorten length so that we don't try to translate 0 to an ASCII character
    var length = bytes.Length;
    while (length > 0 && bytes[length - 1] == 0)
        --length;

    return ASCII.GetString(bytes, 0, length);
}

public byte[] Decrypt(SlotId slotId, byte[] encrypted, Action<int[]> displayChallengePin, Action onEnteredPin = null)
{
    // OnlyKey API uses 25-28 for RSAKey IDs in some places, but here expects 1-4
    var slotAsByte = (byte) (slotId - (byte) SlotId.RsaKey1 - 1);
    SendCryptoMessage(message: Message.Decrypt, slotNumber: slotAsByte, payload: encrypted);

    // Calculate challenge pin using the same algorithm that the device's firmware uses
    var hashSlingingSlasher = SHA256.Create();
    var hash = hashSlingingSlasher.ComputeHash(encrypted);
    displayChallengePin(new[] {GetButton(hash[0]), GetButton(hash[15]), GetButton(hash[31])});

    // OnlyKey will send ENTER when PIN has been entered
    // TODO: Don't assume that this is a console app - connect to the OnlyKey's keyboard device instead, and listen for the enter character (or just disable timeout)
    while (Console.ReadKey(true).Key != ConsoleKey.Enter) {}
    onEnteredPin?.Invoke();
    Thread.Sleep(500); // Why 0.5 seconds? Copied from Python version

    // Retrieve decrypted content
    var decrypted = new byte[encrypted.Length];
    var decryptedPos = 0;
    var chunk = new byte[65];

    // TODO: Do we need to read the same number of bytes that were sent, even if there are entire tailing chunks of zeroes? Otherwise, modify firmware to send message length first.
    while (true)
    {
        _deviceCommunicationStream.Read(chunk);
        var messageLength = Array.IndexOf(array: chunk, value: (byte) 0, startIndex: 1) - 1;
        if (messageLength == -2) // 0 was not found, so -1 was returned, then we subtracted 1 more
            messageLength = 64; // if 0 wasn't found, then the entire buffer is used

        Array.Copy(sourceArray: chunk, sourceIndex: 1, destinationArray: decrypted, destinationIndex: decryptedPos, length: Math.Min(messageLength, decrypted.Length - decryptedPos));

        decryptedPos += messageLength; // move the buffer-head
        if (messageLength < 64 || decryptedPos == decrypted.Length)
            break;
    }

    return decrypted;
}

private void SendCryptoMessage(Message message, byte slotNumber, byte[] payload)
{
    var remainingPayloadLength = payload.Length;
    var payloadPos = 0;

    var chunk = new byte[MaxPayloadSize]; 
    while (remainingPayloadLength != 0)
    {
        var chunkSize = Math.Min(MaxPayloadChunkSize, remainingPayloadLength);
        remainingPayloadLength -= chunkSize;
        var lengthByte = (byte) (chunkSize == MaxPayloadChunkSize ? 255 : chunkSize);
        chunk[0] = lengthByte;
        Array.Copy(sourceArray: payload, sourceIndex: payloadPos, destinationArray: chunk, destinationIndex: 1, length: chunkSize);
        payloadPos += chunkSize;
        SendMessage(message: message, slotNumber: slotNumber, payload: chunk, payloadLength: chunkSize + 1);
    }
}

private void SendMessage(byte[] payload = null, int payloadLength = -1, Message message = Message.Null, byte slotNumber = 0, Field field = Field.Null, int payloadStart = 0)
{
    var buffer = new byte[MaxInputReportSize]; // 65 on Windows, 64 on other platforms
    var bufferPos = MessageHeader.Length;
    Array.Copy(sourceArray: MessageHeader, destinationArray: buffer, length: bufferPos);

    if (message != Message.Null)
        buffer[bufferPos++] = (byte) message;

    if (slotNumber != 0)
        buffer[bufferPos++] = slotNumber;

    if (field != Field.Null)
        buffer[bufferPos++] = (byte) field;

    if (payload != null)
    {
        if (payloadLength == -1)
            payloadLength = payload.Length - payloadStart;

        Array.Copy(sourceArray: payload, sourceIndex: payloadStart, destinationArray: buffer,
            destinationIndex: bufferPos, length: payloadLength);
    }
    _deviceCommunicationStream.Write(buffer);
}

public Dictionary<SlotId, string> GetKeyLabels()
{
    SendMessage(message: Message.GetLabels, slotNumber: 107 /* ASCII for 'k' as in "key" */);
    Thread.Sleep(2000); // TODO: Is this really necessary? Why 2 seconds? (copied from python version)
    var results = new Dictionary<SlotId, string>();
    for (int labelIndex = 0; labelIndex < 36; labelIndex++)
    {
        var data = _deviceCommunicationStream.Read();
        // [0, slot #, '|', label..., ' ', ' ', ' ', ' ', 0...]
        results.Add((SlotId) data[1], ASCII.GetString(data, 3, Array.IndexOf(data, (byte) 0, 3) + 1 - 4));
    }
    return results;
}

The fact that OnlyKey is sending "hello world", then mixing the latter half of it in with the labels, seems to me like a race condition. The fact that it's sending a device locked message when the device is not locked also seems like a bug. Both of these are things that I believe shouldn't be possible, even from incorrect API usage, from a developer usability and reliability standpoint. "After X, you need to wait 10 seconds before doing Y" is fragile. Ideally, it's more like "After X, until Z appears in the buffer, doing Y will result in an error code". So even if I can work around this by, say, doing some sort of flush operation, I think this uncovers a few weak spots in the firmware's behavior that ought to be patched up.

I'll try the debug version. Are there any instructions on how to build hex files from source?

M-Pixel commented 6 years ago

For anyone else who would like to know how to access messages from that debug firmware kindly posted above:

  1. Follow the Teensyduino installation instructions.
  2. Open the "Arduino" app
  3. Under "Tools/Board" select "Teensy 3.6" (maybe this isn't technically the correct version, but it worked for me)
  4. Under "Tools/Port", select your OnlyKey
  5. Press Ctrl + Shift + M, or select "Tools/Serial Monitor"
onlykey commented 6 years ago

@M-Pixel OK I think I know what you are seeing with the Symptom 1/2. RSA 4096 decryption is not fast for an embedded device like this, it takes about 8 seconds to complete. RSA 2048 is much quicker closer to 2 seconds. While the decryption is in progress the OnlyKey does not respond to anything. Any USB messages you send are stored in your computer's USB buffer and once the OnlyKey finishes the decryption (or an error occurs) OnlyKey then receives and processes the USB messages you sent. Also keep in mind that if OnlyKey sends a USB message to the computer but the app is not listening for a message but then later requests a response from OnlyKey the app might pick up the first message. Keep this in mind as this may explain some of the issues you are seeing.

If you want to send over a the C# app I will try to reproduce what you are seeing here.

For the key labels this feature is not currently being used by any of the apps but the idea was that when you set a key on OnlyKey you can set the Key ID and/or a descriptive label. Then the app can send a decrypt request with a Key ID or other descriptor instead of a slot, the OnlyKey will search and see if it has a key with that label. This way you can put any key in any slot.

See here for info on compiling firmware - https://github.com/trustcrypto/OnlyKey-Firmware/issues/59

One more thing to make sure of is correct PKCS encoding. OnlyKey uses this - https://tls.mbed.org/api/rsa_8h.html#a57b7b96e1dbb900bb42cc2e1704e051b And with the debug firmware you can see MBEDTLS error code (although I think it displays as decimal not hex) that will tell you if there is a PKCS encoding error.

M-Pixel commented 6 years ago

Thanks for the link on compiling firmware!

I may just switch to 2048 for the sake of user experience.

I did some debugging and have what I believe to be a sufficiently accurate diagnosis and solution, but I'll send you some test code this weekend regardless (maybe even post the lib to GitHub if time permits).

Scenario Playback

Code: onlyKey.Decrypt(...) // encrypted "password1234567890"

Serial output: (this sequence 9 times with different packet contents)

Received packet
FF FF FF FF F0 4 FF 11 95 5F 2D 95 E4 34 D4 AA E5 2C 58 D7 91 C8 0 C6 E6 49 EA 76 1 8D 25 29 9E 22 63 45 58 22 6B C3 A7 E4 D2 9B E5 D3 C5 FA 2D 75 CF AC E0 CA 6A B8 5C 32 E0 59 EC 8F 4D 46 

OKDECRYPT MESSAGE RECEIVED
Flashget RSA key from slot # 4
Type of RSA KEY is 4
Read RSA Private Key from Sector 0x32600 
CC 16 F3 8B FA A3 62 14 4B E8 9 A8 FB 6F 8 69 33 B7 7D E9 87 69 D3 5 86 A 62 91 DA A0 5A B6 A E9 64 F9 20 A5 23 A0 7 75 6E 5F 91 58 2B 11 A9 4 BA 60 E 88 0 59 98 65 67 CF FC 4A BC D2 C BA EA 80 7F 39 CF EC A5 4B 68 32 0 3E 8C 36 5 B3 A1 74 A6 3 99 C4 30 A7 91 BB 84 18 56 D3 20 9B B3 30 4B E8 E8 A0 36 93 C8 EB D1 E7 AC 7A 18 56 C 73 E9 D5 11 8E DE EB 3F 3D D8 80 9B B4 F3 8D C3 D6 19 EB 4A 20 1F 4C 1C 2 58 AF 6D 86 D0 CC 47 E1 FF AC E1 C7 CD 61 F1 7A 4A 98 8F C8 94 D1 1D 75 F7 5E E7 AA E8 BF D6 EB 5 DD FA 1F 5F 77 61 89 50 D4 2C 90 C C6 63 53 E8 20 9 6F C2 1F B5 BB E8 FC D8 D3 5C D4 22 A2 4 E2 50 4B B6 17 43 9 5C F9 31 B8 FE E4 8D BA 8C 4E 31 DA 2F 6 7B EC EA BF 56 4D 8D 27 8B AA A0 E6 15 65 CA 93 3E 31 D9 AF 99 B0 A1 7B 11 82 34 EC 0 17 D9 83 E4 85 92 43 12 B0 AF CF 58 65 37 5A 16 F1 5D 88 1D E8 20 55 10 BF 47 2F DC 6 40 C8 13 F6 A7 7F 70 E4 A2 93 DB CF E8 55 CC 35 B3 6F 59 67 2 6D 65 7F 2B 1E 6B 99 F3 D0 59 D5 4D 79 55 61 F 7E EB 4B 40 87 EE 50 C 22 CF 9C 3C 5A EB 90 FB FD 3 FD AB AC 1D D7 B1 E6 2C 8C 65 B9 6B D6 F8 72 99 8 6E 7E E7 34 2D 94 AE EA 69 EA D4 6E E7 3C CF 3A CF 80 B6 B5 68 5E E9 DC C1 3 F1 89 9 5D F8 57 15 C8 A6 8F F5 B2 CF 6E ED 2 A3 9 6C 1D 9A 5A 20 39 79 5B 2C E7 D5 91 56 F 5B E0 FF CD 76 52 32 61 6A 47 ED C6 6E CE 7 4 C6 51 D6 3B 39 AA 67 16 B9 A1 CD 2C D5 D8 4C A1 59 7 9F 6D 42 1F B A 86 AB 40 59 F2 D1 FF A6 DF 64 31 8 44 EA A3 2D F6 87 1D 95 2A 2B 8B 74 8C DD D0 42 91 52 CB 27 C2 94 9A D0 47 82 AB 33 C5 D1 E9 4D D9 D8 E7 20 AF 3C ED 31 E8 4F 86 93 DC F 
RSA generate public N:Storing RSA public 
AD 68 9A 5F 35 85 82 7F 2A 9A 9C 25 86 0 AA 6C B7 4F C3 B8 4E 5B 3E C0 75 F8 BF A C3 7B C4 8C 35 A9 71 61 23 6C E7 6F D8 90 74 46 27 C4 BD 73 0 80 98 7A B7 51 9F 30 23 D 49 38 D4 E 95 A3 CD E8 55 26 20 92 5E 1A 8 DE 65 7E 54 88 E 42 28 77 58 AB F9 ED 79 9 2E 79 95 FC 90 6B BF F0 D9 9C BB AD 14 6F 1F 39 A4 3D 83 67 94 4A EA FC 51 8A 8B 44 A0 AC 64 E4 25 9F 4 27 41 F4 25 C1 AC EA D6 CE C6 CD 67 CC D4 D3 C 9C 3A 9F BF C 67 D4 63 84 FF D7 1E C0 33 30 85 AC 6B 96 88 AF E0 4D 9B C1 6E 19 5B CC 16 12 61 97 E7 8E 42 70 61 88 5 17 D1 C3 DD 5A 71 CE 72 80 DA 88 67 54 7 3D 74 17 40 99 7C 8C 1A 52 69 73 A0 1 E8 C6 4 89 C0 AC 83 94 B6 B7 6B BE FF DA B8 37 94 CD E8 E6 42 73 17 FC F0 F4 5E 9E 0 28 51 9 39 7D 75 EA AB 63 3A 2F 5C 9 88 99 30 BE C7 BA A6 3B A2 42 95 E3 5C 8 8D 9A DE 7B 16 EB 20 A0 BA E3 7A 4A C0 3B EC 23 3F 15 3E 5E 31 B1 ED 7D E3 E 60 5F 64 64 46 C9 EC 9F 0 4B 18 7F C5 52 9 5A AD AA 7A B9 42 53 26 15 60 C8 DC 44 14 33 9F 77 5F 65 C8 72 27 7E 60 7B 48 C5 96 50 D3 EC 7E B7 FC 8D 96 75 B 97 9B B0 5F 8D BB A7 2D 8E 66 9A D9 4C 8D 19 A C 2E BD 2C 91 67 3C E9 52 70 A 77 E2 8 B7 8C BF 58 30 90 1C 73 71 73 9A 21 14 10 C6 D8 47 3F 70 14 B 91 BB 64 E3 DB 5D AD C9 A1 8A B9 2 41 2D 1E 13 CF 5D E3 BF E0 C0 21 14 8F 8B 98 F0 92 4D 77 17 ED 9A A F5 B4 9E 56 8E A3 A1 1A 7 AD C6 FD B0 9 8F E6 A6 47 B D5 9C E 54 71 A2 3C 2A 49 AB EA 8C 58 C9 65 99 AD 2C 95 81 17 99 11 73 8 E2 D5 18 D7 13 84 29 6B 17 B0 BF DC 90 98 2 8 B4 E6 F9 FB 1D EC 92 5C 3E C8 61 FE 92 1F 53 B3 BD F3 9 B6 C5 1 1C C5 59 

11 95 5F 2D 95 E4 34 D4 AA E5 2C 58 D7 91 C8 0 C6 E6 49 EA 76 1 8D 25 29 9E 22 63 45 58 22 6B C3 A7 E4 D2 9B E5 D3 C5 FA 2D 75 CF AC E0 CA 6A B8 5C 32 E0 59 EC 8F 4D 46 

Serial output:

11 95 5F 2D 95 E4 34 D4 AA E5 2C 58 D7 91 C8 0 C6 E6 49 EA 76 1 8D 25 29 9E 22 63 45 58 22 6B C3 A7 E4 D2 9B E5 D3 C5 FA 2D 75 CF AC E0 CA 6A B8 5C 32 E0 59 EC 8F 4D 46 EC AD 3E 9 4A 6F C9 FC 3A 3A CF 4C D 12 1D 3E 77 94 C0 54 FB 2 A1 66 BE 53 99 62 0 80 71 25 18 13 D5 51 C5 1 92 AB 34 FD C8 6F E3 31 FB 41 FD 1B AB 4A CF 23 94 16 97 20 D6 30 3E 74 43 22 2 29 15 89 1C 45 FB D0 8E DC CA FF 65 E7 FA 6C 7C 89 66 80 B6 8B BF A8 9D 30 93 F8 CF C7 6F 32 81 99 F5 39 DA BD D2 4 DA DB D1 BF 2B 16 34 2D 60 2A 4 44 6B D5 CF 3B 8B 13 A6 EC 30 FA 5F A 3C 95 AF D9 F3 7E 6C AE 2C 7E 34 82 D4 25 BE 7 15 64 F9 59 E B3 FB A C6 D9 6C 31 82 2A 36 1A E2 EE 6F D4 BE 2F 8 2E 16 33 1A 1C D C7 EC 7E CF 7C EB 34 BE 63 D1 57 32 31 80 A 1B 65 AD 9 12 37 27 B4 15 2E 79 3F E2 3F C6 3D 23 60 80 78 FC D5 3B 37 1D C1 5B 68 35 1D 6B 1 4F D0 C2 79 6B 1D DD EC EB 31 10 DB 2B 26 66 FA B9 B9 E6 D0 2D DD 5 D7 4E 42 63 19 C0 C6 5B F7 3B 94 BC EA FA A5 61 A8 31 E 1B CA EA C C2 80 36 EC FA F2 48 4C BD E2 57 B6 30 90 D5 F6 54 3C 9C 99 67 F0 CD FC AE 4D 8C BC C4 DE E8 10 52 5E 89 E5 9F 6F FF 2F 62 CC A0 15 D 46 C4 F4 B4 1F B BB E7 28 28 98 71 4E EA 6A 17 9D D5 9B 4E 93 6B 3A 89 F4 46 D5 E3 EC 39 31 A6 D1 94 CA 20 6D C7 C 27 8D 9C FD ED A4 5A BE 2F E7 7 4E DA 8A 6 D7 54 0 20 EF B2 E9 BB 51 68 37 2D BA 57 27 5 28 46 D9 3C CD F7 FF C0 D8 B3 15 D4 2B E 73 9D 33 65 AF CC 96 C2 C1 45 45 6B 8F 50 9B AE 7 29 8A 39 25 6D 17 AF 8 2F 8E A8 3A 4F 75 A6 55 9B 86 D3 A2 A6 55 45 73 46 7 EB 8B B3 6F 2A 75 7B 36 12 CE BA 5B B3 C6 F0 9B C3 
Received Message

11 95 5F 2D 95 E4 34 D4 AA E5 2C 58 D7 91 C8 0 C6 E6 49 EA 76 1 8D 25 29 9E 22 63 45 58 22 6B C3 A7 E4 D2 9B E5 D3 C5 FA 2D 75 CF AC E0 CA 6A B8 5C 32 E0 59 EC 8F 4D 46 EC AD 3E 9 4A 6F C9 FC 3A 3A CF 4C D 12 1D 3E 77 94 C0 54 FB 2 A1 66 BE 53 99 62 0 80 71 25 18 13 D5 51 C5 1 92 AB 34 FD C8 6F E3 31 FB 41 FD 1B AB 4A CF 23 94 16 97 20 D6 30 3E 74 43 22 2 29 15 89 1C 45 FB D0 8E DC CA FF 65 E7 FA 6C 7C 89 66 80 B6 8B BF A8 9D 30 93 F8 CF C7 6F 32 81 99 F5 39 DA BD D2 4 DA DB D1 BF 2B 16 34 2D 60 2A 4 44 6B D5 CF 3B 8B 13 A6 EC 30 FA 5F A 3C 95 AF D9 F3 7E 6C AE 2C 7E 34 82 D4 25 BE 7 15 64 F9 59 E B3 FB A C6 D9 6C 31 82 2A 36 1A E2 EE 6F D4 BE 2F 8 2E 16 33 1A 1C D C7 EC 7E CF 7C EB 34 BE 63 D1 57 32 31 80 A 1B 65 AD 9 12 37 27 B4 15 2E 79 3F E2 3F C6 3D 23 60 80 78 FC D5 3B 37 1D C1 5B 68 35 1D 6B 1 4F D0 C2 79 6B 1D DD EC EB 31 10 DB 2B 26 66 FA B9 B9 E6 D0 2D DD 5 D7 4E 42 63 19 C0 C6 5B F7 3B 94 BC EA FA A5 61 A8 31 E 1B CA EA C C2 80 36 EC FA F2 48 4C BD E2 57 B6 30 90 D5 F6 54 3C 9C 99 67 F0 CD FC AE 4D 8C BC C4 DE E8 10 52 5E 89 E5 9F 6F FF 2F 62 CC A0 15 D 46 C4 F4 B4 1F B BB E7 28 28 98 71 4E EA 6A 17 9D D5 9B 4E 93 6B 3A 89 F4 46 D5 E3 EC 39 31 A6 D1 94 CA 20 6D C7 C 27 8D 9C FD ED A4 5A BE 2F E7 7 4E DA 8A 6 D7 54 0 20 EF B2 E9 BB 51 68 37 2D BA 57 27 5 28 46 D9 3C CD F7 FF C0 D8 B3 15 D4 2B E 73 9D 33 65 AF CC 96 C2 C1 45 45 6B 8F 50 9B AE 7 29 8A 39 25 6D 17 AF 8 2F 8E A8 3A 4F 75 A6 55 9B 86 D3 A2 A6 55 45 73 46 7 EB 8B B3 6F 2A 75 7B 36 12 CE BA 5B B3 C6 F0 9B C3 
Enter challenge code 153

I press the three "buttons" in order

Serial output:

Challenge1 entered1
Challenge2 entered5
Challenge3 entered3

OKDECRYPT MESSAGE RECEIVED
Flashget RSA key from slot # 4
Type of RSA KEY is 4
Read RSA Private Key from Sector 0x32600 
CC 16 F3 8B FA A3 62 14 4B E8 9 A8 FB 6F 8 69 33 B7 7D E9 87 69 D3 5 86 A 62 91 DA A0 5A B6 A E9 64 F9 20 A5 23 A0 7 75 6E 5F 91 58 2B 11 A9 4 BA 60 E 88 0 59 98 65 67 CF FC 4A BC D2 C BA EA 80 7F 39 CF EC A5 4B 68 32 0 3E 8C 36 5 B3 A1 74 A6 3 99 C4 30 A7 91 BB 84 18 56 D3 20 9B B3 30 4B E8 E8 A0 36 93 C8 EB D1 E7 AC 7A 18 56 C 73 E9 D5 11 8E DE EB 3F 3D D8 80 9B B4 F3 8D C3 D6 19 EB 4A 20 1F 4C 1C 2 58 AF 6D 86 D0 CC 47 E1 FF AC E1 C7 CD 61 F1 7A 4A 98 8F C8 94 D1 1D 75 F7 5E E7 AA E8 BF D6 EB 5 DD FA 1F 5F 77 61 89 50 D4 2C 90 C C6 63 53 E8 20 9 6F C2 1F B5 BB E8 FC D8 D3 5C D4 22 A2 4 E2 50 4B B6 17 43 9 5C F9 31 B8 FE E4 8D BA 8C 4E 31 DA 2F 6 7B EC EA BF 56 4D 8D 27 8B AA A0 E6 15 65 CA 93 3E 31 D9 AF 99 B0 A1 7B 11 82 34 EC 0 17 D9 83 E4 85 92 43 12 B0 AF CF 58 65 37 5A 16 F1 5D 88 1D E8 20 55 10 BF 47 2F DC 6 40 C8 13 F6 A7 7F 70 E4 A2 93 DB CF E8 55 CC 35 B3 6F 59 67 2 6D 65 7F 2B 1E 6B 99 F3 D0 59 D5 4D 79 55 61 F 7E EB 4B 40 87 EE 50 C 22 CF 9C 3C 5A EB 90 FB FD 3 FD AB AC 1D D7 B1 E6 2C 8C 65 B9 6B D6 F8 72 99 8 6E 7E E7 34 2D 94 AE EA 69 EA D4 6E E7 3C CF 3A CF 80 B6 B5 68 5E E9 DC C1 3 F1 89 9 5D F8 57 15 C8 A6 8F F5 B2 CF 6E ED 2 A3 9 6C 1D 9A 5A 20 39 79 5B 2C E7 D5 91 56 F 5B E0 FF CD 76 52 32 61 6A 47 ED C6 6E CE 7 4 C6 51 D6 3B 39 AA 67 16 B9 A1 CD 2C D5 D8 4C A1 59 7 9F 6D 42 1F B A 86 AB 40 59 F2 D1 FF A6 DF 64 31 8 44 EA A3 2D F6 87 1D 95 2A 2B 8B 74 8C DD D0 42 91 52 CB 27 C2 94 9A D0 47 82 AB 33 C5 D1 E9 4D D9 D8 E7 20 AF 3C ED 31 E8 4F 86 93 DC F 
RSA generate public N:Storing RSA public 
AD 68 9A 5F 35 85 82 7F 2A 9A 9C 25 86 0 AA 6C B7 4F C3 B8 4E 5B 3E C0 75 F8 BF A C3 7B C4 8C 35 A9 71 61 23 6C E7 6F D8 90 74 46 27 C4 BD 73 0 80 98 7A B7 51 9F 30 23 D 49 38 D4 E 95 A3 CD E8 55 26 20 92 5E 1A 8 DE 65 7E 54 88 E 42 28 77 58 AB F9 ED 79 9 2E 79 95 FC 90 6B BF F0 D9 9C BB AD 14 6F 1F 39 A4 3D 83 67 94 4A EA FC 51 8A 8B 44 A0 AC 64 E4 25 9F 4 27 41 F4 25 C1 AC EA D6 CE C6 CD 67 CC D4 D3 C 9C 3A 9F BF C 67 D4 63 84 FF D7 1E C0 33 30 85 AC 6B 96 88 AF E0 4D 9B C1 6E 19 5B CC 16 12 61 97 E7 8E 42 70 61 88 5 17 D1 C3 DD 5A 71 CE 72 80 DA 88 67 54 7 3D 74 17 40 99 7C 8C 1A 52 69 73 A0 1 E8 C6 4 89 C0 AC 83 94 B6 B7 6B BE FF DA B8 37 94 CD E8 E6 42 73 17 FC F0 F4 5E 9E 0 28 51 9 39 7D 75 EA AB 63 3A 2F 5C 9 88 99 30 BE C7 BA A6 3B A2 42 95 E3 5C 8 8D 9A DE 7B 16 EB 20 A0 BA E3 7A 4A C0 3B EC 23 3F 15 3E 5E 31 B1 ED 7D E3 E 60 5F 64 64 46 C9 EC 9F 0 4B 18 7F C5 52 9 5A AD AA 7A B9 42 53 26 15 60 C8 DC 44 14 33 9F 77 5F 65 C8 72 27 7E 60 7B 48 C5 96 50 D3 EC 7E B7 FC 8D 96 75 B 97 9B B0 5F 8D BB A7 2D 8E 66 9A D9 4C 8D 19 A C 2E BD 2C 91 67 3C E9 52 70 A 77 E2 8 B7 8C BF 58 30 90 1C 73 71 73 9A 21 14 10 C6 D8 47 3F 70 14 B 91 BB 64 E3 DB 5D AD C9 A1 8A B9 2 41 2D 1E 13 CF 5D E3 BF E0 C0 21 14 8F 8B 98 F0 92 4D 77 17 ED 9A A F5 B4 9E 56 8E A3 A1 1A 7 AD C6 FD B0 9 8F E6 A6 47 B D5 9C E 54 71 A2 3C 2A 49 AB EA 8C 58 C9 65 99 AD 2C 95 81 17 99 11 73 8 E2 D5 18 D7 13 84 29 6B 17 B0 BF DC 90 98 2 8 B4 E6 F9 FB 1D EC 92 5C 3E C8 61 FE 92 1F 53 B3 BD F3 9 B6 C5 1 1C C5 59 

RSA ciphertext blob size=512

11 95 5F 2D 95 E4 34 D4 AA E5 2C 58 D7 91 C8 0 C6 E6 49 EA 76 1 8D 25 29 9E 22 63 45 58 22 6B C3 A7 E4 D2 9B E5 D3 C5 FA 2D 75 CF AC E0 CA 6A B8 5C 32 E0 59 EC 8F 4D 46 EC AD 3E 9 4A 6F C9 FC 3A 3A CF 4C D 12 1D 3E 77 94 C0 54 FB 2 A1 66 BE 53 99 62 0 80 71 25 18 13 D5 51 C5 1 92 AB 34 FD C8 6F E3 31 FB 41 FD 1B AB 4A CF 23 94 16 97 20 D6 30 3E 74 43 22 2 29 15 89 1C 45 FB D0 8E DC CA FF 65 E7 FA 6C 7C 89 66 80 B6 8B BF A8 9D 30 93 F8 CF C7 6F 32 81 99 F5 39 DA BD D2 4 DA DB D1 BF 2B 16 34 2D 60 2A 4 44 6B D5 CF 3B 8B 13 A6 EC 30 FA 5F A 3C 95 AF D9 F3 7E 6C AE 2C 7E 34 82 D4 25 BE 7 15 64 F9 59 E B3 FB A C6 D9 6C 31 82 2A 36 1A E2 EE 6F D4 BE 2F 8 2E 16 33 1A 1C D C7 EC 7E CF 7C EB 34 BE 63 D1 57 32 31 80 A 1B 65 AD 9 12 37 27 B4 15 2E 79 3F E2 3F C6 3D 23 60 80 78 FC D5 3B 37 1D C1 5B 68 35 1D 6B 1 4F D0 C2 79 6B 1D DD EC EB 31 10 DB 2B 26 66 FA B9 B9 E6 D0 2D DD 5 D7 4E 42 63 19 C0 C6 5B F7 3B 94 BC EA FA A5 61 A8 31 E 1B CA EA C C2 80 36 EC FA F2 48 4C BD E2 57 B6 30 90 D5 F6 54 3C 9C 99 67 F0 CD FC AE 4D 8C BC C4 DE E8 10 52 5E 89 E5 9F 6F FF 2F 62 CC A0 15 D 46 C4 F4 B4 1F B BB E7 28 28 98 71 4E EA 6A 17 9D D5 9B 4E 93 6B 3A 89 F4 46 D5 E3 EC 39 31 A6 D1 94 CA 20 6D C7 C 27 8D 9C FD ED A4 5A BE 2F E7 7 4E DA 8A 6 D7 54 0 20 EF B2 E9 BB 51 68 37 2D BA 57 27 5 28 46 D9 3C CD F7 FF C0 D8 B3 15 D4 2B E 73 9D 33 65 AF CC 96 C2 C1 45 45 6B 8F 50 9B AE 7 29 8A 39 25 6D 17 AF 8 2F 8E A8 3A 4F 75 A6 55 9B 86 D3 A2 A6 55 45 73 46 7 EB 8B B3 6F 2A 75 7B 36 12 CE BA 5B B3 C6 F0 9B C3 

RSA decrypt:536903480
512

RSA len = 512
RSA decrypt completed successfully
24
Plaintext len = 24
Plaintext = 
54 34 2B 62 69 69 45 72 46 56 6D 44 70 4D 64 44 4B 30 50 51 7A 41 3D 3D 

HID returns: test password1234567890

Code: Console.ReadLine();

Serial output: wipe buffers after 20 sec

I press Enter.

Code: onlyKey.Decrypt(...); // encrypted "test key9876543210"

Serial output: (this sequence 7 times with different contents)

Received packet
FF FF FF FF F0 4 FF AB 1A 2B CE 44 18 68 9D D8 B D8 C8 5F A0 14 FE 7F FD E 1D 45 96 B8 E3 2E 2E B 37 57 4 38 D9 31 7B F6 5F 49 C7 B5 23 42 CE B1 D F9 A9 66 E8 D0 70 6B D1 21 37 7F 32 49 

Note that, unlike the first time, we're not seeing "OKDECRYPT MESSAGE RECEIVED" or any other information as they did for the first Decrypt invocation.

HID returns: ERROR DEVICE LOCKED7890

Serial output:

wipe buffers after 5 sec

Note that the output contains part of the previously returned message, despite the fact that this happened after "wipe buffers after 20 sec".

Note that the "ERROR DEVICE LOCKED" message, based on the firmware's source code, indicates that at least one of the following must be true:

Because I don't get "ERROR DEVICE LOCKED" when instead sending OKGETLABELS immediately after OKDECRYPT completes, we can be certain that only CRYPTO_AUTH != 0, because the case for OKGETLABELS also tests the other three conditions but does not test CRYPTO_AUTH.

In summary

Problem: In essence, there is an undocumented order-of-operations + timing assumption. After a successful OKDECRYPT, the OnlyKey does not wait until it has finished cleaning up after the decryption process before accepting new commands. This results in the possibility of misleading error messages and invalid responses.

Suggested Solution:

A timing-based solution (i.e. "after sending OKDECRYPT, you must wait 20 seconds before sending another command") would be difficult for application developers to maintain across firmware and hardware changes.

M-Pixel commented 6 years ago

After working on this a bit more, I think a part of the solution will also need to be speeding up the cooldown/cleanup period. While switching to a smaller key size speeds up decryption, I'm finding that I still get "device locked" unless I wait sometimes over 60 seconds before attempting the second decryption.

onlykey commented 6 years ago

@M-Pixel I have not been able to reproduce what you are seeing here. I am working on the next release of firmware so I would like to make sure we include a fix to this issue. Would you be able to send an example application that demonstrates the issue to t@crp.to

M-Pixel commented 6 years ago

If you're attempting to repro on newer firmware, perhaps you've already fixed it incidentally 🤞. Or perhaps it's a Windows-only problem.

Here is a video: https://screencast-o-matic.com/watch/cF10FFFb4V And the program used in the video: https://github.com/M-Pixel/onlykey-qa

onlykey commented 6 years ago

@M-Pixel Thanks for providing the code sample! I was able to take a quick look today, I still have to do some testing but I think the issues you are seeing are because the device is still in config mode when you are trying to decrypt. Config mode is just for loading secrets and backups (also adding keylabels). In the next release I will make sure to respond with "error not supported in config mode" if other message types are used.

onlykey commented 5 years ago

@M-Pixel I believe that your issue has been addressed in the latest release. Config mode only supports limited things like loading keys but not doing decryption, so the program would really need to be split into 2 parts. We don't have any official documentation on this yet but here is what is supported while in config mode:

So the program flow should go something like this:

Program 1 - Key loading

Program 2 - Key use

I think it would be interesting to use the app you made here to do SSH on Windows now that Windows 10 supports SSH natively. Thanks again for putting this together and let me know if you have any further issues/questions on the latest firmware.