bvanheu / stratatools

Stratasys EEPROM tool
BSD 3-Clause "New" or "Revised" License
88 stars 40 forks source link

Manually Resetting Eeprom Hex Files? #11

Closed unicornswag closed 9 years ago

unicornswag commented 9 years ago

I currently use Fortus 400mc, and I am interested in learning about the encryption used in the eeprom hex files. It seems that the only encrypted portion of the hex is the 10 bytes at 0×58-0×63, and I'm assuming this somehow gets cross-checked against the chip ID. Does anyone have any info on how these 10 bytes get generated, and how they could be manually modified to reset a cartridge to a set amount?

Highlighted below is the section in question from an empty cartridge. 0×62-0×63 is always 8084 on a full cartridge.

00000000 1A 66 41 34 16 EB B0 1F 9F E5 17 20 64 0B 69 AB
00000010 0C 39 96 BC 31 4E 35 C6 85 43 92 5F BF 43 71 79
00000020 34 E2 CD E1 85 76 B1 8E 39 DB CF 71 89 46 6F 22
00000030 39 DB CF 71 89 46 6F 22 A0 3C 46 76 A9 70 5F B0
00000040 53 F3 00 00 00 00 03 CF 55 AA 55 79 AB 0A 52 BE
00000050 E3 F0 00 00 00 00 00 00 2A 0D 84 E4 C2 52 D6 95
00000060 32 93 69 C8 C9 8E B0 FB
53 54 52 41 54 41 53 59
00000070 53 BC 02 2F 0E 7C 70 BC DC DD 3C A3 A9 F8 95 B5
00000080 DD 6F 0B 64 97 1D FD 2D 2E F9 0D AB A4 2D 8B 9F
00000090 0E A5 50 23 5A 1F 47 DF 19 DD F8 A1 6C D3 EB E2
000000A0 6A 89 FF 85 F5 64 06 64 87 82 BB 1C 13 1C ED 49
000000B0 D4 2D 3C 06 D0 3A EA 82 49 2C 60 5D D8 C9 F7 A0
000000C0 7A D2 30 4C C1 2D 88 DB 5C 88 F3 24 3B 0B C1 8B
000000D0 3D DC 47 49 2C 51 83 F4 E6 7A 0A B5 3E CE 5A F4
000000E0 E2 9B 2D 02 AA 3B 5F A1 B1 58 A3 A6 F8 2B 80 88
000000F0 70 19 C4 D7 D1 5B F9 CE 5A B8 5B B3 B2 54 D0 32 00000100 E7 44 1B 6E 58 0C A6 69 50 43 68 9F 6D A5 2E 90 . 00000110 2C CA 64 D8 D2 0B 43 C4 8D FD 6A 5A 73 68 C0 99
00000120 A7 3C 08 C4 50 F0 F6 7D A6 09 D6 8E A2 95 A9 FB
00000130 10 68 F8 F9 CE FF 22 D9 11 EF 55 47 2F CF 54 32
00000140 9C 03 3C CE 5A 11 79 3B 87 68 AF E2 3A 7A 84 2D
00000150 A4 28 8C BB 9D 08 09 7F 41 9A 60 27 D5 8B C5 A6
00000160 86 C0 E5 5A 30 2A F5 9F A6 B9 1B A8 4F AC BB 2A 00000170 01 34 AA 5F BE 4E 24 52 DD 9E 55 17 22 E8 4C 62 00000180 9A 41 B2 13 AE 70 53 7A 44 9E ED 01 99 CD 26 82 00000190 3B 57 B6 07 68 E1 1A 18 01 6A 10 7B 35 54 61 2E
000001A0 5B 84 BA 19 EC E5 B9 EE 99 39 ED 07 0A BD 7B EA 000001B0 11 88 CB 09 0F 7D B0 2F 6C 79 8E 56 A2 8D 4E 05
000001C0 B7 AF 89 4A 12 39 93 AC DF 05 6E D1 93 B2 9B 2D
000001D0 9E 2C 25 7D 82 20 3D 2C 4A E3 ED 5A 04 E7 E9 59
000001E0 84 C1 C4 31 12 39 37 E3 8E 27 FB DA E8 1B 71 AA
000001F0 93 EE E9 61 2B 04 6D 44 88 CD 02 FE A2 01 1E 5E

