Author Topic: Quansheng UV5 (new 2024 V5.00.03) wont allow FW change or chirp. Any Clues?  (Read 15886 times)

0 Members and 1 Guest are viewing this topic.

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
some new findings. It seems that the new bootloader 5 really expects encrypted firmware image.

Here is a fragment of ProcessPacket_057b function (it handles flash write packet):
Code: [Select]
     3d2: 4822      ldr r0, [pc, #136] @ (0x45c)      @ r0 = (uint8_t*)0x20001124;
     3d4: f7ff febe bl 0x154                         @ memcpy(0x20001124, packet_ptr->flashData, 0x100);
     3d8: 4920      ldr r1, [pc, #128] @ (0x45c)      @ r1 = (uint8_t*)0x20001124;
     3da: 2000      movs r0, #0                        @ r0 = 0;
                                                            @ do {
     3dc: 0082      lsls r2, r0, #2                    @    r2 = r0 << 2;
     3de: 588b      ldr r3, [r1, r2]                  @    r3 = *(uint32_t*)&r1[r2];
     3e0: 1c40      adds r0, r0, #1                    @    r0 += 1;
     3e2: ba1b      rev r3, r3                        @    r3 = bswap_32(r3);
     3e4: b280      uxth r0, r0                        @    r0 = (uint16_t)r0;
     3e6: 508b      str r3, [r1, r2]                  @    *(uint32_t*)&r1[r2] = r3
     3e8: 2840      cmp r0, #64 @ 0x40
     3ea: d3f7      bcc.n 0x3dc                    @ } while (r0 < 64);
     3ec: 4f1c      ldr r7, [pc, #112] @ (0x460)      @ r7 = 0x20000F24;
     3ee: 2600      movs r6, #0                        @ r6 = 0;
                                                            @ do {
     3f0: 0130      lsls r0, r6, #4                    @    r0 = r6 << 4;
     3f2: 4a1a      ldr r2, [pc, #104] @ (0x45c)      @    r2 = 0x20001124;
     3f4: 19c1      adds r1, r0, r7                    @    r1 = r0 + r7;
     3f6: 1880      adds r0, r0, r2                    @    r0 = r0 + r2;
     3f8: f7ff ff10 bl 0x21c                         @    DecryptBlock128(0x20001124 + r6*16, 0x20000F24 + r6*16);
     3fc: 1c76      adds r6, r6, #1                    @    r6 += 1;
     3fe: b2b6      uxth r6, r6                        @    r6 = (uint16_t)r6;
     400: 2e10      cmp r6, #16
     402: d3f5      bcc.n 0x3f0                    @ } while (r6 < 16);
     404: 2000      movs r0, #0                        @ r0 = 0;
                                                            @ do {
     406: 0082      lsls r2, r0, #2                    @    r2 = r0 << 2;
     408: 58b9      ldr r1, [r7, r2]                  @    r1 = *(uint32_t*)&r7[r2];
     40a: 1c40      adds r0, r0, #1                    @    r0 += 1;
     40c: ba09      rev r1, r1                        @    r1 = bswap_32(r1);
     40e: b280      uxth r0, r0                        @    r0 = (uint16_t)r0;
     410: 50b9      str r1, [r7, r2]                  @    *(uint32_t*)&r7[r2] = r1;
     412: 2840      cmp r0, #64 @ 0x40
     414: d3f7      bcc.n 0x406                    @ } while (r0 < 64);
     416: b672      cpsid i                        @ DisableIRQ();
     418: 7a68      ldrb r0, [r5, #9]                  @ r0 = (uint8_t)packet_ptr[9];// r0=packet_ptr->chunkNumber.hi;
     41a: 7a29      ldrb r1, [r5, #8]                  @ r1 = (uint8_t)packet_ptr[8];// r1=packet_ptr->chunkNumber.lo;
     41c: 0200      lsls r0, r0, #8                    @ r0 <<= 8;
     41e: 4308      orrs r0, r1                        @ r0 |= r1;                   // r0=packet_ptr->chunkNumber;
     420: 0201      lsls r1, r0, #8                    @ r1 = r0 << 8;               // r1=packet_ptr->chunkNumber * 0x100;
     422: 2001      movs r0, #1                        @ r0 = 1;
     424: 0300      lsls r0, r0, #12                   @ r0 <<= 12;
     426: 1808      adds r0, r1, r0                    @ r0 = r1 + r0;
     428: 2240      movs r2, #64 @ 0x40              @ r2 = 64;
     42a: 490d      ldr r1, [pc, #52] @ (0x460)      @ r1 = 0x20000F24;
     42c: f7ff fede bl 0x1ec                         @ call_thumb_200001bc(0x1000 + packet_ptr->chunkNumber * 0x100, 0x20000F24, 64);  // write flash page???
     430: b662      cpsie i                        @ EnableIRQ();

As you can see, it calls the function at address 0x21c. I name it DecryptBlock128, because it looks like using some hardware cryptographic module to decrypt 128 bits of data:
Code: [Select]
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x21c --stop-address=0x254 -Mforce-thumb bootloader.bin

@ DecryptBlock128(r0, r1)

0000021c <.data+0x21c>:
     21c: 4a0c      ldr r2, [pc, #48]      @ (0x250) @ r2 = 0x400BD000;
     21e: 6803      ldr r3, [r0, #0]   
     220: 6093      str r3, [r2, #8]        @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[0]
     222: 6843      ldr r3, [r0, #4]
     224: 6093      str r3, [r2, #8]        @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[4]
     226: 6883      ldr r3, [r0, #8]
     228: 6093      str r3, [r2, #8]        @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[8]
     22a: 68c0      ldr r0, [r0, #12]
     22c: 6090      str r0, [r2, #8]        @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[12]
                                                  @ do {
     22e: 6850      ldr r0, [r2, #4]        @    r0 = *(uint32_t*)&r2[4];
     230: 07c0      lsls r0, r0, #31         @    r0 <<= 31;
     232: d0fc      beq.n 0x22e          @ } while (r0==0);
     234: 68d0      ldr r0, [r2, #12]
     236: 6008      str r0, [r1, #0]        @ *(uint32_t*)&r1[0] = *(uint32_t*)&r2[12];
     238: 68d0      ldr r0, [r2, #12]
     23a: 6048      str r0, [r1, #4]        @ *(uint32_t*)&r1[4] = *(uint32_t*)&r2[12];
     23c: 68d0      ldr r0, [r2, #12]
     23e: 6088      str r0, [r1, #8]        @ *(uint32_t*)&r1[8] = *(uint32_t*)&r2[12];
     240: 68d0      ldr r0, [r2, #12]
     242: 60c8      str r0, [r1, #12]       @ *(uint32_t*)&r1[12] = *(uint32_t*)&r2[12];
     244: 6810      ldr r0, [r2, #0]        @ r0 = *(uint32_t*)&r2[0];
     246: 2180      movs r1, #128 @ 0x80
     248: 4308      orrs r0, r1   
     24a: 6010      str r0, [r2, #0]        @ *(uint32_t*)&r2[0] = *(uint32_t*)&r2[0] | 0x80;
     24c: 4770      bx lr                  @ return
     24e: 0000
     250: d000      DCD  0x400BD000
     252: 400b     

Manually decompiled version:
Code: [Select]
void DecryptBlock128([r0]uint32_t* src, [r1]uint32_t* dst) {

     uint32_t* reg_base = 0x400BD000;
     
     reg_base[2] = src[0];
     reg_base[2] = src[1];
     reg_base[2] = src[2];
     reg_base[2] = src[3];
     
     while ((reg_base[1] & 1)==0) { }
     
     dst[0] = reg_base[3];
     dst[1] = reg_base[3];
     dst[2] = reg_base[3];
     dst[3] = reg_base[3];
     
     reg_base[0] = reg_base[0] | 0x80;
}

As you can see, it sends data to some hardware unit with base address 0x400BD000 then waits for completion and reads data back.

And now it's more clear what meaning the new byte field in the version packet. It selects one of 16 keys stored in the bootloader at address 0x09f0, each key is 32 bytes length (256 bit).

Here is the code of ProcessPacket_057d function which process version packet:
Code: [Select]
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x2a8 --stop-address=0x30c -Mforce-thumb bootloader.bin

@ ProcessPacket_057d([r0]uint8_t* packet_ptr)

000002a8 <.data+0x2a8>:
     2a8: b510      push {r4, lr}
     2aa: 7d01      ldrb r1, [r0, #20]               @ r1 = (uint8_t)packet_ptr[20];
     2ac: b08c      sub sp, #48 @ 0x30            @ sp -= 48;     // alloc 48 bytes on stack
     2ae: 2910      cmp r1, #16
     2b0: d224      bcs.n 0x2fc                  @ if (r1 >= 16) return;
     2b2: 4a13      ldr r2, [pc, #76] @ (0x300)    @ r2 = 0x0BF0;                  // pointer to string "5.00.01"
     2b4: 7900      ldrb r0, [r0, #4]                @ r0 = (uint8_t)packet_ptr[4];  // first letter of version string
     2b6: 7812      ldrb r2, [r2, #0]                @ r2 = '5';
     2b8: 4290      cmp r0, r2
     2ba: d001      beq.n 0x2c0
     2bc: 282a      cmp r0, #42 @ 0x2a
     2be: d11d      bne.n 0x2fc                  @ if (r0!=r2 && r0!='*') return;
     2c0: 4a10      ldr r2, [pc, #64] @ (0x304)    @ r2 = (uint8_t*)0x20000314;    // ram314
     2c2: 2001      movs r0, #1                      @ r0 = 1;
     2c4: 7050      strb r0, [r2, #1]                @ *(uint8_t*)&r2[1] = r0;       // ram314->IsFlashWriteEnable = 1;
     2c6: 4810      ldr r0, [pc, #64] @ (0x308)    @ r0 = 0x09f0;      // ptr to bootloader area at 0x09f0 offset
     2c8: 0149      lsls r1, r1, #5                  @ r1 = r1 << 5;
     2ca: 1809      adds r1, r1, r0                  @ r1 += r0;         // r1 = (byte)packet_ptr[20] * 0x20 + 0x09f0;
     2cc: 460c      mov r4, r1                      @ r4 = r1;
     2ce: 2210      movs r2, #16                     @ r2 = 16;
     2d0: a804      add r0, sp, #16                 @ r0 = sp+16;
     2d2: f7ff ff3f bl 0x154                       @ memcpy(r0, r1, r2);
     2d6: 4621      mov r1, r4                      @ r1 = r4;
     2d8: 3110      adds r1, #16                     @ r1 += 16;
     2da: 2210      movs r2, #16                     @ r2 = 16;
     2dc: 4668      mov r0, sp                      @ r0 = sp;
     2de: f7ff ff39 bl 0x154                       @ memcpy(r0, r1, r2);
     2e2: 466a      mov r2, sp                      @ r2 = sp;
     2e4: a904      add r1, sp, #16                 @ r1 = sp+16;
     2e6: 2001      movs r0, #1                      @ r0 = 1;
     2e8: f000 f902 bl 0x4f0                       @ call_4f0(r0, r1, r2);
     2ec: a808      add r0, sp, #32                 @ r0 = sp+32;
     2ee: f7ff ffb1 bl 0x254                       @ call_254(r0);
     2f2: 466a      mov r2, sp                      @ r2 = sp;
     2f4: a908      add r1, sp, #32                 @ r1 = sp+32;
     2f6: 2002      movs r0, #2                      @ r0 = 2;
     2f8: f000 f8fa bl 0x4f0                       @ call_4f0(r0, r1, r2);
     2fc: b00c      add sp, #48 @ 0x30            @ sp += 48;     // free 48 bytes on stack
     2fe: bd10      pop {r4, pc}                    @ return;
     300: 0bf0      DCD 0x00000BF0
     302: 0000     
     304: 0314      DCD 0x20000314
     306: 2000     
     308: 09f0      DCD 0x000009f0
     30a: 0000     

Manually decompiled version:
Code: [Select]
void ProcessPacket_057d(uint8_t* packet_ptr) {

     uint8_t keyNumber = packet_ptr[20];
     if (keyNumber >= 16) {
          return;
     }
     uint8_t version = packet_ptr[4];
     if (version != '5' && version != '*') {
          return;
     }
     RAM314_T* ram314 = (RAM314_T*)0x20000314;    // Pointer to ram314
     ram314->IsFlashWriteEnable = 1;              // ram314->u8_1

     uint8_t* keyBase = (uint8_t*)0x09f0;
     uint8_t* keyPtr = keyBase + (keyNumber << 5);

     uint8_t stack[48];
     memcpy(stack + 16, keyPtr, 16);
     memcpy(stack, keyPtr + 16, 16);

     call_4f0(1, stack + 16, stack);
     call_254(stack + 32);
     call_4f0(2, stack + 32, stack);
}

And the function call_4f0() uses the same base address 0x400BD000 as DecryptBlock128():
Code: [Select]
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x4f0 --stop-address=0x534 -Mforce-thumb bootloader.bin > FUN_000004f0.s

@ call_4f0([r0]uint32_t v, [r1]uint32_t* key1, [r2]uint32_t* key2)

000004f0 <.data+0x4f0>:
     4f0: b510      push {r4, lr}
     4f2: 4b0f      ldr r3, [pc, #60] @ (0x530)      @ r3 = 0x400BD000;
     4f4: 681c      ldr r4, [r3, #0]                  @ r4 = *(uint32_t*)&r3[0];
     4f6: 0864      lsrs r4, r4, #1                    @ r4 >>= 1;
     4f8: 0064      lsls r4, r4, #1                    @ r4 <<= 1;
     4fa: 601c      str r4, [r3, #0]                  @ *(uint32_t*)&r3[0] = r4;
     4fc: 00c0      lsls r0, r0, #3                    @ r0 <<= 3;
     4fe: 2420      movs r4, #32                       @ r4 = 32;
     500: 4320      orrs r0, r4                        @ r0 |= r4;    // r0 = (r0 << 3) | 0x20;
     502: 6018      str r0, [r3, #0]                  @ *(uint32_t*)&r3[0] = r0;
     504: 6808      ldr r0, [r1, #0]
     506: 61d8      str r0, [r3, #28]                 @ *(uint32_t*)&r3[28] = *(uint32_t*)&r1[0];
     508: 6848      ldr r0, [r1, #4]
     50a: 6198      str r0, [r3, #24]                 @ *(uint32_t*)&r3[24] = *(uint32_t*)&r1[4];
     50c: 6888      ldr r0, [r1, #8]
     50e: 6158      str r0, [r3, #20]                 @ *(uint32_t*)&r3[20] = *(uint32_t*)&r1[8];
     510: 68c8      ldr r0, [r1, #12]
     512: 6118      str r0, [r3, #16]                 @ *(uint32_t*)&r3[16] = *(uint32_t*)&r1[12];
     514: 6810      ldr r0, [r2, #0]
     516: 62d8      str r0, [r3, #44] @ 0x2c         @ *(uint32_t*)&r3[44] = *(uint32_t*)&r2[0];
     518: 6850      ldr r0, [r2, #4]
     51a: 6298      str r0, [r3, #40] @ 0x28         @ *(uint32_t*)&r3[40] = *(uint32_t*)&r2[4];
     51c: 6890      ldr r0, [r2, #8]
     51e: 6258      str r0, [r3, #36] @ 0x24         @ *(uint32_t*)&r3[36] = *(uint32_t*)&r2[8];
     520: 68d0      ldr r0, [r2, #12]
     522: 6218      str r0, [r3, #32]                 @ *(uint32_t*)&r3[32] = *(uint32_t*)&r2[12];
     524: 6818      ldr r0, [r3, #0]
     526: 2101      movs r1, #1
     528: 4308      orrs r0, r1
     52a: 6018      str r0, [r3, #0]                  @ *(uint32_t*)&r3[0] = *(uint32_t*)&r3[0] | 1;
     52c: bd10      pop {r4, pc}
     52e: 0000
     530: d000           DCD 0x400BD000
     532: 400b

Manually decompiled version:
Code: [Select]
void call_4f0([r0]uint32_t v, [r1]uint32_t* key1, [r2]uint32_t* key2) {

     uint32_t* reg_base = 0x400BD000;
     
     reg_base[0] &= ~1;
     reg_base[0] = (v << 3) | 0x20;
     
     reg_base[7] = key1[0];
     reg_base[6] = key1[1];
     reg_base[5] = key1[2];
     reg_base[4] = key1[3];
     reg_base[11] = key2[0];
     reg_base[10] = key2[1];
     reg_base[9] = key2[2];
     reg_base[8] = key2[3];

     reg_base[0] = reg_base[0] | 1;
}

so it seems like this function setting the decryption key for hardware cryptographic module.

Does anyone know what is the module with base address 0x400BD000? What encryption algorithm it uses? AES 256?
« Last Edit: August 09, 2024, 05:08:29 am by radiolistener »
 
The following users thanked this post: eXisten

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
Yes, this is AES hardware accelerator module:

DP32G030 datasheet: https://github.com/amnemonic/Quansheng_UV-K5_Firmware/raw/main/hardware/DP32G030/DP32G030_Chinese_Datasheet.7z

Now needs to understand how it works...  :)
With datasheet in Chinese language this is not easy  :D


« Last Edit: August 09, 2024, 05:02:27 am by radiolistener »
 
The following users thanked this post: eXisten

Offline Chris79

  • Newbie
  • Posts: 6
  • Country: gb
I've translated it using the Google translate app. I hope this helps.
 
The following users thanked this post: eXisten

Offline eXisten

  • Contributor
  • Posts: 14
  • Country: pl
I translated the entire PDF (DP32G030 datasheet) - it is in parts. Division by page numbers in file names.
If necessary, the conceptual diagrams would have to be translated separately - as Chris79 did. 8)
Will it be useful?

Here is the link to the translated files:

https://megawrzuta.pl/filesgroup/1bd474fe6c9b35ba396863752269c59f.html
« Last Edit: August 09, 2024, 01:29:29 pm by eXisten »
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
Here is reconstructed code for version packet handler and AES_ProcessBlock128 (previous name DecryptBlock128):

Code: [Select]
#define AES_BASE 0x400BD000
#define AES_CR   (*(volatile uint32_t *)(AES_BASE + 0x0000))
#define AES_SR   (*(volatile uint32_t *)(AES_BASE + 0x0004))
#define AES_DINR (*(volatile uint32_t *)(AES_BASE + 0x0008))
#define AES_DOUTR (*(volatile uint32_t *)(AES_BASE + 0x000C))
#define AES_KEYR0 (*(volatile uint32_t *)(AES_BASE + 0x0010))
#define AES_KEYR1 (*(volatile uint32_t *)(AES_BASE + 0x0014))
#define AES_KEYR2 (*(volatile uint32_t *)(AES_BASE + 0x0018))
#define AES_KEYR3 (*(volatile uint32_t *)(AES_BASE + 0x001C))
#define AES_IVR0  (*(volatile uint32_t *)(AES_BASE + 0x0020))
#define AES_IVR1  (*(volatile uint32_t *)(AES_BASE + 0x0024))
#define AES_IVR2  (*(volatile uint32_t *)(AES_BASE + 0x0028))
#define AES_IVR3  (*(volatile uint32_t *)(AES_BASE + 0x002C))

#define AES_CR_EN    (1 << 0)
#define AES_CR_CCFC  (1 << 7)

#define AES_SR_CCF   (1 << 0)

#define AES_CR_CHMOD_ECB (0 << 5)
#define AES_CR_CHMOD_CBC (1 << 5)
#define AES_CR_CHMOD_CTR (2 << 5)

#define AES_MODE_ENCRYPT      0
#define AES_MODE_KEY          1
#define AES_MODE_DECRYPT      2
#define AES_MODE_KEYNDECRYPT  3

typedef struct {
     uint8_t  u8_0;                     // u8  @ offset 0 // state
     uint8_t  IsFlashWriteEnabled;      // u8  @ offset 1
     uint16_t ChunkNumber;              // u16 @ offset 2
     uint16_t ChunkCount;               // u16 @ offset 4
     uint32_t SequenceId;               // u32 @ offset 8
} RAM314_T;


// 0x057d version packet for bootloader v5
// It works the same as 0x0530 version packet for bootloader v2,
// but 0x057d packet has additional byte field to select one of 16 decryption keys hardcoded in bootloader at offset 0x09f0.
// When PC sends this version packet to the radio, the radio initialize AES module with decryption key.
// This key will be used to decrypt firmware content with AES_ProcessBlock128 function.
void ProcessPacket_057d(uint8_t* packet_ptr) {

     uint8_t keyNumber = packet_ptr[20];
     if (keyNumber >= 16) {
          return;
     }
     uint8_t version = packet_ptr[4];
     if (version != '5' && version != '*') {
          return;
     }
     RAM314_T* ram314 = (RAM314_T*)0x20000314;    // Pointer to ram314
     ram314->IsFlashWriteEnable = 1;              // ram314->u8_1

     uint8_t* keyBase = (uint8_t*)0x09f0;          // pointer to keys table (see 0x09f0 offset in bootloader.bin)
     uint8_t* keyPtr = keyBase + (keyNumber << 5);

     uint8_t key[16];
     uint8_t iv[16];
     uint8_t key2[16];
     
     memcpy(key, keyPtr, 16);
     memcpy(iv, (keyPtr + 16), 16);

     AES_Init(AES_MODE_KEY, key, iv);
     AES_GenerateDerivedKey(key2Buf);
     AES_Init(AES_MODE_DECRYPT, key2, iv);
}

void AES_Init(uint32_t mode, uint32_t* key, uint32_t* iv) {
     
     AES_CR &= ~AES_CR_EN;
     AES_CR = (mode << 3) | AES_CR_CHMOD_CBC;
     
     AES_KEYR3 = key[0];
     AES_KEYR2 = key[1];
     AES_KEYR1 = key[2];
     AES_KEYR0 = key[3];
     AES_IVR3 = iv[0];
     AES_IVR2 = iv[1];
     AES_IVR1 = iv[2];
     AES_IVR0 = iv[3];

     AES_CR |= AES_CR_EN;
}

void AES_GenerateDerivedKey(uint32_t* buffer) {

     while ((AES_SR & AES_SR_CCF)==0) { }

     buffer[0] = AES_KEYR3;
     buffer[1] = AES_KEYR2;
     buffer[2] = AES_KEYR1;
     buffer[3] = AES_KEYR0;
     
     AES_CR |= AES_CR_CCFC;
}

void AES_ProcessBlock128(uint32_t* src, uint32_t* dst) {

     AES_DINR = src[0];
     AES_DINR = src[1];
     AES_DINR = src[2];
     AES_DINR = src[3];
     
     while ((AES_SR & AES_SR_CCF)==0) { }
     
     dst[0] = AES_DOUTR;
     dst[1] = AES_DOUTR;
     dst[2] = AES_DOUTR;
     dst[3] = AES_DOUTR;
     
     AES_CR |= AES_CR_CCFC;
}
« Last Edit: August 09, 2024, 09:17:18 am by radiolistener »
 
The following users thanked this post: eXisten

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
Unfortunately I'm not sure how AES KEY_DERIVATION mode works on DP32G030.
I need to know how it works in order to generate exactly the same key.

Probably it just encode empty buffer with zeros with key and IV, but I'm not sure...

Can someone help to find the information?

Maybe someone can run the test code with OpenOCD?

Here is the test code:
Code: [Select]
void testCode() {
     uint8_t key[16];
     uint8_t iv[16];
     uint8_t key2[16];
     for (var i=0; i < 16; i++) {
          key[i] = i;
          iv[i] = i^2;
     }
     AES_Init(1, key, iv);
     AES_GenerateDerivedKey(key2);
     
     for(;;);  // stop here and dump key2 content
}

void AES_Init(uint32_t mode, uint32_t* key, uint32_t* iv) {
     AES_CR &= ~AES_CR_EN;
     AES_CR = (mode << 3) | AES_CR_CHMOD_CBC;
     AES_KEYR3 = key[0];
     AES_KEYR2 = key[1];
     AES_KEYR1 = key[2];
     AES_KEYR0 = key[3];
     AES_IVR3 = iv[0];
     AES_IVR2 = iv[1];
     AES_IVR1 = iv[2];
     AES_IVR0 = iv[3];
     AES_CR |= AES_CR_EN;
}

void AES_GenerateDerivedKey(uint32_t* buffer) {
     while ((AES_SR & AES_SR_CCF)==0) { }
     buffer[0] = AES_KEYR3;
     buffer[1] = AES_KEYR2;
     buffer[2] = AES_KEYR1;
     buffer[3] = AES_KEYR0;
     AES_CR |= AES_CR_CCFC;
}
« Last Edit: August 09, 2024, 11:32:41 am by radiolistener »
 

Offline Tunas1337

  • Newbie
  • Posts: 3
  • Country: mk
Hi radiolistener,

I made an account to reply. Check 0x9e0 (XOR key, unchanged), 9f0, and I think a00. The latter two are AES IV and key, I can't remember which was which. From my analysis, AES is not used, only the command IDs got switched. 578, 57b and 57d.
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
From my analysis, AES is not used, only the command IDs got switched. 578, 57b and 57d.

This is strange, here is my analysis of procedure ProcessPacket_057b at offset 0x338:
Code: [Select]
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x338 --stop-address=0x464 -Mforce-thumb bootloader.bin

typedef struct {
     uint8_t  u8_0;                     // u8  @ offset 0 // state
     uint8_t  IsFlashWriteEnabled;      // u8  @ offset 1
     uint16_t ChunkNumber;              // u16 @ offset 2
     uint16_t ChunkCount;               // u16 @ offset 4
     uint32_t SequenceId;               // u32 @ offset 8
} RAM314_T;


@ // WriteFlashPageReq
@ ProcessPacket_057b([r0]uint8_t* packet_ptr)

00000338 <.data+0x338>:
     338: b5f8      push {r3, r4, r5, r6, r7, lr}
     33a: 4c46      ldr r4, [pc, #280] @ (0x454)      @ r4 = (uint8_t*)0x20000314;  // ram0314 pointer
     33c: 4605      mov r5, r0                        @ r5 = r0;                    // r5=packet_ptr;
     33e: 7860      ldrb r0, [r4, #1]                  @ r0 = *(uint8_t*)&r4[1];     // r0=ram0314->IsFlashWriteEnabled;
     340: 2801      cmp r0, #1
     342: d005      beq.n 0x350                    @ if (!ram0314->IsFlashWriteEnabled) { SendPacket_057c(0, 0, 1); return; }
     344: 2100      movs r1, #0
     346: 2201      movs r2, #1
     348: 4608      mov r0, r1
     34a: f000 f899 bl 0x480
     34e: bdf8      pop {r3, r4, r5, r6, r7, pc}
     350: 7a68      ldrb r0, [r5, #9]                  @ r0 = (uint8_t)packet_ptr[9];   // r0=packet_ptr->chunkNumber.hi;
     352: 7a29      ldrb r1, [r5, #8]                  @ r1 = (uint8_t)packet_ptr[8];   // r1=packet_ptr->chunkNumber.lo;
     354: 0200      lsls r0, r0, #8                    @ r0 <<= 8;
     356: 4308      orrs r0, r1                        @ r0 |= r1;                      // r0=packet_ptr->chunkNumber;
     358: d01c      beq.n 0x394                    @ if (r0 == 0) goto 0x394;
     35a: 7820      ldrb r0, [r4, #0]                  @ r0 = *(uint8_t*)&r4[0];        // r0 = ram0314->u8_0;
     35c: 2802      cmp r0, #2
     35e: d1f6      bne.n 0x34e                    @ if (r0 != 2) return;
     360: 483d      ldr r0, [pc, #244] @ (0x458)      @ r0 = (uint8_t*)0x40061000;     // ???watchdog reg???
     362: 6801      ldr r1, [r0, #0]                  @ r1 = *(uint32_t*)r0;
     364: 2208      movs r2, #8                        @ r2 = 8;
     366: 4051      eors r1, r2                        @ r1 ^= r2;
     368: 6001      str r1, [r0, #0]                  @ *(uint32_t*)r0 = r1;
     36a: 7a68      ldrb r0, [r5, #9]                  @ r0 = (uint8_t)packet_ptr[9];// r0=packet_ptr->chunkNumber.hi;
     36c: 7a29      ldrb r1, [r5, #8]                  @ r1 = (uint8_t)packet_ptr[8];// r1=packet_ptr->chunkNumber.lo;
     36e: 0200      lsls r0, r0, #8                    @ r0 <<= 8;
     370: 4308      orrs r0, r1                        @ r0 |= r1;                   // r0=packet_ptr->chunkNumber;
     372: 8861      ldrh r1, [r4, #2]                  @ r1 = &(uint16_t*)&r4[2];    // r1 = ram0314->ChunkNumber;
     374: 1c49      adds r1, r1, #1                    @ r1 += 1;
     376: 4288      cmp r0, r1
     378: d105      bne.n 0x386                    @ if (r0 != r1) goto 0x386;
     37a: 1d28      adds r0, r5, #4                    @ r0 = r5+4;
     37c: f7ff ff0e bl 0x19c                         @ r0 = ReadUInt32LE(packet_ptr+4); // r0 = packet_ptr->sequenceId;
     380: 68a1      ldr r1, [r4, #8]                  @ r1 = *(uint32_t*)&r4[8];         // r1 = ram0314->SequenceId;
     382: 4288      cmp r0, r1
     384: d021      beq.n 0x3ca                    @ if (r0 == r1) goto 0x3ca;
     386: 2100      movs r1, #0
     388: 2201      movs r2, #1
     38a: 4608      mov r0, r1
     38c: f000 f878 bl 0x480                         @ SendPacket_057c(0, 0, 1);
     390: 2001      movs r0, #1                        @ *(uint8_t*)&r4[0] = 1;           // ram0314->u8_0 = 1;
     392: e05d      b.n 0x450                         @ return;
                                                            @
     394: 1d28      adds r0, r5, #4                    @ r0 = r5 + 4;
     396: f7ff ff01 bl 0x19c                         @ r0 = ReadUInt32LE(packet_ptr+4); // r0 = packet_ptr->sequenceId;
     39a: 60a0      str r0, [r4, #8]                  @ *(uint32_t*)&r4[8] = r0;    // ram0314->SequenceId = packet_ptr->sequenceId;
     39c: 7ae8      ldrb r0, [r5, #11]                 @ r0 = (byte)packet_ptr[11];  // r0=packet_ptr->chunkCount.hi;
     39e: 7aa9      ldrb r1, [r5, #10]                 @ r1 = (byte)packet_ptr[10];  // r0=packet_ptr->chunkCount.lo;
     3a0: 0200      lsls r0, r0, #8                    @ r0 <<= 8;
     3a2: 4308      orrs r0, r1                        @ r0 |= r1;                   // r0 = packet_ptr->chunkCount;
     3a4: 80a0      strh r0, [r4, #4]                  @ *(uint16_t*)&r4[4] = r0;    // ram0314->ChunkCount = packet_ptr->chunkCount;
     3a6: 2000      movs r0, #0
     3a8: 8060      strh r0, [r4, #2]                  @ *(uint16_t*)&r4[2] = 0;     // ram0314->ChunkNumber = 0;
     3aa: 2002      movs r0, #2
     3ac: 7020      strb r0, [r4, #0]                  @ *(uint8_t*)&r4[0] = 2;      // ram0314->u8_0 = 2;
     3ae: 2778      movs r7, #120 @ 0x78              @ r7 = 120
     3b0: b672      cpsid i                        @ __disable_irq();
     3b2: 2600      movs r6, #0                        @ r6 = 0;
                                                            @ do {
     3b4: 2001      movs r0, #1                        @    r0 = 1;
     3b6: 0271      lsls r1, r6, #9                    @    r1 = r6 << 9;  // r1 = r6 * 0x200;
     3b8: 0300      lsls r0, r0, #12                   @    r0 <<= 12;     // r0 = 1<<12 = 0x1000;
     3ba: 1808      adds r0, r1, r0                    @    r0 = r1 + r0;
     3bc: f7ff ff10 bl 0x1e0                         @    call_thumb_200000dc(0x1000 + r6 * 0x200); // erase flash page???
     3c0: 1c76      adds r6, r6, #1                    @    r6 += 1;
     3c2: b2b6      uxth r6, r6                        @    r6 = (uint16_t)r6;
     3c4: 42be      cmp r6, r7
     3c6: d3f5      bcc.n 0x3b4                    @ } while (r6 < r7)
     3c8: b662      cpsie i                        @ __enable_irq();
     3ca: 22ff      movs r2, #255 @ 0xff              @ r2 = 255;
     3cc: 4629      mov r1, r5                        @ r1 = r5;
     3ce: 3201      adds r2, #1                        @ r2 += 1;
     3d0: 3110      adds r1, #16                       @ r1 += 16;
     3d2: 4822      ldr r0, [pc, #136] @ (0x45c)      @ r0 = (uint8_t*)0x20001124;
     3d4: f7ff febe bl 0x154                         @ memcpy(0x20001124, packet_ptr->flashData, 0x100);
     3d8: 4920      ldr r1, [pc, #128] @ (0x45c)      @ r1 = (uint8_t*)0x20001124;
     3da: 2000      movs r0, #0                        @ r0 = 0;
                                                            @ do {
     3dc: 0082      lsls r2, r0, #2                    @    r2 = r0 << 2;
     3de: 588b      ldr r3, [r1, r2]                  @    r3 = *(uint32_t*)&r1[r2];
     3e0: 1c40      adds r0, r0, #1                    @    r0 += 1;
     3e2: ba1b      rev r3, r3                        @    r3 = bswap_32(r3);
     3e4: b280      uxth r0, r0                        @    r0 = (uint16_t)r0;
     3e6: 508b      str r3, [r1, r2]                  @    *(uint32_t*)&r1[r2] = r3
     3e8: 2840      cmp r0, #64 @ 0x40
     3ea: d3f7      bcc.n 0x3dc                    @ } while (r0 < 64);
     3ec: 4f1c      ldr r7, [pc, #112] @ (0x460)      @ r7 = 0x20000F24;
     3ee: 2600      movs r6, #0                        @ r6 = 0;
                                                            @ do {
     3f0: 0130      lsls r0, r6, #4                    @    r0 = r6 << 4;
     3f2: 4a1a      ldr r2, [pc, #104] @ (0x45c)      @    r2 = 0x20001124;
     3f4: 19c1      adds r1, r0, r7                    @    r1 = r0 + r7;
     3f6: 1880      adds r0, r0, r2                    @    r0 = r0 + r2;
     3f8: f7ff ff10 bl 0x21c                         @    AES_ProcessBlock128(0x20001124 + r6*16, 0x20000F24 + r6*16);
     3fc: 1c76      adds r6, r6, #1                    @    r6 += 1;
     3fe: b2b6      uxth r6, r6                        @    r6 = (uint16_t)r6;
     400: 2e10      cmp r6, #16
     402: d3f5      bcc.n 0x3f0                    @ } while (r6 < 16);
     404: 2000      movs r0, #0                        @ r0 = 0;
                                                            @ do {
     406: 0082      lsls r2, r0, #2                    @    r2 = r0 << 2;
     408: 58b9      ldr r1, [r7, r2]                  @    r1 = *(uint32_t*)&r7[r2];
     40a: 1c40      adds r0, r0, #1                    @    r0 += 1;
     40c: ba09      rev r1, r1                        @    r1 = bswap_32(r1);
     40e: b280      uxth r0, r0                        @    r0 = (uint16_t)r0;
     410: 50b9      str r1, [r7, r2]                  @    *(uint32_t*)&r7[r2] = r1;
     412: 2840      cmp r0, #64 @ 0x40
     414: d3f7      bcc.n 0x406                    @ } while (r0 < 64);
     416: b672      cpsid i                        @ __disable_irq();
     418: 7a68      ldrb r0, [r5, #9]                  @ r0 = (uint8_t)packet_ptr[9];// r0=packet_ptr->chunkNumber.hi;
     41a: 7a29      ldrb r1, [r5, #8]                  @ r1 = (uint8_t)packet_ptr[8];// r1=packet_ptr->chunkNumber.lo;
     41c: 0200      lsls r0, r0, #8                    @ r0 <<= 8;
     41e: 4308      orrs r0, r1                        @ r0 |= r1;                   // r0=packet_ptr->chunkNumber;
     420: 0201      lsls r1, r0, #8                    @ r1 = r0 << 8;               // r1=packet_ptr->chunkNumber * 0x100;
     422: 2001      movs r0, #1                        @ r0 = 1;
     424: 0300      lsls r0, r0, #12                   @ r0 <<= 12;
     426: 1808      adds r0, r1, r0                    @ r0 = r1 + r0;
     428: 2240      movs r2, #64 @ 0x40              @ r2 = 64;
     42a: 490d      ldr r1, [pc, #52] @ (0x460)      @ r1 = 0x20000F24;
     42c: f7ff fede bl 0x1ec                         @ call_thumb_200001bc(0x1000 + packet_ptr->chunkNumber * 0x100, 0x20000F24, 64);  // write flash page???
     430: b662      cpsie i                        @ __enable_irq();
     432: 7a68      ldrb r0, [r5, #9]                  @ r0 = (uint8_t)packet_ptr[9];// r0=packet_ptr->chunkNumber.hi;
     434: 7a2a      ldrb r2, [r5, #8]                  @ r2 = (uint8_t)packet_ptr[8];// r1=packet_ptr->chunkNumber.lo;
     436: 0201      lsls r1, r0, #8                    @ r1 = r0 << 8;
     438: 4311      orrs r1, r2                        @ r1 |= r2;                   // r1=packet_ptr->chunkNumber;
     43a: 8061      strh r1, [r4, #2]                  @ *(uint16_t*)&r4[2] = r1;    // ram0314->ChunkNumber = packet_ptr->chunkNumber;
     43c: 2200      movs r2, #0                        @ r2 = 0;
     43e: 68a0      ldr r0, [r4, #8]                  @ r0 = *(uint32_t*)&r4[8];    // r0 = ram0314->SequenceId;
     440: f000 f81e bl 0x480                         @ SendPacket_057c(ram0314->SequenceId, packet_ptr->chunkNumber, 0)
     444: 8860      ldrh r0, [r4, #2]                  @ r0 = *(uint16_t*)&r4[2];    // r0 = ram0314->ChunkNumber;
     446: 88a1      ldrh r1, [r4, #4]                  @ r1 = *(uint16_t*)&r4[4];    // r1 = ram0314->ChunkCount;
     448: 1c40      adds r0, r0, #1                    @ r0 += 1;
     44a: 4288      cmp r0, r1
     44c: d187      bne.n 0x35e                    @ if (r0 != r1) return;
     44e: 2003      movs r0, #3                        @ r0 = 3;
     450: 7020      strb r0, [r4, #0]                  @ *(uint8_t*)&r4[0] = r0;     // ram0314->u8_0 = 3;
     452: bdf8      pop {r3, r4, r5, r6, r7, pc}      @ return;
     454: 0314      DCD 0x20000314
     456: 2000
     458: 1000      DCD 0x40061000
     45a: 4006
     45c: 1124      DCD 0x20001124
     45e: 2000
     460: 0f24      DCD 0x20000F24
     462: 2000

look at this fragment:
Code: [Select]
// copy flash data from packet to RAM@0x20001124
     3d2: 4822      ldr r0, [pc, #136] @ (0x45c)      @ r0 = (uint8_t*)0x20001124;
     3d4: f7ff febe bl 0x154                         @ memcpy(0x20001124, packet_ptr->flashData, 0x100);
     3d8: 4920      ldr r1, [pc, #128] @ (0x45c)      @ r1 = (uint8_t*)0x20001124;
     3da: 2000      movs r0, #0                        @ r0 = 0;

// reverse bytes (bswap_32) for every u32 word at RAM@0x20001124
                                                            @ do {
     3dc: 0082      lsls r2, r0, #2                    @    r2 = r0 << 2;
     3de: 588b      ldr r3, [r1, r2]                  @    r3 = *(uint32_t*)&r1[r2];
     3e0: 1c40      adds r0, r0, #1                    @    r0 += 1;
     3e2: ba1b      rev r3, r3                        @    r3 = bswap_32(r3);
     3e4: b280      uxth r0, r0                        @    r0 = (uint16_t)r0;
     3e6: 508b      str r3, [r1, r2]                  @    *(uint32_t*)&r1[r2] = r3
     3e8: 2840      cmp r0, #64 @ 0x40
     3ea: d3f7      bcc.n 0x3dc                    @ } while (r0 < 64);
     3ec: 4f1c      ldr r7, [pc, #112] @ (0x460)      @ r7 = 0x20000F24;
     3ee: 2600      movs r6, #0                        @ r6 = 0;

// AES decrypt with 128 bit (16 bytes) blocks from RAM@0x20001124 to RAM@0x20000F24
                                                            @ do {
     3f0: 0130      lsls r0, r6, #4                    @    r0 = r6 << 4;
     3f2: 4a1a      ldr r2, [pc, #104] @ (0x45c)      @    r2 = 0x20001124;
     3f4: 19c1      adds r1, r0, r7                    @    r1 = r0 + r7;
     3f6: 1880      adds r0, r0, r2                    @    r0 = r0 + r2;
     3f8: f7ff ff10 bl 0x21c                         @    AES_ProcessBlock128(0x20001124 + r6*16, 0x20000F24 + r6*16);
     3fc: 1c76      adds r6, r6, #1                    @    r6 += 1;
     3fe: b2b6      uxth r6, r6                        @    r6 = (uint16_t)r6;
     400: 2e10      cmp r6, #16
     402: d3f5      bcc.n 0x3f0                    @ } while (r6 < 16);
     404: 2000      movs r0, #0                        @ r0 = 0;

// reverse bytes for every u32 word at RAM@0x20000F24
                                                            @ do {
     406: 0082      lsls r2, r0, #2                    @    r2 = r0 << 2;
     408: 58b9      ldr r1, [r7, r2]                  @    r1 = *(uint32_t*)&r7[r2];
     40a: 1c40      adds r0, r0, #1                    @    r0 += 1;
     40c: ba09      rev r1, r1                        @    r1 = bswap_32(r1);
     40e: b280      uxth r0, r0                        @    r0 = (uint16_t)r0;
     410: 50b9      str r1, [r7, r2]                  @    *(uint32_t*)&r7[r2] = r1;
     412: 2840      cmp r0, #64 @ 0x40
     414: d3f7      bcc.n 0x406                    @ } while (r0 < 64);

// flash write decrypted data from RAM@0x20000F24
     416: b672      cpsid i                        @ __disable_irq();
     418: 7a68      ldrb r0, [r5, #9]                  @ r0 = (uint8_t)packet_ptr[9];// r0=packet_ptr->chunkNumber.hi;
     41a: 7a29      ldrb r1, [r5, #8]                  @ r1 = (uint8_t)packet_ptr[8];// r1=packet_ptr->chunkNumber.lo;
     41c: 0200      lsls r0, r0, #8                    @ r0 <<= 8;
     41e: 4308      orrs r0, r1                        @ r0 |= r1;                   // r0=packet_ptr->chunkNumber;
     420: 0201      lsls r1, r0, #8                    @ r1 = r0 << 8;               // r1=packet_ptr->chunkNumber * 0x100;
     422: 2001      movs r0, #1                        @ r0 = 1;
     424: 0300      lsls r0, r0, #12                   @ r0 <<= 12;
     426: 1808      adds r0, r1, r0                    @ r0 = r1 + r0;
     428: 2240      movs r2, #64 @ 0x40              @ r2 = 64;
     42a: 490d      ldr r1, [pc, #52] @ (0x460)      @ r1 = 0x20000F24;
     42c: f7ff fede bl 0x1ec                         @ call_thumb_200001bc(0x1000 + packet_ptr->chunkNumber * 0x100, 0x20000F24, 64);  // write flash page???
     430: b662      cpsie i                        @ __enable_irq();

As you can see it definitely uses AES decryption...

Did you tried it?

I suspect it will write broken firmware image if you upload it with a new packet 0x057b with no AES encryption. So the radio won't boot.

There is need to encrypt flash image with AES CBC. But we need to generate the key in the same way as DP32G030 doing it. But I'm not sure - what happens when you initialize AES module with key and IV in mode 1 (keygen)?

Does it just encrypt a buffer with zeros or how does it get the key?

I need test result for known key and IV to understand what it doing exactly.

Maybe there is an answer in the DP32G030 datasheet, but it is in Chinese, so it's hard to find something...


I'm ready to make test version of k5tool to upload firmware with using AES encryption on bootloader 5, but I'm not sure if my assumption is correct that DP32G030 AES module just encrypts zeros to generate new key. If my assumption is incorrect, it may generate wrong key and brick the radio.

This is why it will be great if someone can upload test code under debugger and dump the key result for known key, iv pair. It will helps to test the code before testing it on the real radio.

Unfortunately I don't have UV-K5 with bootloader 5 and asking someone with version 5 to try upload with test code may lead to brick with high probability and asking to test different code changes may take too long time. So, I want to make sure that I see all things, before propose to test upload firmware on v5.
« Last Edit: August 09, 2024, 10:53:51 pm by radiolistener »
 

Offline eXisten

  • Contributor
  • Posts: 14
  • Country: pl
Quote
Maybe there is an answer in the DP32G030 datasheet, but it is in Chinese, so it's hard to find something...

Take a look 5 posts above - it might be useful...?  ;)
« Last Edit: August 09, 2024, 01:30:31 pm by eXisten »
 

Offline zrq

  • Frequent Contributor
  • **
  • Posts: 338
  • Country: 00
AES_CR: AES control register
AES_SR: AES state register
AES_DINR: AES data input register
AES_DOUTR: AES data output register
AES_KEYR?: AES key register ?
AES_IVR?: AES IV register?

Let me know if there is anything else I can help as a native speaker. I can translate the section 5.25 of the datasheet in the weekend if it helps.

Yes, this is AES hardware accelerator module:

DP32G030 datasheet: https://github.com/amnemonic/Quansheng_UV-K5_Firmware/raw/main/hardware/DP32G030/DP32G030_Chinese_Datasheet.7z

Now needs to understand how it works...  :)
With datasheet in Chinese language this is not easy  :D
« Last Edit: August 09, 2024, 09:52:11 pm by zrq »
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
Take a look 5 posts above - it might be useful...?  ;)

Thanks, what I'm looking for is "Key derivation" procedure. Unfortunately the datasheet just explain the sequence on how to do it with hardware AES module. This sequence is already reconstructed from the code.

The question is how hardware AES module generates the key (generation algorithm).
In order to generate it on PC, I need to know how it works in the hardware. But unfortunately the datasheet don't explain this...
There are different AES KDF (Key Derivation Function) exists, but they all are not a part of AES standard and it's unknown which one is used in DP32G030 hardware AES module.
So, this is where I stuck.

In other words, here is the code which generates key on hardware AES module:
Code: [Select]
     uint8_t key[16] = ...;
     uint8_t iv[16] = ...;
     AES_Init(1, key, iv);
     uint8_t key2[16];
     AES_GenerateDerivedKey(key2);

Original (key, iv) pair is known. But I don't know how to generate key2 on PC, because this is done on DP32G030 hardware AES module and I don't have ability to test it.

If you can run this code on DP32G030 and share some test vectors (input key,iv and output key2 content), it will help a lot.

The functions source code is here:
Code: [Select]
#define AES_BASE 0x400BD000
#define AES_CR   (*(volatile uint32_t *)(AES_BASE + 0x0000))
#define AES_SR   (*(volatile uint32_t *)(AES_BASE + 0x0004))
#define AES_KEYR0 (*(volatile uint32_t *)(AES_BASE + 0x0010))
#define AES_KEYR1 (*(volatile uint32_t *)(AES_BASE + 0x0014))
#define AES_KEYR2 (*(volatile uint32_t *)(AES_BASE + 0x0018))
#define AES_KEYR3 (*(volatile uint32_t *)(AES_BASE + 0x001C))
#define AES_IVR0  (*(volatile uint32_t *)(AES_BASE + 0x0020))
#define AES_IVR1  (*(volatile uint32_t *)(AES_BASE + 0x0024))
#define AES_IVR2  (*(volatile uint32_t *)(AES_BASE + 0x0028))
#define AES_IVR3  (*(volatile uint32_t *)(AES_BASE + 0x002C))

#define AES_CR_EN    (1 << 0)
#define AES_CR_CHMOD_CBC (1 << 5)
#define AES_CR_CCFC  (1 << 7)
#define AES_SR_CCF   (1 << 0)


void AES_Init(uint32_t mode, uint32_t* key, uint32_t* iv) {
     AES_CR &= ~AES_CR_EN;
     AES_CR = (mode << 3) | AES_CR_CHMOD_CBC;
     AES_KEYR3 = key[0];
     AES_KEYR2 = key[1];
     AES_KEYR1 = key[2];
     AES_KEYR0 = key[3];
     AES_IVR3 = iv[0];
     AES_IVR2 = iv[1];
     AES_IVR1 = iv[2];
     AES_IVR0 = iv[3];
     AES_CR |= AES_CR_EN;
}

void AES_GenerateDerivedKey(uint32_t* buffer) {
     while ((AES_SR & AES_SR_CCF)==0) { }
     buffer[0] = AES_KEYR3;
     buffer[1] = AES_KEYR2;
     buffer[2] = AES_KEYR1;
     buffer[3] = AES_KEYR0;
     AES_CR |= AES_CR_CCFC;
}

Unfortunately I don't have radio with bootloader 5 to test it. And don't have configured environment to use in-circuit debugging to run the test code and get test vector with known (key,iv) pair and generated key result, it can help to implement proper encryption. I bought second UV-K5 for experiments, but got broken one with damaged serial port, now things are going worse and I don't have ability to buy a new one.

PS: if someone tried to update firmware with a new packet id=0x057b but without AES encrypted image and the radio is bricked. Let me know, I can provide you with several encrypted firmware images for testing, it can help to unbrick the radio and helps to get proper way to generate AES key.

Also if someone has a dump with bootloader v2, please share it. Since I have radio with bootloader v2, I'm interesting to play with its bootloader.
« Last Edit: August 10, 2024, 12:06:30 am by radiolistener »
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
Also if someone has access to in-circuit debugging and if it will be hard to reconstruct the algorithm of key generation used in hardware, we can get pre-computed keys for (key,iv) pairs in bootloader 5 and use pre-computed keys on PC to encrypt the firmware image. There is needs access to DP32G030 debugger to get pre-computed keys.
« Last Edit: August 10, 2024, 12:10:03 am by radiolistener »
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
In short, currently there is need the dump of key2 for this code executed on DP32G030:
Code: [Select]
uint8_t key[16] = { 0xe1,0x6e,0x0d,0x29,0xe0,0xc8,0x34,0x18,0x98,0x7f,0x94,0x33,0xf5,0xff,0x62,0x0e }; // key 00
uint8_t  iv[16] = { 0x14,0xb7,0xa2,0xbe,0x02,0x23,0xe2,0x59,0xb2,0x06,0x6d,0x88,0x86,0x97,0x7e,0x36 }; //  iv 00
AES_Init(1, key, iv);
uint8_t key2[16];
AES_KDF(key2);

// ***dump of key2 needed***


// ===functions code===
#define AES_BASE 0x400BD000
#define AES_CR   (*(volatile uint32_t *)(AES_BASE + 0x0000))
#define AES_SR   (*(volatile uint32_t *)(AES_BASE + 0x0004))
#define AES_KEYR0 (*(volatile uint32_t *)(AES_BASE + 0x0010))
#define AES_KEYR1 (*(volatile uint32_t *)(AES_BASE + 0x0014))
#define AES_KEYR2 (*(volatile uint32_t *)(AES_BASE + 0x0018))
#define AES_KEYR3 (*(volatile uint32_t *)(AES_BASE + 0x001C))
#define AES_IVR0  (*(volatile uint32_t *)(AES_BASE + 0x0020))
#define AES_IVR1  (*(volatile uint32_t *)(AES_BASE + 0x0024))
#define AES_IVR2  (*(volatile uint32_t *)(AES_BASE + 0x0028))
#define AES_IVR3  (*(volatile uint32_t *)(AES_BASE + 0x002C))

#define AES_CR_EN    (1 << 0)
#define AES_CR_CHMOD_CBC (1 << 5)
#define AES_CR_CCFC  (1 << 7)
#define AES_SR_CCF   (1 << 0)

void AES_Init(uint32_t mode, uint32_t* key, uint32_t* iv) {
     AES_CR &= ~AES_CR_EN;
     AES_CR = (mode << 3) | AES_CR_CHMOD_CBC;
     AES_KEYR3 = key[0];
     AES_KEYR2 = key[1];
     AES_KEYR1 = key[2];
     AES_KEYR0 = key[3];
     AES_IVR3 = iv[0];
     AES_IVR2 = iv[1];
     AES_IVR1 = iv[2];
     AES_IVR0 = iv[3];
     AES_CR |= AES_CR_EN;
}

void AES_KDF(uint32_t* buffer) {
     while ((AES_SR & AES_SR_CCF)==0) { }
     buffer[0] = AES_KEYR3;
     buffer[1] = AES_KEYR2;
     buffer[2] = AES_KEYR1;
     buffer[3] = AES_KEYR0;
     AES_CR |= AES_CR_CCFC;
}

This dump is pre-computed key for keyNumber=0, which can be used to encrypt firmware for bootloader v5.
It allows to implement upload firmware with bootloader v5.

Can someone help to obtain that dump?
« Last Edit: August 10, 2024, 01:19:15 am by radiolistener »
 

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru
As an option. Rewrite the bootloader code to output keys and upload it to the station. I haven't heard of a debugger for dp32g030. We can upload a corrected boot to the station if necessary. And the bootv2 dump is freely available on the Internet.
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
it can be executed as a part of custom firmware and print 16 hex values on the display, but I don't have working environment to compile it and test. So, if someone working on custom firmware it will not be so hard to test it.

Getting this pre-computed key allows to use existing bootloader v5.

Here is updated test code, I added calculation for decrypt test vector, I need dump of these key2 and data array content, it allows to test all things offline.

Code: [Select]
uint8_t key[16] = { 0xe1,0x6e,0x0d,0x29,0xe0,0xc8,0x34,0x18,0x98,0x7f,0x94,0x33,0xf5,0xff,0x62,0x0e }; // key 00
uint8_t  iv[16] = { 0x14,0xb7,0xa2,0xbe,0x02,0x23,0xe2,0x59,0xb2,0x06,0x6d,0x88,0x86,0x97,0x7e,0x36 }; //  iv 00

AES_Init(1, key, iv);
uint8_t key2[16];
AES_KDF(key2);

// *** dump 16 bytes from key2 array ***

AES_Init(2, key2, iv);
uint8_t data[16];
AES_ProcessBlock128(key, data);

// ***dump 16 bytes from data array***

// ===functions code===
#define AES_BASE 0x400BD000
#define AES_CR   (*(volatile uint32_t *)(AES_BASE + 0x0000))
#define AES_SR   (*(volatile uint32_t *)(AES_BASE + 0x0004))
#define AES_DINR (*(volatile uint32_t *)(AES_BASE + 0x0008))
#define AES_DOUTR (*(volatile uint32_t *)(AES_BASE + 0x000C))
#define AES_KEYR0 (*(volatile uint32_t *)(AES_BASE + 0x0010))
#define AES_KEYR1 (*(volatile uint32_t *)(AES_BASE + 0x0014))
#define AES_KEYR2 (*(volatile uint32_t *)(AES_BASE + 0x0018))
#define AES_KEYR3 (*(volatile uint32_t *)(AES_BASE + 0x001C))
#define AES_IVR0  (*(volatile uint32_t *)(AES_BASE + 0x0020))
#define AES_IVR1  (*(volatile uint32_t *)(AES_BASE + 0x0024))
#define AES_IVR2  (*(volatile uint32_t *)(AES_BASE + 0x0028))
#define AES_IVR3  (*(volatile uint32_t *)(AES_BASE + 0x002C))

#define AES_CR_EN    (1 << 0)
#define AES_CR_CHMOD_CBC (1 << 5)
#define AES_CR_CCFC  (1 << 7)
#define AES_SR_CCF   (1 << 0)

void AES_Init(uint32_t mode, uint32_t* key, uint32_t* iv) {
     AES_CR &= ~AES_CR_EN;
     AES_CR = (mode << 3) | AES_CR_CHMOD_CBC;
     AES_KEYR3 = key[0];
     AES_KEYR2 = key[1];
     AES_KEYR1 = key[2];
     AES_KEYR0 = key[3];
     AES_IVR3 = iv[0];
     AES_IVR2 = iv[1];
     AES_IVR1 = iv[2];
     AES_IVR0 = iv[3];
     AES_CR |= AES_CR_EN;
}

void AES_KDF(uint32_t* buffer) {
     while ((AES_SR & AES_SR_CCF)==0) { }
     buffer[0] = AES_KEYR3;
     buffer[1] = AES_KEYR2;
     buffer[2] = AES_KEYR1;
     buffer[3] = AES_KEYR0;
     AES_CR |= AES_CR_CCFC;
}
void AES_ProcessBlock128(uint32_t* src, uint32_t* dst) {
     AES_DINR = src[0];
     AES_DINR = src[1];
     AES_DINR = src[2];
     AES_DINR = src[3];
     while ((AES_SR & AES_SR_CCF)==0) { }
     dst[0] = AES_DOUTR;
     dst[1] = AES_DOUTR;
     dst[2] = AES_DOUTR;
     dst[3] = AES_DOUTR;
     AES_CR |= AES_CR_CCFC;
}

PS: The new K5TOOL code with support for bootloader v5 protocol is almost complete. I will commit it soon on github.
The only thing that needs to be implemented is to add pre-computed key and test it on decrypt test vector to make sure that all things works the same as on MCU.
« Last Edit: August 10, 2024, 06:59:15 am by radiolistener »
 
The following users thanked this post: eXisten

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
And the bootv2 dump is freely available on the Internet.

Just found. I have no idea why I didn't seen it before, because this site in my favorite list and I downloaded firmware from it  :D

Now I'm going to play with bootloader v2  :)
« Last Edit: August 10, 2024, 08:23:47 am by radiolistener »
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
Just compared bootloader v2 code with bootloader v5 and can confirm my previous analysis is correct, there are 2 significant difference:
1) setVersion packet for v5 has additional code to initialize AES key
2) writeFlash packet for v5 has additional code which apply AES decrypt before flashing the data

As I said before, I stuck with key generation algorithm which is used in bootloader v5, it uses KDF function of hardware AES module. There is need to find the algorithm which is used for KDF in DP32G030 AES module. Alternative solution is to use pre-computed key. Both ways need to run a test code on the MCU (see code in my previous messages).It can be done on any UV-K5 radio with OpenOCD or with custom made firmware.

Currently I have no ready to use environment with custom firmware and didn't tried to use SWD. If someone can help to run the test code on DP32G030 and get the result dump it helps to add support for bootloader v5 in flasher tools.

I have ST-LINKv2, can someone help with info on proper connect to SWD pins of UV-K5 and configs for DP32G030?

I found some message on internet that pin 2=SWDIO and pin 3=SWCLK, is this correct?

 

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru
Yeah 1-3.3.v 2-io 3-clk 4-gnd
 
The following users thanked this post: radiolistener

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru

As I understand, to run this code through Openocd, need to convert it to the Openocd config file format. And there the code differs from standard C.
« Last Edit: August 13, 2024, 11:31:05 am by ruslik064 »
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
As I understand, to run this code through Openocd, need to convert it to the Openocd config file format.

I didn't worked with openocd yet, so I cannot help with it.
Technically I have st-linkv2 which I using for stm32 and I have uv-k5, so I can do it myself, but it needs time to get all things together and configure it.

And there the code differs from standard C.

the test code should be compiled with C compiler with no issue. Which line is problematic?
« Last Edit: August 13, 2024, 12:03:07 pm by radiolistener »
 

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru
I haven't tried compiling the code. I meant that as I see it, the essence of the code is pure work with the controller registers. This can be done through openocd using its commands. Only the format of the configuration file is a little unclear to me in the area of ​​describing variables. For example, look at the openocd config for uvk5 on github. There are some commands describing the algorithm for recording, for example, a microcontroller.
 

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru
I made a test stand with openocd and a connected board.
 

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 4054
  • Country: ua
I haven't tried compiling the code. I meant that as I see it, the essence of the code is pure work with the controller registers. This can be done through openocd using its commands.

technically yes you can try to do it from debugger with no code compilation, but I'm not sure if it can properly process loading accelerator registers.

Only the format of the configuration file is a little unclear to me in the area of ​​describing variables. For example, look at the openocd config for uvk5 on github. There are some commands describing the algorithm for recording, for example, a microcontroller.

Can you give the link?
 

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru
« Last Edit: August 13, 2024, 12:18:39 pm by ruslik064 »
 

Offline ruslik064

  • Contributor
  • Posts: 30
  • Country: ru
It seems to me that there is some initialization of the aes module in the bootloader code. Because the aes registers simply do not respond via debug. In particular, the work with the 0x40000000 register is interesting. It seems to me that something similar is at offset 0x628. I am bad at decompiling. Therefore, this is only an assumption.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf