Esquema de cifrado integrado de curva elíptica (ECIES): cifrado mediante curvas elípticas

May 01 2023
Con ECC (criptografía de curva elíptica), tenemos la oportunidad de utilizar tanto el poder del cifrado de clave pública como la velocidad y la seguridad del cifrado de clave simétrica. Y, entonces, avanzamos lentamente hacia las mejores prácticas para el cifrado, donde existe un consenso cada vez mayor: construyamos un método de cifrado híbrido con Golang.
https://unsplash.com/photos/DlnK1KOREds

Con ECC (criptografía de curva elíptica), tenemos la oportunidad de utilizar tanto el poder del cifrado de clave pública como la velocidad y la seguridad del cifrado de clave simétrica. Y, entonces, avanzamos lentamente hacia las mejores prácticas para el cifrado, donde existe un consenso cada vez mayor en torno a:

  • Intercambio de claves de cifrado de clave pública: ECDH (P256), ECDH (P384), ECDH (P521), X25519 y X448.
  • Cifrado de clave pública: RSA y ECIES.
  • Método hash para derivación de claves (HKDF): SHA256, SHA384 y SHA512.
  • Clave simétrica: AES GCM de 128 bits y AES GCM de 256 bits.

Así que construyamos un método de cifrado híbrido con Golang.

Los basicos

Ahora, digamos que Bob enviará un mensaje encriptado a Alice. Luego, Alice generará un par de claves (una clave pública y una clave privada). Luego envía su clave pública a Bob, y él la usa para derivar una clave simétrica para el cifrado ( S ). Luego encripta el mensaje usando K y con AES GCM. Bob recibe el cifrado (C) y un valor de R. A partir de R , puede derivar S de su clave privada. Con esta clave, puede descifrar el texto cifrado para derivar el mensaje de texto sin formato.

Para lograr esto en RSA, Alice crearía una clave simétrica aleatoria (KA) y luego la cifraría con la clave pública de Bob y luego cifraría un archivo con KA. Luego enviaría la clave cifrada (Epk(KA)) a Bob, y el archivo cifrado, y luego él descifraría la clave cifrada para revelar KA, y luego podría descifrar el archivo:

Pero los métodos de curva elíptica no encriptan los datos de forma natural, así que veamos cómo se hace esto con estos métodos.

Teoría

En este método, Alice genera una clave privada aleatoria ( dA ) y luego toma un punto en una curva elíptica ( G ) y determina su clave pública ( QA ):

QA = dA × G

G y QA son, por tanto, puntos en una curva elíptica. Alice luego envía control de calidad a Bob. A continuación, Bob generará:

R = r × G

S = r × QA

y donde r es un número aleatorio (nonce) generado por Bob. La clave simétrica (S) se usa luego para cifrar un mensaje.

Alice recibirá entonces el mensaje cifrado, junto con R. Luego puede determinar la misma clave de cifrado con:

S = dA × R

cual es:

S = dA ×( r × G )

S = r × ( dA × G )

S = r × QA

y que es la misma que la clave que generó Bob. Una vez que tiene S, ahora tiene la clave simétrica que ha cifrado el archivo y luego puede descifrarlo. El método se ilustra aquí:

Ejecución de muestra

Una ejecución de muestra es:

Public key type:	HPKE_KEM_P256_HKDF_SHA256
 Params	kem_id: 16 kdf_id: 1 aead_id: 1
Key exchange parameters:
 Ciphersize: 65
 EncapsulationSeedSize: 32
 PrivateKeySize: 32
 PublicKeySize: 65
 SeedSize: 32
 SharedKeySize: 32
Cipher parameters:
 Key Length: 16
Key derivation function:
 Extract size: 32
Message: Testing 123
Cipher: 343363373461353461343437666463613435653437383435666537653264666334306331623239653439303333646236646334323663
Decipher: Testing 123

package main
import (
 "crypto/rand"
 "encoding/hex"
 "fmt"
 "os"
 "strconv"
 "github.com/cloudflare/circl/hpke"
)
func main() {
 kemID := int(hpke.KEM_P256_HKDF_SHA256)
 kdfID := int(hpke.KDF_HKDF_SHA256)
 aeadID := int(hpke.AEAD_AES128GCM)
 msg := "Hello"
 argCount := len(os.Args[1:])
 if argCount > 0 {
  msg = os.Args[1]
 }
 if argCount > 1 {
  kemID, _ = strconv.Atoi(os.Args[2])
 }
 if argCount > 2 {
  kdfID, _ = strconv.Atoi(os.Args[3])
 }
 if argCount > 3 {
  aeadID, _ = strconv.Atoi(os.Args[4])
 }
 suite := hpke.NewSuite(hpke.KEM(kemID), hpke.KDF(kdfID), hpke.AEAD(aeadID))
 info := []byte("Test")
 Bob_pub, Bob_private, _ := hpke.KEM(kemID).Scheme().GenerateKeyPair()
 Bob, _ := suite.NewReceiver(Bob_private, info)
 Alice, _ := suite.NewSender(Bob_pub, info)
 enc, sealer, _ := Alice.Setup(rand.Reader)
 Alice_msg := []byte(msg)
 aad := []byte("Additional data")
 ct, _ := sealer.Seal(Alice_msg, aad)
 opener, _ := Bob.Setup(enc)
 Bob_msg, _ := opener.Open(ct, aad)
 fmt.Printf("Public key type:\t%s\n", Bob_pub.Scheme().Name())
 fmt.Printf(" Params\t%s\n", suite.String())
 fmt.Printf("Key exchange parameters:\n")
 fmt.Printf(" Ciphersize:\t%d\n", hpke.KEM(kemID).Scheme().CiphertextSize())
 fmt.Printf(" EncapsulationSeedSize:\t%d\n", hpke.KEM(kemID).Scheme().EncapsulationSeedSize())
 fmt.Printf(" PrivateKeySize:\t%d\n", hpke.KEM(kemID).Scheme().PrivateKeySize())
 fmt.Printf(" PublicKeySize:\t%d\n", hpke.KEM(kemID).Scheme().PublicKeySize())
 fmt.Printf(" SeedSize:\t%d\n", hpke.KEM(kemID).Scheme().SeedSize())
 fmt.Printf(" SharedKeySize:\t%d\n", hpke.KEM(kemID).Scheme().SharedKeySize())
 fmt.Printf("Cipher parameters:\n")
 fmt.Printf(" Key Length:\t%d\n", hpke.AEAD(aeadID).KeySize())
 fmt.Printf("Key derivation function:\n")
 fmt.Printf(" Extract size:\t%d\n", hpke.KDF(kdfID).ExtractSize())
 fmt.Printf("\nMessage:\t%s\n", Alice_msg)
 fmt.Printf("Cipher:\t%x\n", hex.EncodeToString(ct))
 fmt.Printf("Decipher:\t%s\n", Bob_msg)
}

Si bien RSA sigue siendo la forma más común de cifrado de clave pública, ECIES proporciona el poder de la clave pública y la velocidad de la clave simétrica. Pero, por supuesto, RSA se puede usar con cifrado de clave simétrica, y encriptamos la clave simétrica para usarla con la clave pública. Y, por lo tanto, RSA se usa normalmente para cifrar nuestras claves de cifrado.