Jump to content
IGNORED

Lynx Encryption?


cdoty

Recommended Posts

Can anyone explain exactly how the Lynx encryption works?

 

I'm interested to see if some of the same techniques were applied to the 3DO.

 

It is standard RSA encryption. The formula is:

 

PLAINTEXT = (ENCRYPTED ^ 3) % PUBLIC_KEY

 

The public key is

static unsigned char N[MAX_m] = {

0x35, 0xB5, 0xA3, 0x94, 0x28, 0x06, 0xD8, 0xA2,

0x26, 0x95, 0xD7, 0x71, 0xB2, 0x3C, 0xFD, 0x56,

0x1C, 0x4A, 0x19, 0xB6, 0xA3, 0xB0, 0x26, 0x00,

0x36, 0x5A, 0x30, 0x6E, 0x3C, 0x4D, 0x63, 0x38,

0x1B, 0xD4, 0x1C, 0x13, 0x64, 0x89, 0x36, 0x4C,

0xF2, 0xBA, 0x2A, 0x58, 0xF4, 0xFE, 0xE1, 0xFD,

0xAC, 0x7E, 0x79

};

 

You decrypt the data in 51 byte chunks.

 

So basically you take 51 bytes and treat it as a very large number and raise it to the power of 3. Then divide it by the 51 byte very large public key and the modulo of this division is the plain text (51 bytes).

 

After that you take the next 51 bytes and repeat this procedure until the whole binary has been decrypted.

 

Curt Wendel has posted the sources for doing this in the Lynx thread.

 

The private key for doing the encryption is not known in plain text. It was encrypted by another RSA key which means that it existed only for a brief time in the Amiga RAM during the encryption process.

 

--

Karri

Link to comment
Share on other sites

  • 4 weeks later...
but the lynx has not enough ram to hold a decrypted copy of a game cart, and I think it's only done at boot time, isn't it used only as a checksum to validate that the software is genuine ?

What exactly does it do with the decrypted data ?

 

It places the decrypted data in memory and perfoms a jump there. So it better be correct ;)

 

In Handy I have an option to do the decryption in Handy using the full power of the PC instead of emulating the startup code as 6502 instructions. This approach is a LOT faster.

 

The encrypted code actually contains the loader that fetches the first executable from the cart.

 

Harry Dodgson created a very tiny 410 byte loader that is the only encrypted code on cart for the current build system.

 

I just received yesterday a modified 410 byte loader from TailChao. The idea is to create loaders that initialize the AUDIO I/O bit at startup also.

 

The cool thing with initializing this bit at startup is that you can extend the address pins by one extra bit. And this allows 1Mbyte game carts.

 

--

Karri

Link to comment
Share on other sites

So, does it only decrypts the first 64KiB ? Or does it always decrypt data when reading from the cart ? I think I read that if the encryption isn't correct, the lynx displays "insert game", it would just crash if the data decrypted wasn't valid code.

 

I'm just starting learning how to program the lynx, there are some points I have trouble understanding.

Like for example, how do we do carts with a different size than 256KiB

 

the concept of files on the cart rather puzzle me too

Link to comment
Share on other sites

So, does it only decrypts the first 64KiB ? Or does it always decrypt data when reading from the cart ? I think I read that if the encryption isn't correct, the lynx displays "insert game", it would just crash if the data decrypted wasn't valid code.

 

I'm just starting learning how to program the lynx, there are some points I have trouble understanding.

Like for example, how do we do carts with a different size than 256KiB

 

the concept of files on the cart rather puzzle me too

 

The cart encrypts as many bytes as you ask it to do. In my case 410 bytes.

 

If the encryption is incorrect you get the INSERT GAME message.

 

The Lynx cart is addressed by 8 bits. These bits are connected to the highest address lines.

The lower address lines are driven by a counter.

 

For a 256k cart you can read 1024 bytes in a row after you set the 8 bit high address.

For a 128k cart you can only read 512 bytes and for a 512k cart 2048 bytes.

 

After the encrypted "loader" 410 bytes you have a directory for the files with 8 bytes/file.

These bytes tell the 8-bit high value of the address lines and the 16-bit offset from where on the cart the file starts.

