Tengo una clave compartida de la que necesito derivar un iv para poder descifrarla.
Los documentos del chat empresarial de Apple indican:
Genere la clave derivada y el vector inicial Ejecute la clave compartida a través de la función de derivación de clave X9.63 con la función hash SHA256. Esto da como resultado una carga útil de 48 bytes. Sus resultados deberían ser rV3qrszd0PMPgeRhNnlOYA==
Esto es lo que probé. Usé las funciones criptográficas scryptSync y pbkdf2Sync con muchas configuraciones 'saladas'. No estoy seguro si estas son las funciones correctas para este trabajo.
const crypto = require('crypto'); const keyLength = 48; // sharedKey is a base64 string const sharedKey = "2lvSJsBO2keUHRfvPG6C1RMUmGpuDbdgNrZ9YD7RYnvAcfgq/fjeYr1p0hWABeif"; // publicKey is a base64 string const publicKey = "BDiRKNnPiPUb5oala31nkmCaXMB0iyWy3Q93p6fN7vPxEQSUlFVsInkJzPBBqmW1FUIY1KBA3BQb3W3Qv4akZ8kblqbmvupE/EJzPKbROZFBNvxpvVOHHgO2qadmHAjHSg==" const key1 = crypto.scryptSync(sharedKey, 'salt', keyLength); console.log(key2.toString('base64')); const key2 = crypto.pbkdf2Sync(sharedKey, 'salt', 10000, keyLength, 'sha256'); console.log(key2.toString('base64')); // results should be: // mAzkYatDlz4SzrCyM23NhgL/+mE3eGgfUz9h1CFPhZM= // iv: rV3qrszd0PMPgeRhNnlOYA==A continuación se muestra el código de muestra de Apple para derivar la clave y el vector inicial con una función de derivación de clave X9.63.
def ITOSP(self, longint, length): """ITOSP, short for Integer-to-Octet-String Primitive, converts a non-negative integer to an octet string of a specified length. This particular function is defined in the PKCS #1 v2.1: RSA Cryptography Standard (June 14, 2002) https://www.cryptrec.go.jp/cryptrec_03_spec_cypherlist_files/PDF/pkcs-1v2-12.pdf""" hex_string = "%X" % longint assert len(hex_string) <= 2 * length, "ITOSP function: Insufficient length for encoding" return binascii.a2b_hex(hex_string.zfill(2 * length)) def KDFX963(self, inbyte_x, shared_data, key_length, hashfunct=sha256, hash_len=32): """KDFX963 is a key derivation function (KDF) that takes as input byte sequence inbyte_x and additional shared data shared_data and outputs a byte sequence key of length key_length. This function is defined in ANSI-X9.63-KDF, and this particular flavor of KDF is known as X9.63. You can read more about it from: http://www.secg.org/sec1-v2.pdf""" assert key_length >= 0, "KDFX963 function: key_length should be positive integer" k = key_length / float(hash_len) k = int(ceil(k)) acc_str = "" for i in range(1, k+1): h = hashfunct() h.update(inbyte_x) h.update(self.ITOSP(i, 4)) h.update(shared_data) acc_str = acc_str + h.hexdigest() return acc_str[:key_length * 2]X9.63 KDF es una función de derivación clave, descrita, por ejemplo, aquí y aquí . scrypt y PBKDF2 también son KDF, pero diferentes, por lo que, por supuesto, no se puede reproducir el resultado esperado con ellos.
Por lo tanto, necesita una biblioteca NodeJS que admita X.963 KDF. Si no puede encontrar uno, también puede implementar el suyo propio.
X9.63 KDF espera un secreto compartido y una información compartida y determina una clave grande de tamaño de clave de la siguiente manera:
De manera más formal, incluidas varias comprobaciones, el algoritmo se describe en los enlaces anteriores. El código Python publicado más adelante en la pregunta también implementa esta lógica.
Una posible implementación de NodeJS (omitiendo las comprobaciones de la especificación) es:
var crypto = require('crypto'); var digest = 'sha256'; var digestLen = 32; function X963KDF(sharedSecret, sharedInfo, keySize){ var maxCount = Math.ceil(keySize/digestLen); var result = Buffer.allocUnsafe(0); for (var count = 1; count < maxCount + 1; count++){ var counter = Buffer.allocUnsafe(4); counter.writeUInt32BE(count, 0); var current = Buffer.concat([sharedSecret, counter, sharedInfo]); var hash = crypto.createHash(digest).update(current).digest(); result = Buffer.concat([result, hash]); } return result.slice(0, keySize); }Prueba:
En la pregunta se publica el secreto compartido, pero no la información compartida. Una búsqueda en Internet revela que el problema publicado se describe, por ejemplo, aquí , por lo que también se puede determinar la información compartida (sin embargo, sería mejor si agrega esta información a su pregunta):
var sharedSecret = Buffer.from('2lvSJsBO2keUHRfvPG6C1RMUmGpuDbdgNrZ9YD7RYnvAcfgq/fjeYr1p0hWABeif', 'base64') var sharedInfo = Buffer.from('04389128d9cf88f51be686a56b7d6792609a5cc0748b25b2dd0f77a7a7cdeef3f111049494556c227909ccf041aa65b5154218d4a040dc141bdd6dd0bf86a467c91b96a6e6beea44fc42733ca6d139914136fc69bd53871e03b6a9a7661c08c74a', 'hex'); var keyiv = X963KDF(sharedSecret, sharedInfo, 48); var key = keyiv.slice(0,32).toString('base64'); var iv = keyiv.slice(32, 48).toString('base64'); console.log("Key: ", key); // Key: mAzkYatDlz4SzrCyM23NhgL/+mE3eGgfUz9h1CFPhZM= console.log("IV: ", iv); // IV: rV3qrszd0PMPgeRhNnlOYA==La clave generada y el IV son iguales a los valores esperados.