Editing Kirk

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 20: Line 20:
Both use the usual Weierstrass form.
Both use the usual Weierstrass form.


== Elliptic curve for Kirk commands 1/2/3/0xA ==
== Elliptic curve for Kirk commands 1/2/3/10 ==


This curve is used for the ECDSA verification of Kirk commands 1, 2, 3 and 0xA.
This curve is used for the ECDSA verification of Kirk commands 1, 2, 3 and 0xA.
Line 46: Line 46:
</pre>
</pre>


These commands allow to do operations with any public key. For the latest [[iplloader]] version which adds an additional ECDSA verification of the XOR of the block hashes, the public key which is hardcoded in the iplloader is (0xBC660611A70BD7F2D140A48215C096D11D2D4112, 0xF0E9379AC4E0D387C542D091349DD15169DD5A87).
These commands allow to do operations with any public key. For the latest Pre-IPL version which adds an additional ECDSA verification of the XOR of the block hashes, the public key which is hardcoded in the Pre-IPL is (0xBC660611A70BD7F2D140A48215C096D11D2D4112, 0xF0E9379AC4E0D387C542D091349DD15169DD5A87).


== Code sample ==
== Code sample ==
Line 146: Line 146:
|}
|}


= PSP Individual Keys =
= Per-console keys =


Kirk commands 2, 3, 5, 6, 8, 9, 0x10 and 0x12 use individual (per-console) seeds to generate individual keys. The base per-console seed is the Fuse ID (6 bytes), which is transformed into a 0x30-byte buffer named unofficially "individual key mesh". The PSP individual key mesh is used to generate various final individual keys depending on a seed parameter.
Some Kirk commands like commands 16 and 18 use individual (per-console) seeds. The base per-console seed is the Fuse ID (6 bytes), which is transformed into a 0x30 bytes buffer ("key mesh"). This buffer is used to generate different keys depending on a seed.


== PSP Individual Key Mesh ==
{| class="wikitable"
 
|+
=== Structure ===
!Seed
!Usage
|-
|0
|Kirk commands 2 (encryption) & 3 (decryption) (the real encryption & CMAC keys are random, but this per-console key is used to encrypt them)
|-
|1
|Kirk command 5 (encryption) & 8 (decryption)
|-
|2
|Kirk command 6 (encryption) & 9 (decryption)
|-
|3
|Kirk command 16
|-
|4
|Kirk command 18
|-
|5
|Unused
|-
|6
|RNG buffer reseeding
|}


<source lang="c">
<source lang="c">
typedef struct ScePspIndividualKeyMesh { // size is 0x30
typedef struct ScePspKeyMesh { // size is 0x30
     SceUInt8 derivation_seed_0[0x10]; // a seed used to derive final keys with derivation_key
     SceUInt8 aes128cbc_key_1[0x10]; // used by Kirk commands 5 & 8 and 16
     SceUInt8 derivation_seed_1[0x10]; // a seed used to derive final keys with derivation_key
     SceUInt8 aes128cbc_key_2[0x10]; // used by Kirk command 2 & 3, 6 & 9 and 18
     SceUInt8 derivation_key[0x10]; // AES128 key used to derive final keys from seed_0 and seed_1
     SceUInt8 derivation_key[0x10]; // used to derive the 2 other keys
} ScePspIndividualKeyMesh;
} ScePspKeyMesh;
</source>
</source>


=== Algorithm ===
To generate the key mesh of a PSP, provided the Fuse ID (0xBC100090 and 0xBC100094 hardware registers), execute the following code.
 
To generate the individual key mesh of a specific PSP, provided its [[Fuse ID]], execute the following code.