It also contains a 16-bit address of where the file should go in RAM when you read it.

It also has 3 bytes of data that you can use any way you want.

 

The directory entry 0 is the address of the title sprite.

The directory entry 1 is the address for the first executable.

The rest of the cart is up tp you.

 

--

Karri

Link to comment
Share on other sites

Thanks a lot for the explanations.

 

About the audioin pin, can't we just toggle it after the encryption check just like a bank switching ? Or do you mean that it could be transparent to the software ?

 

The problem is that the encryption needs the directory. And the Lynx I starts up with AUDIO I/O pin in another state then LYNX II. Therefore we need to have the directory twice in the memory and that is not nice. So I would like to have only the bootloader twice in memory but the directory would be there only once.

 

To accomplish this the bootloader needs to set the AUDIO I/O pin to zero before it starts reading the directory.

 

--

Karri

Link to comment
Share on other sites

So one can make any size cartridge with the same 410bytes boot loader ?

 

And does the new bootloader work ?

 

Anyway even if you have to double the directory, it shouldn't take that much space (say we have 200 entries, thats 1600bytes, with the bootloader it's less than 2KiB, that leaves 510KiB for each bank).

 

Speaking of banks, in the Handy cartridge file format there's support for bank0 and bank1, how is the lynx supposed to switch banks ?

Link to comment
Share on other sites

So one can make any size cartridge with the same 410bytes boot loader ?

 

And does the new bootloader work ?

 

Anyway even if you have to double the directory, it shouldn't take that much space (say we have 200 entries, thats 1600bytes, with the bootloader it's less than 2KiB, that leaves 510KiB for each bank).

 

Speaking of banks, in the Handy cartridge file format there's support for bank0 and bank1, how is the lynx supposed to switch banks ?

 

Every bank size needs a different boot loader because the content of the directory is different.

 

The second bank is used as a write strobe for flash or SRAM carts. I have never seen a cart where it would be used for a second rom chip.

 

Of course it would be possible to make a 2MB cart using two stobes and AUDIO I/O.

 

But I would prefer to extend the address range with a simple latch instead.

 

Actually I have one prototype on my desk where I extend the address range with a I2C 8 bit latch. This cart would use 5 bits for extra address pins, one bit to control the use of AUDIO I/O, 2 bits for controlling the block sizes.

 

The idea is to give full software control of a 8MB cart to find my cart images easier. No more flipping of DIP switches.

--

Karri

Link to comment
Share on other sites

  • 11 months later...

But I would prefer to extend the address range with a simple latch instead.

 

Actually I have one prototype on my desk where I extend the address range with a I2C 8 bit latch. This cart would use 5 bits for extra address pins, one bit to control the use of AUDIO I/O, 2 bits for controlling the block sizes.

 

The idea is to give full software control of a 8MB cart to find my cart images easier. No more flipping of DIP switches.

--

Karri

 

Do you have schematics for this prototype that you're willing to share? How are you doing the I2C control from software? Which pins on the cart connector are you using for I2C?

 

Wookie

Link to comment
Share on other sites

Back to the encryption question. I understand how the decryption works and I've read through the docs on the lynx encryption stuff that you can get from cgexpo.com.

 

I was wondering if anybody has written a doc on how the encryption code works. Has anybody ported the code to any other platform? Is there a utility for Windows/Linux for doing the encryption or is everybody just running uae and encrypting their files on an (emulated) amiga?

 

Also, Karri, are you still making dev flash carts for people? I'm interesting in buying one from you. I've been working on some software projects related to NES development with the copyNES device from retrousb.com. I've written an open source client library for interfacing with the copyNES here. I also forked Brian Provinciano's neshla high level assembler and ported it to Linux, it is here. The reason I say this is because I've been working on making neshla into a re-targetable assembler. I'm making it extendable so that it can compile for the NES, Lynx and other 6502 based systems.

 

I'm interested in getting a dev cart so that I can add support for the Lynx to neshla, including writing a set of system libraries in neshla code for the Lynx, similar to the one for the NES.

 

I'm interested in the encryption software because I want to add the option to encrypt the output properly so that the resulting binary can be immediately flashed to the dev cart and played.

Link to comment
Share on other sites