bvanheu commented 9 years ago

The cartridge is fully encrypted. The code is opensource, you can see how the encryption is implemented. I'm not sure what is your question?

The data at 0x58 is the material quantity remaining on the cartridge.

unicornswag commented 9 years ago

I've taken a look at the code, but I'm not well-versed in Python. I figured if the encryption used to generate the 10 bytes of data at 0×58-0×63 is fairly straightforward, then this could be useful for making similar tools, perhaps implemented in C or even Shell. I'm assuming it's related to the amount of remaining material and the chip ID?

slaytonrnd commented 9 years ago

If you are familiar with C# I created a port of the stratasys code at: https://github.com/slaytonrnd/CartridgeWriter

The bulk of the work in done in Cartridge.cs and Crypto.cs. You can search for "0x58" in Cartridge.cs to see where the current material bytes are read / written.

It took me a while to do the port as I was not familiar with Python either and I still don't fully understand why the encryption algorithm is written the way it is; I'm not an encryption expert. That being said it was a great way to learn a bit about Python and encryption at the same time. I've been doing some C coding recently too and am sure the code could be ported to C as well. I'm not sure about Shell; are you working in the Linux world? Which shell are you using?

unicornswag commented 9 years ago

Cool! Your port looks like a nice, user-friendly way of reading and reflashing the chips. I currently use a Raspberry Pi running Linux to establish a 1-wire serial connection to the eeprom via GPIO. When your C# port generates a decrypted / refilled hex, does it get saved to a temporary file somewhere before being sent to the Arduino? If so, people like myself with alternative serial setups could use your program to generate the hex, and then do the actual flashing with whatever hardware they prefer.

My limited knowledge of the eeprom hex structure is based on comparing hex dumps from cartridges at various levels of filament, as well as from the outline shown here:

http://haveblue.org/?p=938

Obviously, that page is quite old. I'm wondering if anyone is willing to share any further developents in the encryption used there.

Bald888Eagle commented 9 years ago

I looked into this a while back, and Ben explained it some. There is apparently also a custom "whitening key" that's used, as standard DES-X encryption of data gives different output than when done with stratasys software.

The crypto is DES-X in CBC mode, so if you change one block, at the beginning, you have to change every block, but i think their implementation is flawed so the blocks are not chained meaning you can change a block without changing all the blocks.

I think I see why you'd say the implementation is flawed - the Manufacturing Date and Use Date are exactly the same in the hex data on the EEPROM, which I would have thought would be in different blocks of the cipher.  (But if you're just calling Desx_Crypto() from the standard Crypto.Cipher Python Library, how could the encryption/decryption be flawed?) 

Actually there is no flaw in the implementation but the flaw is on how they are using it. Instead of encrypting the whole data with a single key (which would make sense), they encrypt chunks of 8 bytes with a new "DESX" instance, which doesn't make sense since it breaks the concept of chaining the block to distribute the randomness of the cipher.

99 for i in range(len(plaintext)/8):
100 ciphertext[i_8:i_8+8] = bytearray(output_whitener(des(input_whitener(str(plaintext[i_8:i_8+8])))))
101 des = DES.new(str(key[0:8]), DES.MODE_CBC, str(bytearray(8))).encrypt

The culprit lies in the line 101, creating a new instance of the "des" object to reset the cipher state.

slaytonrnd commented 9 years ago

@unicornswag: No, CartridgeWriter does not currently write the changes to file, but it would not be hard to add that to the program. It also currently does not read from a file, which I'm guessing you would need; that is on my TODO list. Right now it reads/writes directly from/to the DS2433 chip via an Arduino and creates a file backup of the contents of the DS2433 chip when it reads.

If you have some hex dumps of the cartridge when it had more material or better yet the original hex dump when the cartridge was full you can load that back into the cartridge to "refill" it. The hex dump has to be from the cartridge (or chip) you are working with because of the way the encryption key is created. The key incorporates the EEPROM UID which is unique for each chip. Check https://github.com/bvanheu/stratasys/blob/master/stratasys/manager.py or https://github.com/slaytonrnd/CartridgeWriter/blob/master/CartridgeWriter/Cartridge.cs and the build_key or BuildKey routines respectively for how the keys are created.