<source lang="c">
<source lang="c">
void gen_psp_individual_key_mesh(ScePspIndividualKeyMesh *key_mesh) {
void gen_psp_individual_seed() {  
   int i, k;
   int i, k;
  ScePspKeyMesh seed;
   u8 subkey_1[0x10], subkey_2[0x10];
   u8 subkey_1[0x10], subkey_2[0x10];
   rijndael_ctx aes_ctx;
   rijndael_ctx aes_ctx;
   u8 fuse_id[8];
   u8 fuseid[8];
    
    
   // Byte-reverse the Fuse ID
   // Byte-reverse the Fuse ID
   u32 g_fuse90 = *(u32 *)0xBC100090;
   u32 g_fuse90 = *(u32 *)0xBC100090;
   u32 g_fuse94 = *(u32 *)0xBC100094;
   u32 g_fuse94 = *(u32 *)0xBC100094;
   fuse_id[7] = g_fuse90 &0xFF;
   fuseid[7] = g_fuse90 &0xFF;
   fuse_id[6] = (g_fuse90>>8) &0xFF;
   fuseid[6] = (g_fuse90>>8) &0xFF;
   fuse_id[5] = (g_fuse90>>16) &0xFF;
   fuseid[5] = (g_fuse90>>16) &0xFF;
   fuse_id[4] = (g_fuse90>>24) &0xFF;
   fuseid[4] = (g_fuse90>>24) &0xFF;
   fuse_id[3] = g_fuse94 &0xFF;
   fuseid[3] = g_fuse94 &0xFF;
   fuse_id[2] = (g_fuse94>>8) &0xFF;
   fuseid[2] = (g_fuse94>>8) &0xFF;
   fuse_id[1] = (g_fuse94>>16) &0xFF;
   fuseid[1] = (g_fuse94>>16) &0xFF;
   fuse_id[0] = (g_fuse94>>24) &0xFF;
   fuseid[0] = (g_fuse94>>24) &0xFF;
   
   
   rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key
   rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key
    
    
   for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID
   for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID
     subkey_2[i] = subkey_1[i] = fuse_id[i % 8];
     subkey_2[i] = subkey_1[i] = fuseid[i % 8];


   for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times
   for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times
Line 197: Line 219:
   rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key
   rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key


   for (i = 0; i < 3; i++) { // encrypt 3, 6 and 9 times subkey_2 to obtain the final key mesh
   for (i = 0; i < 3; i++) { // encrypt 3, 6 and 9 times the subkey_2 to obtain the final keymesh
     for (k = 0; k < 3; k++)
     for (k = 0; k < 3; k++)
       rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
       rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
     memcpy(key_mesh[i * 0x10], subkey_2, 0x10);
     memcpy(&seed[i * 0x10], subkey_2, 0x10);
   }
   }
}
}
 
