GOOGLE ADS

jueves, 21 de abril de 2022

Curve 25519 Clave simétrica con Python

Estoy usando CryptoKit de Apple para crear claves para una aplicación de iOS, cifrar los datos y luego enviarlos al backend a través de JSON y almacenarlos en una base de datos PGSQL.

Si bien todo eso funciona perfectamente, necesito poder descifrar los datos del backend y, por lo tanto, necesito poder crear la misma clave simétrica que usé para cifrar los datos.

Cuando creé las claves a través de Swift, se hizo de la siguiente manera:

let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
let sharedSecret = try! privateKey.sharedSecretFromKeyAgreement(with: publicKey)
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(using: SHA512.self,
salt: "\(vhvioerhvoreovjreoivgifjtughrygryrufejewdf))".data(using:.utf8)!,
sharedInfo: Data(),
outputByteCount: 32)

Nota: La sal es solo yo escribiendo un montón de caracteres aleatorios para este código de ejemplo, pero entiendes la idea.

Necesito lograr lo mismo usando Python. Las claves son cadenas codificadas en base64 enviadas a través de JSON al back-end también, por lo que necesito hacer una decodificación b64 en ellas (que ya tengo funcionando).

Intenté usar pynacl pero no puedo entender cómo crear la clave simétrica, usando sha512 y la misma sal que usé para crear la clave simétrica en Swift. Tampoco estoy vinculado de ninguna manera a pynacl si hay una mejor opción.

Nuevamente, el código anterior es solo un ejemplo del proceso utilizado para crear una clave privada, pública y simétrica en Swift. De hecho, estoy usando una clave pública de una cuenta, con una clave privada de otra para crear la clave simétrica y cifrar los datos. Haré lo mismo a la inversa para descifrar (es decir, una clave pública de la cuenta de clave privada y una clave privada de la cuenta de clave pública).

Hasta ahora, jugando con él en python, tengo lo siguiente (pero nuevamente, no estoy atado a pynacl si hay una mejor solución):

from nacl.public import Box, PrivateKey, PublicKey, SealedBox
from nacl.hash import sha512
import nacl.encoding
from base64 import b64decode, b64encode
import binascii
privKeyRead = b64decode('uBruInrnbtrberverv6XZZqQDDeS4SwORSHriW04=')
private_key = PrivateKey(privKeyRead)
pubKeyRead = b64decode('UIZSc3QBfewojfoewjgowjgCqA/P8PjXDlQwU7rTHFBw=')
public_key = PublicKey(pubKeyRead)
salt = b64decode('HIHIUGUBLJOIHIBIOHO9nM0NSSkNDejUwcXJMNUdlUT0=')
cryptoBox = Box(private_key, public_key)
sharedSecret = Box.shared_key(cryptoBox)
# Print Keys and Salt
print("Private Key:", privKeyRead)
print("Public Key:", pubKeyRead)
print("Salt:", salt)
print("Crypto Box:", cryptoBox)
print("Shared Secret:", sharedSecret)
return printed

Nota: Los valores privKeyRead, pubKeyRead y salt tienen caracteres basura para este ejemplo, ya que obviamente no puedo publicar los valores reales. También me di cuenta de que tanto Crypto Box como Shared Secret son idénticos, por lo que estoy bastante seguro de que solo necesitaría uno y no ambos.

Por último, y no quiero que nadie se obsesione con esto, estoy usando Zope5 y es por eso que ves mi ejemplo de python tal como es. Este es un script de python en zope que es perfectamente válido. También puedo crear métodos externos, por lo que si tiene funciones, etc. que funcionarán mejor, no dude en publicar exactamente cómo lo haría donde Zope no está en la ecuación. Voy a reconfigurar para zope si es necesario.


Solución del problema

El código Swift genera una clave privada, determina la clave pública relacionada, obtiene un secreto compartido usando X25519 y deriva la clave simétrica usando HKDF:

import Foundation
import Crypto
let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
let sharedSecret = try! privateKey.sharedSecretFromKeyAgreement(with: publicKey)
let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey(using: SHA512.self,
salt: "a test salt".data(using:.utf8)!,
sharedInfo: Data(),
outputByteCount: 32)
print("Private key: ", privateKey.rawRepresentation.base64EncodedString())
print("Public key: ", publicKey.rawRepresentation.base64EncodedString())
print("Shared secret: ", sharedSecret.withUnsafeBytes {return Data(Array($0)).base64EncodedString()})
print("Symmetric key: ", symmetricKey.withUnsafeBytes {return Data(Array($0)).base64EncodedString()})

Una salida posible es:

Private key: 8AtqpW6UJBhAEzTnvHMQ8ki28TrDvAEbKuV3FDiROWw=
Public key: kICzRWQYcawmlVJpSJ2TINuUDmI0xGm2BnH10qHVxxs=
Shared secret: ijACBaZfaCQuvwwILb5uncYroZ4MLBzOfNZLD5khjz4=
Symmetric key: Il570rzDZ9brWBtxUtWv/Hv29rN6pBls7/AtOK+2oPU=

Para la implementación en Python, se necesita una biblioteca que admita X25519 y HKDF, por ejemplo, criptografía (la criptografía parece ser la mejor opción aquí en comparación con PyNaCl, ya que este último no es compatible con HKDF):

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import base64
# X25519
private_key = X25519PrivateKey.generate()
public_key = private_key.public_key()
shared_secret = private_key.exchange(public_key)
# HKDF
symmetric_key = HKDF(
algorithm=hashes.SHA512(),
length=32,
salt='a test salt'.encode('utf-8'),
info=b'',
).derive(shared_secret)
print(base64.b64encode(shared_secret))
print(base64.b64encode(symmetric_key))

Prueba:
para la prueba, use las claves del código Swift, que se pueden importar de la siguiente manera:

from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
...
private_key = X25519PrivateKey.from_private_bytes(base64.b64decode("8AtqpW6UJBhAEzTnvHMQ8ki28TrDvAEbKuV3FDiROWw="))
public_key = X25519PublicKey.from_public_bytes(base64.b64decode("kICzRWQYcawmlVJpSJ2TINuUDmI0xGm2BnH10qHVxxs="))

Con esto, el código Python proporciona el secreto compartido y la clave del código Swift.

No hay comentarios:

Publicar un comentario

Regla de Firestore para acceder a la generación de subcolección Permisos faltantes o insuficientes

Tengo problemas con las reglas de Firestore para permitir el acceso a algunos recursos en una subcolección. Tengo algunos requests document...