As far as I know bvanheu's stratasys code is the leading edge in describing the encryption algorithm programmatically.

unicornswag commented 9 years ago

Before coming across bvanheau's repo, I was doing exactly as you described, by recording and later rewriting a backup of the original (full) hex.

Currently, I use this software to reflash all of my empty Fortus cartidges to 7 cubic inches, as there is normally just over 7 inches left in the cannister when it registers at 0 on the machine. However, a better aproach may be to reflash all of my new, unused cartridges to a value of around 99.3 cubic inches (a full cannister reads 92.3 on the machine) before using them. That way, you wouldn't need to worry about the serial number being logged on that machine prior to reflashing, and the eeprom then being rejected after the reflash. We also ocassionally get "double-filled" cannisters with 184.6 in^2 of filament when full. I experiented with reflashing an empty eeprom to 193, and the machine accepted this as a valid ammount of material.

On a related note, does anyone know if the encryption has been worked out for the newer Nylon12 and ASA materials?

slaytonrnd commented 9 years ago

I went and had a look at the Fortus 400mc specs and that is a really impressive machine. My experience is limited to working with a Dimension 1200es (and not my machine at that) and ABS+. I don't know if the encryption for the newer materials would change or not; stratasys would probably have to roll out a software update to the machines for that to happen. If the encryption hasn't changed then probably just the material type code from bytes 0x08-0x0f would change and we'd need to update code to handle that.

That is a great idea to pre-flash the cartridge to account for the extra material included for cartridge changes. I didn't know you could reflash the eeprom to have more than the initial amount of material and that could be very useful! I was just thinking you could take it a step further and increase your reflash by a factor of 10 (993.0 cubic inches) and then reload the cartridge whenever the material remaining goes down by 10%. Then you could do 10 refills for every one reflash. Does the machine handle things gracefully when a cartridge runs out of material? I've never seen that happen and always kind of wondered why the machines didn't just run until all of the material in the cartridge was used.

The other advantage of using bvanheu's code is that the serial number can be changed for the cartridge. The machine I was working with had been set to delete the list of cartridge serial numbers stored there as documented in the haveblue post you reference above, but I'm assuming that changing the serial number of the cartridge gets around that problem, though I have yet to verify that in practice.

unicornswag commented 9 years ago

I'm not entirely sure how the machine would handle a fully empty cartridge but it would be good to know. As far as changing the serial number, I believe you can only change the serial of the cartridge, but not the eeprom, as that is hard-wired in when the chip is manufactured. Unfortunately, I think the log on the machine (system.dat) keeps track of the chip's serial, not the cartridge's.

Looking at the python code, I am seeing "material.py", with a list of material names next to some hex. Do you think adding a new entry here would be all that is necessary to add new materials? I'm not sure where the hex next to each material name comes from, and whether you would need to know the specific name (ie. NYL_BLK) of the material being added.

bvanheu commented 9 years ago

Unfortunately, I think the log on the machine (system.dat) keeps track of the chip's serial, not the cartridge's.

This is not the case ;) You can reuse the same cartridge as long as you change the "serial number" on the EEPROM (--serial-number option to stratasys-cli)

Do you think adding a new entry here would be all that is necessary to add new material?

Yes, the hex value is then written on the EEPROM that will be read by the printer.

bvanheu commented 9 years ago

If you are familiar with C# I created a port of the stratasys code at: https://github.com/slaytonrnd/CartridgeWriter

I've added a link in the README to your repo, thanks!

bvanheu commented 9 years ago

Actually there is no flaw in the implementation but the flaw is on how they are using it.

Yep this is exactly what i wanted to say ;) The DES algorithm is not flawed, but the way they use it is broken. I'm not sure why they did it that way, it doesn't make any sense.

unicornswag commented 9 years ago

If anyone's interested, here's a hexdump from any empty Nylon 12, 92 cubic inch cannister.

ID: 39 00 00 01 6B 28 0B 23