Back to the encryption question. I understand how the decryption works and I've read through the docs on the lynx encryption stuff that you can get from cgexpo.com.

 

I was wondering if anybody has written a doc on how the encryption code works. Has anybody ported the code to any other platform? Is there a utility for Windows/Linux for doing the encryption or is everybody just running uae and encrypting their files on an (emulated) amiga?

 

Also, Karri, are you still making dev flash carts for people? I'm interesting in buying one from you. I've been working on some software projects related to NES development with the copyNES device from retrousb.com. I've written an open source client library for interfacing with the copyNES here. I also forked Brian Provinciano's neshla high level assembler and ported it to Linux, it is here. The reason I say this is because I've been working on making neshla into a re-targetable assembler. I'm making it extendable so that it can compile for the NES, Lynx and other 6502 based systems.

 

I'm interested in getting a dev cart so that I can add support for the Lynx to neshla, including writing a set of system libraries in neshla code for the Lynx, similar to the one for the NES.

 

I'm interested in the encryption software because I want to add the option to encrypt the output properly so that the resulting binary can be immediately flashed to the dev cart and played.

 

Nice ideas. I should really dig up my flash manufacturing for such a good cause.

 

I have also ported the encryption/decyption process to C-code. But there is one problem...

 

While the Atari guys were working on this they were very afraid that somebody would crack the encryption be stealing the private key. So they came up with an ingenious scheme. The private key is never available.

 

The silly swapping of disks and complex stuff comes from the fact that the private key used for signing the binary is signed by another key that is lost forever. At the signing process we have only the public key of the "lost forever" key. With this public key you can retrieve the private key. But the private key will only be retrieved in Amiga RAM.

 

So the only way to retrieve the real private key is to run the Amiga code in an Amiga debugger and capture the key from memory. Too much work for me - sorry.

 

What I know so far is that the code needed to decrypt the cart is like this. I added this to Handy to speed up debugging of the carts.

 

// Some private members
int ptr, c,
 num2, num7, num8, numA,
 ptr5, ptrB, ptrF,
 ptr5037, ptr5043, ptr5047,
 ptr506C, ptr5071, ptrTMP, Cptr,
 Actr, Yctr, Xctr, carry;
int in_hand, out_hand;
unsigned char   buffer[600];
unsigned char   work[256];
unsigned char   result[600];
unsigned char   constant[52];
int r_len;
int err;

// This is actually the SHA1 public key that resides in Lynx ROM
unsigned char constant_vals[52] = {
 0x35, 0xB5, 0xA3, 0x94, 0x28, 0x06,
 0xD8, 0xA2, 0x26, 0x95, 0xD7, 0x71, 0xB2, 0x3C, 0xFD, 0x56, 0x1C, 0x4A, 0x19, 0xB6, 0xA3, 0xB0,
 0x26, 0x00, 0x36, 0x5A, 0x30, 0x6E, 0x3C, 0x4D, 0x63, 0x38, 0x1B, 0xD4, 0x1C, 0x13, 0x64, 0x89,
 0x36, 0x4C, 0xF2, 0xBA, 0x2A, 0x58, 0xF4, 0xFE, 0xE1, 0xFD, 0xAC, 0x7E, 0x79
};

void CSystem::minus_it()
{
 int ct, tmp;
 /* carry is set on entry */
 for (ct=50;ct>=0;ct--) {
   tmp = work[ptr506C+ct] - constant[ct] - (1-carry);
   if (tmp < 0) {
     work[ptr5071+ct] = (unsigned char)(tmp + 256);
     carry = 0;
   }
   else {
     work[ptr5071+ct] = (unsigned char)(tmp);
     carry = 1;
   }
 }
}

void CSystem::sub505D()
{
 ptr506C = ptrB;
 if (work[ptrB] < constant[0])
   return;
 else
   carry = 1;
 minus_it();
 if (carry == 0)
   return;
 ptrTMP = ptrB;
 ptrB = ptr5071;
 ptr5071 = ptrTMP;
}

void CSystem::shift_it()
{
 int ct, oldc;

 oldc = 0;
 for (ct=50;ct>=0;ct--) {
   carry = (work[ptr5037+ct] & 0x80) / 0x80;
   work[ptr5037+ct] = (unsigned char)((work[ptr5037+ct] << 1) + oldc);
   oldc = carry;
 }
}