</source>The key mesh can then be used along with a seed to generate a key using the following algorithm:<syntaxhighlight lang="c">
typedef struct {
void make_perconsole_key(u8 output[16], int seed, ScePspKeyMesh keymesh)
unsigned char buf1[8]; // 0
{
unsigned char buf2[8]; // 8
     if (seed & 1) {
unsigned char buf3[8]; // 0x10
         memcpy(output, keymesh.aes128cbc_key_2, 16);
} SomeStructure;
     } else {
 
         memcpy(output, keymesh.aes128cbc_key_1, 16);
void gen_psp_individual_key_mesh_official_implementation(SomeStructure *ss, ScePspIndividualKeyMesh *key_mesh) {
    }
  byte bVar1;
     // Encrypt the result several times depending on the seed
  byte *dst;
     rijndael_set_key(&aes_ctx, keymesh.aes128cbc_derivation_key);
  int idx;
     seed = (seed / 2) + 1;
  int j;
     while ((seed--) >= 0) {
  byte *src;
  byte subkey_2[16];
  byte subkey_1[16];
  uint ctx[64];
  uint ctx2[64];
 
  AES_set_encrypt_key_2(g_ids_master_key, 128, ctx); // set g_ids_master_key as AES key
  AES_set_decrypt_key_2(g_ids_master_key, 128, ctx2); // set g_ids_master_key as AES key
 
  idx = 0; // initialize the subkeys using the Fuse ID
  do {
    bVar1 = ss[idx + ((int)(idx + ((uint)(idx >> 0x1f) >> 0x1d)) >> 3) * -8];
    src = subkey_2 + idx;
    dst = subkey_1 + idx;
    idx = idx + 1;
    *src = bVar1;
    *dst = bVar1;
  } while (idx < 0x10);
 
  idx = 2; // encrypt first subkey three times, and decrypt second subkey three times
  do {
    AES_encrypt_2(subkey_1, subkey_1, ctx);
    idx = idx - 1;
    AES_decrypt_2(subkey_2, subkey_2, ctx2);
  } while (-1 < idx);
 
  AES_set_encrypt_key_2(subkey_1, 128, ctx); // set subkey_1 as AES key
 
  idx = 0; // encrypt three times each one of the three first blocks
  do {
    j = 2;
    do {
      j = j - 1;
      AES_encrypt_2(subkey_2, subkey_2, ctx);
    } while (-1 < j);
    dst = key_mesh + idx * 0x10;
    j = 0;
    do {
      src = subkey_2 + j;
      j = j + 1;
      *dst = *src;
      dst = dst + 1;
    } while (j < 0x10);
    idx = idx + 1;
  } while (idx < 3);
}
</source>
 
== Final PSP Individual Keys ==
 
=== Algorithm ===
 
In some Kirk commands, the individual key mesh is used along with a seed parameter to generate a final individual key using the following algorithm.
 
<syntaxhighlight lang="c">
void make_perconsole_key(u8 output[16], int seed_param, ScePspIndividualKeyMesh *key_mesh) {
     if (seed_param & 1)
         memcpy(output, key_mesh->derivation_seed_1, 16);
     else
         memcpy(output, key_mesh->derivation_seed_0, 16);
 
     // Encrypt the result several times depending on the seed parameter
     rijndael_set_key(&aes_ctx, key_mesh->derivation_key);
     seed_param = (seed_param / 2) + 1;
     while ((seed_param--) >= 0) {
         rijndael_encrypt(&aes_ctx, output);
         rijndael_encrypt(&aes_ctx, output);
     }
     }
Line 285: Line 242:
</syntaxhighlight>
</syntaxhighlight>


=== Seed Parameter Per Command ===
== 0x40-byte buffer ==
 
{| class="wikitable"
|+
!Seed parameter
!Usage
|-
|0
|Kirk commands 2 (encryption) & 3 (decryption) (the real encryption and CMAC keys are random, but this per-console key is used to encrypt them)
|-
|1
|Kirk command 5 (encryption) & 8 (decryption)
|-
|2
|Kirk command 6 (encryption) & 9 (decryption)
|-
|3
|Kirk command 16
|-
|4
|Kirk command 18
|-
|5
|Unused
|-
|6
|RNG buffer reseeding
|}
 
== PSP Individual Key Mesh Certificate ==


There exists a PSP Individual Key Mesh Certificate stored in both PSP flashData.prx and in PS Vita cmep keyrings 0x601 and 0x602 (in endian-swapped fashion). It contains the individual key mesh followed by the Fuse ID from which it was generated and ends with a hash.
There is a 0x40-byte buffer, named here <code>ScePspIndividualSeed</code>, used in both PSP flashData.prx and (to be checked) in PS Vita cmep keyrings 0x601 and 0x602. It might be slightly different from the mesh buffer described above, so we keep it for now. Indeed, it is unsure if this 0x40 bytes buffer on PS Vita holds the final AES keys or if it is before applying the derivation_key.


=== Structure ===
Some Kirk commands like commands 16 and 18 use individual (per-console) seeds. The base per-console seed is the Fuse ID (6 bytes), which is transformed into a 0x40 bytes buffer. The first 0x10 bytes of the buffer is the AES CBC MAC key used by Kirk command 18 whilst the second 0x10 bytes are the AES CBC key used by Kirk command 16.


<source lang="C">
<source lang="C">
typedef struct ScePspIndividualKeyMeshCert { // size is 0x40
typedef struct ScePspIndividualSeed { // size is 0x40
     ScePspIndividualKeyMesh key_mesh;
     SceUInt8 aes128cbc_mac_key[0x10]; // used by Kirk command 18
    SceUInt8 aes128cbc_key[0x10]; // used by Kirk command 16
    SceUInt8 derivation_key[0x10]; // used to derive the 2 other keys of the structure
     SceUInt8 fuse_id[8]; // endianness to precise
     SceUInt8 fuse_id[8]; // endianness to precise
     SceUInt8 reserved[4]; // could be arbitrary but in practice always zeroed
     SceUInt32 padding; // usually set to zero
     SceUInt32 hash; // the hash algorithm is in PSP Jig Kick flashData.prx
     SceUInt32 hash; // the hash algorithm is in PSP Jig Kick flashData.prx
} ScePspIndividualKeyMeshCert;
} ScePspIndividualSeed;
</source>
</source>


=== Algorithm ===
To generate ScePspIndividualSeed, provided the Fuse ID (0xBC100090 and 0xBC100094 hardware registers), execute the following code.
 
To generate the ScePspIndividualKeyMeshCert of a specific PSP, provided its [[Fuse ID]], execute the following code.


<source lang="C">
<source lang="C">
void gen_psp_individual_key_mesh_certificate_hash(ScePspIndividualKeyMeshCert *cert) {
void gen_psp_individual_seed() {   
   byte bVar1;
   int i, k;
  uint uVar2;
   ScePspIndividualSeed seed;
   int iVar3;
   u8 subkey_1[0x10], subkey_2[0x10];
   byte *pbVar4;
   rijndael_ctx aes_ctx;
   uint uVar5;
   u8 fuseid[8];
   uint uVar6;
   byte *pbVar7;
  uint uVar8;
  byte bVar9;
  int idx;
  int offset;
  byte *pbVar11;
  byte local_60 [80];
  byte m [16];
  uint uVar10;
    
    
   pbVar11 = local_60;
   // Byte-reverse the Fuse ID
   m[0] = 1;
  u32 g_fuse90 = *(u32 *)0xBC100090;
   m[1] = 0xf;
  u32 g_fuse94 = *(u32 *)0xBC100094;
   m[2] = 0x36;
  fuseid[7] = g_fuse90 &0xFF;
   m[3] = 0x78;
  fuseid[6] = (g_fuse90>>8) &0xFF;
   m[4] = 0x40;
  fuseid[5] = (g_fuse90>>16) &0xFF;
   fuseid[4] = (g_fuse90>>24) &0xFF;
   fuseid[3] = g_fuse94 &0xFF;
   fuseid[2] = (g_fuse94>>8) &0xFF;
   fuseid[1] = (g_fuse94>>16) &0xFF;
   fuseid[0] = (g_fuse94>>24) &0xFF;
  rijndael_set_key(&aes_ctx, ids_master_key, 128); // set ids_master_key as AES key
 
  for (i = 0; i < 0x10; i++) // initialize the subkeys using the Fuse ID
    subkey_2[i] = subkey_1[i] = fuseid[i % 8];


   offset = 0;
   for (i = 0; i < 3; i++) { // encrypt first subkey three times, and decrypt second subkey three times
  do {
     rijndael_encrypt(&aes_ctx, subkey_1, subkey_1);
    pbVar4 = cert + offset;
     rijndael_decrypt(&aes_ctx, subkey_2, subkey_2);
    pbVar7 = local_60 + offset;
   }
     offset = offset + 1;
     *pbVar7 = *pbVar4;
   } while (offset < 0x3c);


   offset = 0x3c;
   rijndael_set_key(&aes_ctx, subkey_1, 128); // set subkey_1 as AES key
  do {
    pbVar7 = local_60 + offset;
    offset = offset + 1;
    *pbVar7 = 0;
  } while (offset < 0x40);


   offset = 0;
   for (i = 0; i < 3; i++) { // encrypt three times each one of the three first blocks
  do {
     for (k = 0; k < 3; k++)
     bVar1 = *pbVar11;
      rijndael_encrypt(&aes_ctx, subkey_2, subkey_2);
    memcpy(&seed[i * 0x10], subkey_2, 0x10);
  }


    idx = 0;
  rijndael_set_key(&aes_ctx, seed.derivation_key, 128); // set the derivation key as AES key
    do {
      uVar8 = (uint)m[idx];
      iVar3 = idx + 0x40;
      uVar10 = 0;
      bVar9 = 0;
      uVar2 = (uint)bVar1;
      while (uVar8 != 0) {
        uVar6 = uVar2 << 1;
        uVar5 = uVar8 & 1;
        uVar8 = (int)uVar8 >> 1;
        if (uVar5 != 0) {
          uVar10 = uVar10 ^ uVar2;
        }
        bVar9 = (byte)uVar10;
        uVar2 = uVar6;
        if ((uVar6 & 0x100) != 0)
          uVar2 = uVar6 ^ 0x11d;
      }
      idx = idx + 1;
      local_60[iVar3] = bVar9;
    } while (idx < 5);


    idx = 0;
   for (i = 0; i < 2; i++) { // encrypt twice the seeds to get the final keys
    do {
     rijndael_encrypt(&aes_ctx, seed.aes128cbc_mac_key, seed.aes128cbc_mac_key);
      pbVar7 = pbVar11 + idx;
    rijndael_encrypt(&aes_ctx, seed.aes128cbc_key, seed.aes128cbc_key);
      iVar3 = idx + 0x40;
  }
      idx = idx + 1;
      *pbVar7 = *pbVar7 ^ local_60[iVar3];
    } while (idx < 5);
 
    idx = offset + 1;
    pbVar11 = local_60 + offset + 1;
    offset = idx;
  } while (idx < 0x3c);
 
  offset = 0x3c;
  do {
    pbVar11 = local_60 + offset;
    pbVar7 = cert + offset;
    offset = offset + 1;
    *pbVar7 = *pbVar11;
    *pbVar11 = 0;
  } while (offset < 0x40);
 
  return;
}
 
void gen_psp_individual_key_mesh_certificate(SomeStructure *ss, byte *data_for_0x38, ScePspIndividualKeyMeshCert *cert) { 
  gen_psp_key_mesh(cert->key_mesh);
 
   for (int idx = 0; idx < 8; idx++)
    cert->fuse_id[idx] = ss[idx];
 
  for (int idx = 0; idx < 4; idx++)
     cert->reserved[idx] = data_for_0x38[idx];
 
  gen_psp_individual_key_mesh_certificate_hash(cert);
 
  return 0;
}
 
typedef struct U64 {
unsigned int low;
unsigned int high;
} U64;
 
int CreateSomeStructure(SomeStructure *ss) {
U64 fuse_id;
int i;
 
memcpy(&fuse_id, &g_fuse_id, 8);
 
memset(ss->buf1, 0, 8);
memset(ss->buf2, 0xFF, 8);
 
memcpy(ss->buf3, &fuse_id.high, 4);
memcpy(ss->buf3+4, &fuse_id.low, 4);
 
for (i = 0; i < 4; i++) {
ss->buf1[3-i] = ss->buf3[i];
ss->buf1[7-i] = ss->buf3[4+i];
}
 
return 0;
}
 
uint gen_psp_individual_seed_helper(ScePspIndividualKeyMeshCert *cert) {
  SomeStructure ss;
  CreateSomeStructure(&ss);
  int data_for_0x38 = 0;
  gen_psp_individual_key_mesh_certificate(&ss, &data_for_0x38, cert)
  return 0;
}
}
</source>
</source>
= Mapping Structure =
<pre>
0xBDE00000 = Kirk Signature
0xBDE00004 = Kirk Version
0xBDE00008 = Kirk Error
0xBDE0000C = Kirk Proc Phase
0xBDE00010 = Kirk CMD Number
0xBDE00014 = Kirk Result
0xBDE00018 = Unknown?
0xBDE0001C = Kirk Status
0xBDE00020 = Kirk Status Asynchronous
0xBDE00024 = Kirk Status Asynchronous End
0xBDE00028 = Kirk Status End
0xBDE0002C = Kirk Source Address
0xBDE00030 = Kirk Destination Address
</pre>


= Commands =
= Commands =
Line 682: Line 500:


== Command 0x0: decrypt kbooti ==
== Command 0x0: decrypt kbooti ==
This command is only used by devkits to decrypt the kbooti, ie the devkit's Bootrom. It supposedly can only be run at a very early stage. The very short header is as follows.
This command is only used by devkits to decrypt the kbooti, ie the devkit's Pre-IPL. It supposedly can only be run at a very early stage. The very short header is as follows.
{| class="wikitable"
{| class="wikitable"
|+
|+
Line 934: Line 752:


== Command 0xF: Seed RNG buffer ==
== Command 0xF: Seed RNG buffer ==
This function seeds the Kirk 32-byte RNG buffer used to generate all the random data coming from Kirk.
This function seeds the Kirk RNG buffer used to generate all the random data coming from Kirk.


It takes as an input and output data of size 0x1c:
It takes as an input and output data of size 0x1c:


* 0x00 - 64-bit counter - increased by 1 in the output
* 0x00 - unknown - modified by an unknown opcode
* 0x08 - seed data (0x14 bytes long) - used for seeding as an input, and contains fresh reseeded data for the output
* 0x04 - counter - increased by 1 in the output
* 0x08 - seed data - used for seeding, and contains fresh reseeded data for the output


Seeding works this way:
Seeding works this way:


# Increment the input counter
# Increase input counter and do unknown operation on offset 0x00
# Set the first 0x14 bytes of the PRNG seed to the input seed data, XOR'ed with a SHA1 of data coming from a true random number generator
# Set the PRNG seed to the input seed data, XOR'ed with a SHA1 of data coming from a true random number generator
# Initialize the 32-byte RNG buffer to two empty words, and two words taken from the input data at offsets 0x00 and 0x04
# Initialize RNG buffer to two empty words, and then output data at offsets 0x00 and 0x04
# Do a reseeding (see below)
# Do a reseeding
# Output the bytes contained in the first 0x14 bytes of the PRNG seed after the reseeding
# Output resulting buffer.


Reseeding is then done by all operations requiring random data and works this way:
Reseeding is then done by all operations requiring random data and works this way:


# Encrypt RNG buffer with AES per-console key with seed 6
# Encrypt RNG buffer with AES per-console key with seed 6
# Set the last half of the PRNG seed (0x14 bytes) to the contents RNG buffer
# Reseed the PRNG with the RNG buffer
# Regenerate data with the PRNG
# Regenerate data with the PRNG
The functions requiring random data then use some parts of the PRNG state ("seed" (first 0x28 bytes of the PRNG state) or "result" (last 0x14 bytes of the PRNG state)) as random data to be used.


== Command 0x10: ECDSA signature generation ==
== Command 0x10: ECDSA signature generation ==
Line 970: Line 788:
== Command 0x11: ECDSA signature verification ==
== Command 0x11: ECDSA signature verification ==


This command verifies an ECDSA signature. It is used to verify IdStorage IDPS certificates.
This command verifies an ECDSA signature.


It takes no output, and takes as an input:
It takes no output, and takes as an input:
Line 981: Line 799:


== Command 0x12: verify certificate ==
== Command 0x12: verify certificate ==
This command verifies an AES-CBC-MAC (OMAC1) signature. It is used to verify [[IDStorage#IDStorage_certified_sections|ID Storage certificates]].


This command has no output.
This command has no output.


It takes as input an [[IDStorage#IDStorage_certified_sections|ID Storage certificate]] read from [[IDStorage]].
It takes as an input a 0xB8-long buffer:
 
*0x00: certificate data (either ConsoleID or OpenPSID) and ECDSA signature etc. (unused here)
<source lang="C">
*0xA8: AES-CMAC hash of the rest of the header.
typedef struct kirk_command_0x12_input{
It verifies the AES CMAC of the header using per-console key with seed 4.
ids_cert_psp certificate;
} kirk_command_0x12_input;
</source>
 
It uses per-console key with seed 4.


= Error codes =
= Error codes =
Please note that all contributions to PSP Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PSP Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)