00000000 66 AC 46 D2 D1 C8 6A 26 78 C9 56 44 B9 3D 78 80

00000010 57 03 38 0E BA C1 6B 9D BB 57 E9 FE E1 0A 49 A3

00000020 7E F9 03 39 6C 22 33 CB 3B F6 67 CA A2 05 E2 C7

00000030 3B F6 67 CA A2 05 E2 C7 A6 64 3F 19 3F 1D 13 74

00000040 AF 5C 00 00 00 00 B8 C8 55 AA 55 D7 5E 77 52 BE

00000050 A9 FC 00 00 00 00 00 00 9B B7 B1 21 7F D3 60 C2

00000060 69 8E F3 E2 83 2D 31 61 53 54 52 41 54 41 53 59

00000070 53 55 DF 28 B2 37 87 23 7B 08 0E 94 D7 85 47 C6

00000080 51 1F DA 7A B5 D9 00 5E 4F D3 08 C4 80 6E 91 90

00000090 6A F2 1F B7 43 3E 48 2B 85 7C C9 AD 24 77 D3 24

000000A0 90 E1 44 D9 75 D1 80 16 2E 70 35 CE 4B C8 07 7B

000000B0 D8 73 C5 74 9A EF 19 60 12 1A 79 22 84 E3 58 85

000000C0 D0 41 C2 5B 04 5B 2E CA 62 BF 72 4A 62 2F 45 70

000000D0 F6 FB DB B6 FB 96 A1 C5 88 EC E1 06 74 00 00 29

000000E0 9C 4A 32 1B 7B 1C 51 69 91 92 2C 56 A8 BE 1B 65

000000F0 EB EB D9 A0 2D BD 77 43 BF 05 8C 39 B4 95 2C 5A

00000100 3E 51 8F 79 0A 15 10 AB A7 E2 1C 92 30 9B 75 1F

00000110 F0 62 38 F9 59 20 59 37 42 D5 E6 CC 79 E3 D8 30

00000120 5B CC 00 51 3A A1 19 D8 D3 12 9F ED 51 E4 2E F6

00000130 5C 98 04 7A 01 70 0A F0 61 9B 0B 74 81 80 C0 FB

00000140 AE EA 35 F0 6E FF A8 EF EF 6B DF 43 8D 6E 0D 53

00000150 22 C0 85 F4 5E 4E 6B 0F 1F 7A B8 FB D7 18 33 86

00000160 82 2A 6A 8B CA 0E E3 F8 32 C1 4B DD 15 A5 68 CE

slaytonrnd commented 9 years ago

If anyone's interested, here's a hexdump from any empty Nylon 12, 92 cubic inch cannister.

It decrypts fine with stratasys-cli; the material code shows as 0x5a. Add it in the list in material.py and give it a try!

slaytonrnd commented 9 years ago

I'm not sure why they did it that way, it doesn't make any sense.

Thanks for confirming that. I had thought as much when I researched the encryption, but wasn't sure if I was missing something.

I've added a link in the README to your repo, thanks!

Thank you!

unicornswag commented 9 years ago

It decrypts fine with stratasys-cli; the material code shows as 0x5a. Add it in the list in material.py and give it a try!

Awesome! I gave it a try with the name "NYL12" and it worked great! However I don't understand how you got the "0x5a". When I read the cartridge, it gives the material name "unknown" next to a hex code different from that one. when I tried adding that code to material.py and rereading, the code changed.

slaytonrnd commented 9 years ago

This is what I got when I ran it without changes to material.py:

nylon1

It has "unknown (90 - 0x5a)" for material, but I wasn't sure if the 0x5a was really the correct value so I wrote the decrypted content to file and found "00 00 00 00 00 80 56 40" in bytes 0x08-0x0f. When I those values into a double and printed it out it was 90 (decimal) or 0x5a (hex). What value did you end up with?

unicornswag commented 9 years ago

For some reason when I tried pulling the info from the the hex file on the EEPROM, I was having issues getting the material code. However, when I copied the hex onto my RasPi's SD card and ran stratasys-cli on it again, I was able to get the code, "0x5a", without a problem. Thanks!

slaytonrnd commented 9 years ago

Excellent; I'm glad you got it figured out!