void CSystem::add_it()
{
 int ct, tmp;
 carry = 0;
 for (ct=50;ct>=0;ct--) {
   tmp = work[ptr5043+ct] + work[0xAA+ct] + carry;
   if (tmp >= 256)
     carry = 1;
   else
     carry = 0;
   work[ptr5047+ct] = (unsigned char)(tmp);
 }
}

void CSystem::sub5018()
{
 int ct;

 for (ct=0;ct<51;ct++)
   work[ptrB+ct] = 0;
 Yctr = 0;
 do {
   numA = work[ptrF + Yctr];
   num8 = 255;
   do {
     ptr5037 = ptr5043 = ptr5047 = ptrB;
     shift_it();
     carry = (numA & 0x80) / 0x80;
     numA = (unsigned char)(numA << 1);
     if (carry != 0) {
       add_it();
       sub505D();
       if (carry != 0)
         sub505D();
     } else
       sub505D();
     num8 = num8 >> 1;
   } while (num8 != 0);
   Yctr ++ ;
 } while (Yctr < 51);
}

void CSystem::sub5000()
{
 ptrB = 0x11;
 ptr5071 = 0x44;
 ptrF = 0xAA;
 sub5018();
 ptrF = ptrB;
 ptrB = 0x77;
 sub5018();
}

void CSystem::convert_it()
{
 int ct;
 long t1, t2;

 num7 = buffer[Cptr];
 num2 = 0;
 Cptr ++ ;
 do {
   for (ct=50;ct>=0;ct--) {
     work[0xAA+ct] = buffer[Cptr];
     Cptr ++ ;
   }
   if ((work[0xAA] | work[0xAB] | work[0xAC]) == 0) {
     err = 1;
   }
   t1 = ((long)(work[0xAA]) << 16) +
     ((long)(work[0xAB]) <<   +
     (long)(work[0xAC]);
   t2 = ((long)(constant[0]) << 16) +
     ((long)(constant[1]) <<   +
     (long)(constant[2]);
   if (t1 > t2) {
     err = 1;
   }
   sub5000();
   if (work[ptrB] != 0x15) {
     err = 1;
   }
   Actr = num2;
   Yctr = 0x32;
   do {
     Actr += work[ptrB + Yctr];
     Actr &= 255;
     result[ptr5] = (unsigned char) (Actr);
     ptr5 ++ ;
     Yctr -- ;
   } while (Yctr != 0);
   num2 = Actr;
   num7 ++ ;
 } while (num7 != 256);
 if (Actr != 0) {
   err = 1;
 }
}

Later in Handy code...

if (useFastboot) {
 int i;
 // Decrypt Lynx cart bootloader. This is done inside the Lynx at boot time
 // Based on work done by Harry Dodgson
 c = 520;
 for (i = 0; i < c; i++) {
   buffer[i] = mCart->Peek(i);
 }
 ptr5 = 0;
 Cptr = 0;
 convert_it();
 ptr5 = 256;
 convert_it();
 for (i = 0; i < c; i++) {
   mRam->Poke(i + 0x200, result[i]);
 }

 

--

Karri

Link to comment
Share on other sites

I also forked Brian Provinciano's neshla high level assembler and ported it to Linux, it is here. The reason I say this is because I've been working on making neshla into a re-targetable assembler. I'm making it extendable so that it can compile for the NES, Lynx and other 6502 based systems.

 

42Bastian did a similar thing called the BLL (Behind Lynx Lines).

 

Actually I have been trying to come up with a macro-package that would allow me to write structured assembly code in BLL style for the cc65.org assembler. It has the power to do this.

 

What I like about the cc65.org system is that it makes C-code into readable assembler. It is also aware of assembler tricks that I do not know. And therefore my C-code is more efficient than any assembler code I could write.

 

Here is just an example of my Klondike Solitaire game rules. A function to check for the rule to add a card to the target pile:

; ---------------------------------------------------------------
; unsigned char __near__ add_klondikeTarget (__near__ struct $anon-struct-0008*, __near__ struct $anon-struct-0007*)
; ---------------------------------------------------------------

.segment        "CARDS_CODE"

.proc   _add_klondikeTarget: near

.segment        "CARDS_BSS"

L000B:
       .res    2,$00

.segment        "CARDS_CODE"

;
; Card *newone = c2;
;
       jsr     ldax0sp
       sta     L000B
       stx     L000B+1
;
; if (Pile_isEmpty(pile))
;
       ldy     #$05
       jsr     pushwysp
       jsr     _Pile_isEmpty
       tax
       beq     L000D
;
; return (Card_rank(newone) == Ace);
;
       lda     L000B
       ldx     L000B+1
       jsr     pushax
       jsr     _Card_rank
       cpx     #$00
       bne     L0013
       cmp     #$01
L0013:  jsr     booleq
       jmp     incsp4
;
; return (Card_rank(newone) == Card_rank(Pile_top(pile)) + 1)
;
L000D:  lda     L000B
       ldx     L000B+1
       jsr     pushax
       jsr     _Card_rank
       jsr     pushax
       ldy     #$07
       jsr     pushwysp
       jsr     _Pile_top
       jsr     pushax
       jsr     _Card_rank
       ina
       bne     L0019
       inx
L0019:  jsr     tosicmp
;
; && (Card_suit(Pile_top(pile)) == Card_suit(newone));
;
       bne     L001A
       ldy     #$05
       jsr     pushwysp
       jsr     _Pile_top
       jsr     pushax
       jsr     _Card_suit
       jsr     pushax
       lda     L000B
       ldx     L000B+1
       jsr     pushax
       jsr     _Card_suit
       jsr     tosicmp
       beq     L0014
L001A:  ldx     #$00
       txa
       jmp     incsp4
L0014:  ldx     #$00
       lda     #$01
;
; }
;
       jmp     incsp4

.endproc

 

The code above is easy to read and could be developed further to create a source-level debugger for C-programs.

 

For assembler freaks it would be nice to have a good macro-kit on top of this assembler. 42Bastian started to work on this and he got this far:

bllmacros.txt

Link to comment
Share on other sites

So the only way to retrieve the real private key is to run the Amiga code in an Amiga debugger and capture the key from memory. Too much work for me - sorry.

 

You could offer a free dev cart to the first person to post the private key and C code that uses the key to properly encrypt a bootloader.

 

Wookie.

Link to comment
Share on other sites

So I was trying to get UAE up and running so that I could try out the RSA encryption stuff. I was looking at Harry Dodgson's walkthrough but what he starts with is different than what you get in the zip file available from the cgexpo.com web site.

 

How do you turn the contents of the zip file into .adf amiga floppy images that UAE can boot from to run through the encryption? I never owned or used an Amiga so I'm a complete newb on that platform. I've never used UAE before either. I do have the Amiga Forever discs that contain the licenses boot roms and workbench and apps so I can get UAE to boot up to the workbench.

 

Harry seems to have left out some crucial details that Amiga experts just "know" but complete newbs like me do not.

Link to comment
Share on other sites

So I was trying to get UAE up and running so that I could try out the RSA encryption stuff. I was looking at Harry Dodgson's walkthrough but what he starts with is different than what you get in the zip file available from the cgexpo.com web site.

 

How do you turn the contents of the zip file into .adf amiga floppy images that UAE can boot from to run through the encryption? I never owned or used an Amiga so I'm a complete newb on that platform. I've never used UAE before either. I do have the Amiga Forever discs that contain the licenses boot roms and workbench and apps so I can get UAE to boot up to the workbench.

 

Harry seems to have left out some crucial details that Amiga experts just "know" but complete newbs like me do not.

 

Yes, I also never bothered to try out the encryption process.

 

What I want is just 3 encrypted bootloaders:

- 2048 bytes/sector

- 1024 bytes/sector

- 512 bytes/sector

 

All these bootloaders should also set the AUDIO I/O bit to output and to a known state (I believe 0 is good).

 

42Bastian and Duranik use a scheme where AUDIO I/O bit is an extra address line.

This AUDIO I/O bit is in different state when you power up a Lynx I or a Lynx II. So you need to have two copies of the bootloader and directory on the cart.

 

If the bootloader would start by setting the AUDIO I/O bit then you would only need two bootloaders and one directory.

 

If we get these 3 bootloaders then we never need to use encryption for making the cart. Just copy the loader in front of the directory and off you go.

 

--

Karri

Link to comment
Share on other sites

If we get these 3 bootloaders then we never need to use encryption for making the cart. Just copy the loader in front of the directory and off you go.

 

I still want the key so that we don't lose the ability to encrypt new stuff in the future. Having fish and knowing how to fish are two very different things.

Link to comment
Share on other sites

If we get these 3 bootloaders then we never need to use encryption for making the cart. Just copy the loader in front of the directory and off you go.

 

Wouldn't you still need to encrypt the bootloader though? I thought that the Lynx OS unencrypts the bootloader using the key embedded in the device? Is that not correct?

Link to comment
Share on other sites

Harry created a bootloader and encrypted it. The idea is that you tell the Lynx to include only 410 bytes in the signature. The bootloader happens to be exactly 410 bytes. This means that the rest of the cart can be anything. So we can use the same bootloader for all carts.

 

The only problem is that to calculate the position of the title sprite on the cart and the first executable on the cart you need to use the same block length as was coded into the encrypted bootloader. So we need 3 different encrypted bootloaders.

 

Then it would be cool to know the real private key. But checking the signature of more than 410 bytes would just slow down the boot process.

 

--

Regards,

 

Karri

Link to comment
Share on other sites

Then it would be cool to know the real private key. But checking the signature of more than 410 bytes would just slow down the boot process.

 

I don't want to encrypt more than the 410 bytes. I just want to be able to make my own boot loader. I don't want to be at the mercy of some binary boot loader that we can't generate on our own.

 

Wookie

Link to comment
Share on other sites

Harry created a bootloader and encrypted it. The idea is that you tell the Lynx to include only 410 bytes in the signature. The bootloader happens to be exactly 410 bytes. This means that the rest of the cart can be anything. So we can use the same bootloader for all carts.

 

The only problem is that to calculate the position of the title sprite on the cart and the first executable on the cart you need to use the same block length as was coded into the encrypted bootloader. So we need 3 different encrypted bootloaders.

 

Then it would be cool to know the real private key. But checking the signature of more than 410 bytes would just slow down the boot process.

 

Ah, I understand. I thought you were suggesting that we wouldn't need any encryption at all. Can't you just get Harry to modify the bootloader to create the three versions? If you had the encryption key, you could build a bootloader (with an external program) and link at linker time with cc65, using whichever bootloader you required. Actually forget it, just easier and faster to have pre-assembled bootloaders that you include.

Link to comment
Share on other sites

Karri, do you have contact info for Harry Dodgson? I'd like to email him and see if I can get the .adf disk images that he used when taking screen caps of his walkthrough of encrypting a lynx rom. It looks like the source code for the encryption tool is in the .zip file from cgexpo.com so if I can get a set of working disk images, I'll be able to start poking around to get the encryption key used for encryption.

Link to comment
Share on other sites

Karri, do you have contact info for Harry Dodgson? I'd like to email him and see if I can get the .adf disk images that he used when taking screen caps of his walkthrough of encrypting a lynx rom. It looks like the source code for the encryption tool is in the .zip file from cgexpo.com so if I can get a set of working disk images, I'll be able to start poking around to get the encryption key used for encryption.

 

I have those sources too. Curt Vendell posted them on AtariAge.

 

If you want I can also mail you my derived work that does encrypt and decrypt stuff.

 

Harry is here on AtariAge. I think his Atari-name is Harry_Dodgson

 

--

Karri

Link to comment
Share on other sites

I have those sources too. Curt Vendell posted them on AtariAge.

 

By "sources" do you mean the .adf disk images? The zip file from cgexpo.com just contains a bunch of source files and executables. I've tried getting them into UAE but haven't been successful. I'm not sure how to get files from my linux box into an Amiga .adf disk image without an actual Amiga computer. Plus, even if I was able to get the files into Amiga .adf disk images, I have no idea which files go onto which disk image.

 

If you've got the disk images already, I'd love to get them. If not, I'll look up Harry and see if he can help me put together some disk images.

 

Wookie

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...