¿Cómo usamos una lista mnemotécnica de palabras como semilla (como nos hemos acostumbrado a usar billeteras de criptomonedas) para poder recuperar una clave privada en caso de que se pierda, se borre accidentalmente o se atasque en un dispositivo roto?
Esto podría ser útil para el cifrado e2e entre clientes: se supone que las claves se generan en el cliente, solo la clave pública se compartirá con el servidor.
Los usuarios podrían regenerar las claves sin conexión cuando sea necesario, siempre que puedan proporcionar el mnemotécnico nuevamente, obviamente para almacenarlas de forma segura y sin conexión.
La semilla mnemotécnica debe ser lo suficientemente larga para proporcionar una cantidad segura de entropía.
Algunas preguntas y respuestas parecen estar muy desactualizadas : ¿cómo podemos lograr esto en Javascript/Typescript, posiblemente usando bibliotecas mantenidas?
bip39 y node-forgePara citar esta respuesta que me ha guiado para lograr esta solución:
en este escenario, la clave pública resultante es, por naturaleza, pública y, por lo tanto, puede servir para ataques de diccionario fuera de línea. El atacante solo tiene que probar posibles contraseñas hasta encontrar la misma clave pública. Eso es intrínseco a lo que quieres lograr.
Podemos suponer que 128 bits de entropía deberían ser suficientes para prevenir este tipo de ataques, en el futuro previsible, sin embargo, afortunadamente podemos decidir qué tan fuerte será nuestra mnemotécnica.
En primer lugar, podemos generar un mnemotécnico utilizando la implementación bip-39 JS.
import { generateMnemonic } from "bip39"; const mnemonic = generateMnemonic(256) // 256 to be on the _really safe_ side. Default is 128 bit. console.log(mnemonic) // prints 24 words Ahora podemos usar node-forge para generar nuestras claves. La función pki.rsa.generateKeyPair acepta una función de generador de números pseudoaleatorios en la entrada. El objetivo es lograr que esta función NO calcule un número pseudoaleatorio (esto ya no sería determinista ), sino que devuelva un valor calculado a partir del mnemotécnico .
import { mnemonicToSeed } from "bip39"; import { pki, random } from "node-forge"; const seed = (await mnemonicToSeed(mnemonic)).toString('hex') const prng = random.createInstance(); prng.seedFileSync = () => seed Ahora podemos alimentar la función generateKeyPair con nuestro prng "amañado":
const { privateKey, publicKey } = pki.rsa.generateKeyPair({ bits: 4096, prng, workers: 2 })Ahora tenemos claves RSA seguras y deterministas, generadas directamente en el cliente y restaurables con el mismo mnemónico que una entrada. Considere los riesgos que implica el uso de claves deterministas y asegúrese de que sus usuarios NO almacenen el mnemotécnico en línea o en cualquier otro lugar en su cliente (generalmente, se sugiere escribirlo en papel y guardarlo en un lugar seguro).