Search code examples
cryptographyreverse-engineeringelliptic-curvenode-cryptosecp256k1

How to generate derive shared secret key from public key


I am reverse engineering an application. What I noticed is that in a part of this application, a request is made to the server and a key is received from the server in base64 format. Then it converts the same key into hex format and then derives from it and creates another key named shared secret key. Then, it uses this key for symmetric encryption operations in the AES method. I want to know what the algorithm of this derivation is like and how I can get from the main key string to the derived key string, which is called shared secret key.

main key base64 receive from server : BEdGgIH9PLFU2BcF41Uqvn38inO3bBHbUwKcAIJRBAnf+hfiZo4hWogn8eqVHBCzjdtxmzieFEuRpqi8NRlWH6o=

convert base64 key to hex format : 0447468081fd3cb154d81705e3552abe7dfc8a73b76c11db53029c0082510409dffa17e2668e215a8827f1ea951c10b38ddb719b389e144b91a6a8bc3519561faa

derived key : 7e2f725debc83adafe237b95ed5cf1c9a8b16e25b83848049ecbd4a85b38f000

i found some code of this application in javascript that i trace for generate shared secret key The codes that perform this operation in my desired system (in JavaScript language) are as follows:

let a = '0447468081fd3cb154d81705e3552abe7dfc8a73b76c11db53029c0082510409dffa17e2668e215a8827f1ea951c10b38ddb719b389e144b91a6a8bc3519561faa';
n = d.keyFromPublic(a, "hex");
s = e.derive(n.getPublic());
r = s.toString(16); 
//after run this code r is 7e2f725debc83adafe237b95ed5cf1c9a8b16e25b83848049ecbd4a85b38f000
now r is a hex and use for AES.

Also, the terms "secp256k1" can be seen in the codes, which I think are related to the methods and algorithms of this operation

What exactly do these codes do? How and during what operation is the primary key derived and we reach the second key? Thank you for providing explanations regarding this operation


Solution

  • As already mentioned in a comment, this appears to be an ECDH (elliptic-curve Diffie-Hellman) key exchange.

    In a Diffie-Hellman key exchange, there are two participants: in your case the application and the server. Each participant has a key pair consisting of a private key and the corresponding public key. The participants send each other their public keys, but never reveal their private key. Each participants makes a calculation calc(my_private_key, their_public_key) whose output is a byte string called the shared secret. The remarkable thing about Diffie-Hellman is that although the two participants make different calculations, the output is the same: a mathematical property ensures that calc(application_private_key, server_public_key) (as calculated in the application) gives the same result as calc(server_private_key, application_public_key) (as calculated in the server).

    The participants must agree on a group; for the elliptic curve variant, this group is called a curve. The function calc needs to know this group; it may either be encoded in one or both of the key arguments or passed as a separate parameter. Secp256k1 is a widely supported curve (mostly popular with cryptocurrencies), so it's likely that your application always uses that particular curve.

    For secp256k1, the private key and the shared secret are all 256-bit numbers, which are conventionally represented in little-endian order as 32-byte strings. The public key is a pair of 256-bit numbers. You have the public key encoded as a 65-byte string where the first byte is 0x04; this is the most common encoding for public keys, consisting of the byte 0x04 followed by the two numbers (each encoded as 32 bytes in little-endian format), typically called “uncompressed point” format1.

    You can generate the same shared secret by calling the ECDH function of any cryptographic library, passing it the curve secp256k1, your application's private key and the server's public key. If you need to generate your own private key, the library will have a function for that; again you need an ECC key on the curve secp256k1. If you need to send the corresponding public key to the server, again, the library will have a function to obtain it from the private key object (or will expose the public key as a separate object).

    The shared secret should not be used directly as a symmetric key: it should be passed to a key derivation function. But this is a relatively benign design mistake that many applications make (and it's very unlikely to be practically exploitable). So it's very plausible that your application uses the first 16 bytes of the shared secret directly as an AES key.

    1 Called this way because it's possible to recover these two numbers from one of them and and one extra bit — this is called a “compressed point”. But since you already have an uncompressed point, it's unlikely that you'll have to worry about compressed points.