El cifrado simétrico usa la misma clave para cifrar y descifrar. La idea que más importa en esta página es la diferencia entre un cifrado sencillo y un cifrado AEAD (cifrado autenticado con datos asociados). AES-CBC o AES-CTR por sí solos proporcionan confidencialidad y nada más: un atacante que no puede leer el texto en claro aún puede invertir bits en el texto cifrado, y el receptor descifrará el resultado manipulado sin ninguna indicación de que algo va mal. AES-GCM y ChaCha20-Poly1305 autentican además de cifrar, de modo que la manipulación se detecta y el descifrado falla directamente. Prefiera un cifrado AEAD a menos que algo específico obligue a usar un modo sencillo.
Todas las funciones residen en sgcCrypto_AES, sgcCrypto_Modes, sgcCrypto_ChaCha y sgcCrypto_Poly1305. Nunca reutilice un IV o un nonce con la misma clave: para CTR y los cifrados AEAD, la reutilización no solo debilita la confidencialidad, en el caso concreto de GCM rompe directamente la garantía de autenticación. Genere uno nuevo con sgcRandomBytes para cada mensaje.
AES-GCM (recomendado)
El modo AEAD que la mayoría de los protocolos usan por defecto. El nonce tiene 12 bytes; genérelo de forma aleatoria en cada mensaje. aAAD se autentica pero no se cifra, que es como un texto cifrado queda vinculado a su contexto, por ejemplo un identificador de mensaje o una versión de protocolo, sin ocultar ese contexto a quien pueda ver el texto cifrado.
function sgcAES_GCM_Encrypt(const aKey, aIV, aPlain, aAAD: TBytes; out aTag: TBytes): TBytes;
function sgcAES_GCM_Decrypt(const aKey, aIV, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;
var
oKey, oIV, oCipher, oTag, oPlain, oAAD: TBytes;
begin
oIV := sgcRandomBytes(12);
oCipher := sgcAES_GCM_Encrypt(oKey, oIV, oPlain, oAAD, oTag);
{ send oIV, oCipher and oTag to the recipient }
if not sgcAES_GCM_Decrypt(oKey, oIV, oCipher, oAAD, oTag, oPlain) then
{ the ciphertext, the tag or the AAD was altered: reject it };
end;
ChaCha20-Poly1305 y XChaCha20-Poly1305
El par AEAD de RFC 8439. Donde AES-GCM se apoya en la aceleración por hardware de AES, ChaCha20-Poly1305 se ejecuta a velocidad constante en software puro, razón por la cual TLS y WireGuard lo usan por defecto en dispositivos móviles y embebidos sin AES-NI. El nonce tiene 12 bytes, la misma restricción que GCM. XChaCha20-Poly1305 extiende el nonce a 24 bytes, lo bastante grande como para elegirlo al azar durante toda la vida de una clave sin llevar la cuenta de un contador, lo cual importa cuando varios escritores independientes comparten una clave y no pueden coordinar un contador entre ellos.
function sgcChaCha20Poly1305_Encrypt(const aKey, aNonce, aPlain, aAAD: TBytes; out aTag: TBytes): TBytes;
function sgcChaCha20Poly1305_Decrypt(const aKey, aNonce, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;
function sgcXChaCha20Poly1305_Encrypt(const aKey, aNonce, aPlain, aAAD: TBytes; out aTag: TBytes): TBytes;
function sgcXChaCha20Poly1305_Decrypt(const aKey, aNonce, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;
AES-CCM
Contador con CBC-MAC, RFC 3610 y NIST SP 800-38C. Este es el AEAD que prefieren las pilas embebidas e IoT, porque solo necesita el propio cifrado de bloque y ninguna tabla GHASH: Zigbee, 802.15.4, Bluetooth y las suites CCM de TLS lo especifican todas. Donde haya margen, GCM sigue siendo la mejor opción, ya que CCM hace dos pasadas sobre los datos y no se puede canalizar.
El nonce tiene de 7 a 13 bytes y su longitud no es una elección libre: 15 menos la longitud del nonce es el tamaño del campo de longitud, así que fija el tamaño máximo del mensaje, y un nonce de 13 bytes limita el texto en claro a 65535 bytes. Un nonce nunca debe repetirse con la misma clave, exactamente igual que con GCM. aTagLen es 4, 6, 8, 10, 12, 14 o 16 bytes.
function sgcAES_CCM_Encrypt(const aKey, aNonce, aPlain, aAAD: TBytes; aTagLen: Integer; out aTag: TBytes): TBytes;
function sgcAES_CCM_Decrypt(const aKey, aNonce, aCipher, aAAD, aTag: TBytes; out aPlain: TBytes): Boolean;
var
oKey, oNonce, oCipher, oTag, oPlain, oAAD: TBytes;
begin
oNonce := sgcRandomBytes(12);
oCipher := sgcAES_CCM_Encrypt(oKey, oNonce, oPlain, oAAD, 16, oTag);
{ send oNonce, oCipher and oTag to the recipient }
if not sgcAES_CCM_Decrypt(oKey, oNonce, oCipher, oAAD, oTag, oPlain) then
{ the ciphertext, the tag or the AAD was altered: reject it };
end;
AES-GMAC
GMAC es exactamente AES-GCM ejecutado con un texto en claro vacío y el mensaje completo pasado como datos adicionales, que es como lo define el estándar. Por lo tanto hereda todas y cada una de las reglas de nonce de GCM: aIV tiene normalmente 12 bytes y nunca debe repetirse con la misma clave. Reutilizar un IV bajo GMAC no solo lo debilita, filtra la subclave hash y permite a un atacante falsificar etiquetas directamente, así que el fallo es total y no gradual. AES-CMAC en la página de Hash y Autenticación de Mensajes no necesita ningún IV y es la opción más segura a menos que GCM ya esté en el protocolo. La etiqueta tiene 16 bytes.
function sgcAES_GMAC(const aKey, aIV, aData: TBytes): TBytes;
function sgcAES_GMAC_Verify(const aKey, aIV, aData, aTag: TBytes): Boolean;
Modos de bloque AES
Estos solo proporcionan confidencialidad, sin autenticación: combínelos con un MAC de Hash y Autenticación de Mensajes si el texto en claro necesita integridad, o use AES-GCM anterior en su lugar.
function sgcAES_CBC_Encrypt(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CBC_Decrypt(const aKey, aIV, aCipher: TBytes): TBytes;
function sgcAES_CTR(const aKey, aIV, aData: TBytes): TBytes;
function sgcAES_CFB_Encrypt(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CFB_Decrypt(const aKey, aIV, aCipher: TBytes): TBytes;
function sgcAES_OFB(const aKey, aIV, aData: TBytes): TBytes;
function sgcAES_ECB_Encrypt(const aKey, aPlain: TBytes): TBytes;
function sgcAES_ECB_Decrypt(const aKey, aCipher: TBytes): TBytes;
sgcAES_CTR y sgcAES_OFB son su propia inversa: llame otra vez a la misma función con la misma clave e IV para descifrar. ECB se incluye solo para interoperar con formatos antiguos que lo especifican; los bloques de texto en claro idénticos producen bloques de texto cifrado idénticos, lo que filtra la estructura de los datos, así que no debería elegirse para nada nuevo.
CBC también está disponible sin ningún relleno, el modo que Java escribe como AES/CBC/NoPadding. La longitud de la entrada debe ser un múltiplo exacto de 16 y se lanza una excepción cuando no lo es, en lugar de que la llamada rellene silenciosamente y produzca un texto cifrado que el otro lado no pueda leer. Solo es seguro donde el protocolo ya garantiza datos alineados a bloque.
function sgcAES_CBC_Encrypt_NoPad(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CBC_Decrypt_NoPad(const aKey, aIV, aCipher: TBytes): TBytes;
El robo de texto cifrado evita la sobrecarga de relleno de CBC para datos que no son múltiplo del tamaño de bloque:
function sgcAES_CTS_Encrypt(const aKey, aIV, aPlain: TBytes): TBytes;
function sgcAES_CTS_Decrypt(const aKey, aIV, aCipher: TBytes): TBytes;
Esquemas de relleno
Un modo de bloque necesita que el texto en claro sea múltiplo de 16 bytes, y el esquema de relleno es cómo se llega hasta ahí. PKCS#7 es la única opción correcta para algo nuevo; los otros seis existen para leer y escribir datos de un sistema que ya eligió otra cosa. padPKCS5 no está en la lista porque para un bloque de 16 bytes es PKCS#7, los dos nombres solo difieren en los tamaños de bloque para los que se definieron.
type
TsgcPadding = (padNone, padPKCS7, padZero, padANSIX923, padISO7816_4,
padISO10126_2, padTBC);
function sgcPad_Add(const aData: TBytes; aBlockSize: Integer; aPadding: TsgcPadding): TBytes;
function sgcPad_Remove(const aData: TBytes; aBlockSize: Integer; aPadding: TsgcPadding; out aPlain: TBytes): Boolean;
function sgcAES_CBC_EncryptPad(const aKey, aIV, aPlain: TBytes; aPadding: TsgcPadding): TBytes;
function sgcAES_CBC_DecryptPad(const aKey, aIV, aCipher: TBytes; aPadding: TsgcPadding): TBytes;
function sgcAES_ECB_EncryptPad(const aKey, aPlain: TBytes; aPadding: TsgcPadding): TBytes;
function sgcAES_ECB_DecryptPad(const aKey, aCipher: TBytes; aPadding: TsgcPadding): TBytes;
var
oKey, oIV, oPlain, oCipher: TBytes;
begin
oCipher := sgcAES_CBC_EncryptPad(oKey, oIV, oPlain, padANSIX923);
end;
sgcAES_CBC_Encrypt y sgcAES_ECB_Encrypt más arriba son el atajo PKCS#7 para estas. sgcPad_Remove devuelve False ante un relleno malformado en lugar de lanzar una excepción, y sigue el mismo camino para todo tipo de fallo, porque distinguirlos es lo que necesita un oráculo de relleno. Tenga especial cuidado con padZero: no puede distinguir un byte de relleno de un cero final del texto en claro, así que elimina todos los ceros finales, y unos datos que legítimamente puedan terminar en uno vuelven truncados.
Aceleración por hardware de AES (AES-NI)
En compilaciones Windows Win32 y Win64 hechas con Delphi, sgcCrypto_AES comprueba la CPU en tiempo de ejecución y, cuando detecta AES-NI, PCLMULQDQ y SSSE3, ejecuta el cifrado de bloque único, AES-CTR, AES-GCM (el modo contador y GHASH) y el cifrado AES-CBC sobre esas instrucciones, que son de tiempo constante por diseño. Sin ellas, y en Linux64, macOS, iOS y Android, se utiliza como antes el núcleo software de tiempo constante. El descifrado CBC siempre utiliza el núcleo software. El motor TLS nativo se beneficia de esto automáticamente.
sgcAES_HardwareAvailable devuelve True cuando esta compilación incluye la ruta de hardware y la CPU la admite. La variable global sgcAES_UseHardware, True de forma predeterminada, establecida en False fuerza el núcleo software, por ejemplo para comparar ambos. Establézcala una sola vez al arrancar.
var sgcAES_UseHardware: Boolean = True;
function sgcAES_HardwareAvailable: Boolean;
if sgcAES_HardwareAvailable then
Memo1.Lines.Add('AES-NI en uso');
AES Key Wrap
El envoltorio de claves protege una clave, no un mensaje: no necesita IV, es determinista, y autentica la clave envuelta. Esta es la forma estándar de almacenar una clave de cifrado de datos bajo una clave maestra, por ejemplo una clave maestra de un HSM o KMS que envuelve una clave por archivo. RFC 3394 exige que la clave que se envuelve sea múltiplo de 8 bytes; RFC 5649 añade relleno para claves de cualquier longitud.
function sgcAES_KeyWrap(const aKEK, aPlainKey: TBytes): TBytes;
function sgcAES_KeyUnwrap(const aKEK, aWrapped: TBytes; out aPlainKey: TBytes): Boolean;
function sgcAES_KeyWrapPad(const aKEK, aPlainKey: TBytes): TBytes;
function sgcAES_KeyUnwrapPad(const aKEK, aWrapped: TBytes; out aPlainKey: TBytes): Boolean;
ChaCha20 y Salsa20 (cifradores de flujo en bruto)
Los cifradores de flujo detrás de las construcciones AEAD anteriores, disponibles directamente para protocolos que especifican el cifrado en bruto. Ninguno de los dos autentica: casi siempre las versiones AEAD con Poly1305 más arriba en esta página son lo que realmente necesita. Salsa20 es el predecesor de ChaCha20, mantenido por compatibilidad con formatos de la era NaCl; elija ChaCha20 para cualquier cosa nueva.
function sgcChaCha20(const aKey, aNonce, aData: TBytes; aCounter: Cardinal): TBytes;
function sgcXChaCha20(const aKey, aNonce, aData: TBytes; aCounter: Cardinal): TBytes;
function sgcSalsa20(const aKey, aNonce, aData: TBytes; aCounter: Int64): TBytes;
function sgcXSalsa20(const aKey, aNonce, aData: TBytes; aCounter: Int64): TBytes;
Heredado: DES y NTLM
sgcCrypto_Legacy también proporciona DES simple en modo ECB y la derivación de claves NTLM v1 basada en DES. DES tiene una clave efectiva de 56 bits, forzable por fuerza bruta en hardware moderno en cuestión de horas; existe únicamente para interoperar con protocolos que lo especifican por su nombre (la autenticación NTLM es el motivo habitual). No lo elija para nada nuevo; use AES en su lugar.
function sgcDES_EncryptECB(const aKey, aData: TBytes): TBytes;
function sgcDES_DecryptECB(const aKey, aData: TBytes): TBytes;
function sgcDES_NTLM7to8(const aKey7: TBytes): TBytes;