Deriving a user id ("tripcode") from a user-supplied password.
The algorithm that is used to derive a public key looks as follows:
import CryptoJS from "crypto-js"; // https://github.com/brix/crypto-js
import scrypt from "scrypt-js"; // https://github.com/ricmoo/scrypt-js
import { ethers } from "ethers"; // https://github.com/ethers-io/ethers.js/
// Derive a 32-byte key to be used as private key material.
const derivePrivateKey = async (password: string) => {
const hashHex = CryptoJS.SHA256(password).toString();
const hash = hexToBytes(hashHex);
const salt = stringToBytes("somachat");
const N = 4 * 1024;
const r = 8;
const p = 1;
const keyLen = 32;
const key = bytesToHex(await scrypt.scrypt(hash, salt, N, r, p, keyLen));
return key;
};
// Derive an uncompressed (65-byte) public secp256k1 key.
const derivePublicKey = async (password: string) => {
const key = await derivePrivateKey(password);
const wallet = new ethers.Wallet(key);
const { publicKey } = wallet;
return publicKey;
}
The resulting publicKey is the 65 byte long public part of a secp256k1 keypair. The public key is sent in this format to the server, where it is compressed to its 33-byte form (which is possible due to secp256k1's symmetry around the x-axis – more here) and encodes the result in base58.
package main
import (
"encoding/hex"
"github.com/btcsuite/btcd/btcutil/base58"
"github.com/ethereum/go-ethereum/crypto"
)
// Convert a 65-byte hex representation prefixed by 0x into
// a base-58 representation of the 33-byte compressed public key.
func toUserId(pubKeyHex string) (string, error) {
b, err := hex.DecodeString(pubKeyHex[2:])
if err != nil {
return "", err
}
pubkey, err := crypto.UnmarshalPubkey(b)
if err != nil {
return "", err
}
keyBytes := crypto.CompressPubkey(pubkey)
key := base58.Encode(keyBytes)
return key, nil
}
The original 65-byte public key can be extracted from the base-58 representation.
The base-58 representation of the public key is either 44 or 45 bytes long. For display purposes, the client shows only the first 7 characters prefixed by an exclamation mark, but the full representation can be accessed by clicking on a user or a user's message and copying the "user id".