import crypto, { ECDHKeyFormat } from 'crypto' import ed2Curve from 'ed2curve' import nacl from 'tweetnacl' import pkg from 'tweetnacl-util' import { throwNewError } from '../errors' import { EciesCurveType } from './asymmetricModels' // tweetnacl-util is a commonjs module so need to be imported this way for named imports to work const { encodeBase64 } = pkg function assertPublicKeyBufferLengthValid(publicKey: Buffer, functionName: string) { if (!(publicKey.length === 65 || publicKey.length === 33)) { throwNewError(`${functionName}: publicKey buffer must be 65 bytes (uncompressed) or 33 bytes (compressed)`) } } /** Generates Ephemeral Keypair and sharedSecret - used for ECC curves and some others supported by Node crypto curveType */ export function generateEphemPublicKeyAndSharedSecretType1( publicKey: NodeJS.ArrayBufferView, curveType: EciesCurveType, keyFormat: ECDHKeyFormat, ) { assertPublicKeyBufferLengthValid(publicKey as Buffer, 'generateEphemPublicKeyAndSharedSecretType1') const ecdh = crypto.createECDH(curveType) const encodedEphemPublicKey = ecdh.generateKeys(null, keyFormat) const sharedSecret = ecdh.computeSecret(publicKey) return { ephemPublicKey: encodedEphemPublicKey, sharedSecret } } /** Generates sharedSecret using ephemPublicKey */ export function generateSharedSecretType1( publicKey: NodeJS.ArrayBufferView, secretKey: NodeJS.ArrayBufferView, curveType: EciesCurveType, ) { assertPublicKeyBufferLengthValid(publicKey as Buffer, 'generateSharedSecretType1') const ecdh = crypto.createECDH(curveType) ecdh.setPrivateKey(secretKey) const sharedSecret = ecdh.computeSecret(publicKey) return sharedSecret } /** Generated Ephemeral PublicKey and SharedSecret * The ed25519PublicKey parameter must be 32 byte encoded as a Uint8Array */ export function generateEphemPublicKeyAndSharedSecretEd25519(ed25519PublicKey: Uint8Array) { const { publicKey: ephemPublicKey, secretKey } = nacl.box.keyPair() const encodedEphemPublicKey = encodeBase64(ephemPublicKey) // **IMPORTANT**: algosdk only uses nacl module for generating signature // Thus it uses nacl.sign.keyPair() to generate ed25519 pk/sk // ed25519 keys are only for signature operation. // To use it with DiffieHellman encryption we need to convert keypair to curve25519 // Then we can use converted key to generate secret via nacl.box // https://stackoverflow.com/questions/26954215/how-to-use-ed25519-to-encrypt-decrypt-data const curve25519PublicKey = ed2Curve.convertPublicKey(ed25519PublicKey) const sharedSecret = nacl.box.before(curve25519PublicKey, secretKey) return { ephemPublicKey: encodedEphemPublicKey, sharedSecret } } /** Generates sharedSecret using ephemeral PublicKey and ed25519 SecretKey * The ed25519PublicKey parameter must be 32 byte encoded as a Uint8Array */ export function generateSharedSecretEd25519(ephemPublicKey: Uint8Array, ed25519SecretKey: Uint8Array) { const curve25519SecretKey = ed2Curve.convertSecretKey(ed25519SecretKey) // Check IMPORTANT comment in generateEphemPublicKeyAndSharedSecret() const sharedSecret = nacl.box.before(ephemPublicKey, curve25519SecretKey) return sharedSecret }