CryptoKit es el framework que usan las apps de Swift en las plataformas de Apple para hashes, firmas, acuerdo de claves, AEAD y HPKE. Viene con el sistema operativo, así que su versión es la del sistema; el SDK marca P-256 como disponible desde iOS 13 y macOS 10.15, y ML-KEM, ML-DSA y X-Wing desde iOS 26 y macOS 26. Los datos de esta página se comprobaron en macOS 27.0.1 con Swift 6.4 en un Mac con Apple silicon, y los dos ejemplos se compilaron y ejecutaron allí; iOS no se comprobó. En Linux, la misma API la proporciona swift-crypto.
Una firma P-256 es un valor ECDSASignature con dos formas: rawRepresentation, el r‖s de 64 bytes que usan JWS (ES256) y WebCrypto, y derRepresentation, la forma DER que esperan X.509, OpenSSL y Java. Enviar la forma equivocada es un error de interoperabilidad frecuente; el ejemplo convierte de forma explícita, y el conversor DER ⇄ en bruto muestra ambas. El análisis es estricto y lanza errores: los bytes en bruto pasados a init(derRepresentation:) lanzan un CryptoKitASN1Error —truncatedASN1Field con la firma de prueba del RFC 6979, aunque el caso concreto depende de los bytes—, y el DER pasado a init(rawRepresentation:) lanza incorrectParameterSize, mientras que isValidSignature simplemente devuelve false.
Los nonces son aleatorios —dos firmas de un mismo mensaje salieron distintas, y la clave de prueba del RFC 6979 no produjo la r del RFC— y s no se normaliza: aparecen valores high-S, y una firma con s sustituido por n − s se verificó. signature(for: data) calcula el hash con la función que corresponde a la curva (SHA-256, SHA-384, SHA-512), pero signature(for: digest) acepta cualquier tipo de resumen: un resumen SHA-256 se firmó y se verificó con una clave P-384, y un resumen SHA-512 firmado con P-256 se truncó a sus primeros 32 bytes, lo que confirmó Security.framework. Si calculas tú el hash (pre-hash), encárgate de pasar el resumen que corresponde a la curva.
Las firmas Ed25519 (Curve25519.Signing) son aleatorizadas: con la clave de prueba del RFC 8032, la firma de CryptoKit del mensaje de prueba difirió de la del RFC y de su propia firma anterior, y aun así ambas se verificaron, también con Node.js. Por eso, las pruebas que comparan los bytes de la firma con los vectores de prueba del RFC 8032 fallan con CryptoKit en macOS; verifica las firmas en su lugar. Los valores S no canónicos se rechazan, pero el punto identidad se aceptó como clave pública, y una firma con R = identidad y S = 0 se verificó para un mensaje arbitrario. Rechaza tú mismo las claves de orden pequeño cuando una firma deba vincularse a un firmante concreto. Las claves Ed25519 solo existen como 32 bytes en bruto; no tienen forma PEM ni DER.
Los tipos poscuánticos son ML-KEM-768 y ML-KEM-1024, ML-DSA-65 y ML-DSA-87 —no hay ML-KEM-512, ML-DSA-44 ni SLH-DSA— y el KEM híbrido X-Wing (XWingMLKEM768X25519), que también se ofrece como conjunto de cifrado de HPKE. La firma ML-DSA es hedged (reforzada con aleatoriedad) y acepta una cadena de contexto de FIPS 204, que el verificador debe repetir. La desencapsulación ML-KEM de un texto cifrado dañado devuelve una clave distinta en lugar de lanzar un error. Las claves privadas se guardan solo como semillas —64 bytes para ML-KEM, 32 para ML-DSA— o como la semilla con una comprobación de integridad, así que no se puede importar una clave privada expandida exportada por otra biblioteca. Prueba los esquemas en la herramienta de ML-KEM y en la herramienta de firmas poscuánticas.
SecureEnclave.P256.Signing.PrivateKey() crea una clave dentro del Secure Enclave, y en este Mac también lo hicieron los tipos ML-KEM y ML-DSA de SecureEnclave. Estos tipos no exponen ninguna clave privada en bruto, DER ni PEM —solo dataRepresentation, una referencia (handle) con la que se volvió a abrir la clave—, mientras que la clave pública es un P256.Signing.PublicKey o un MLDSA65.PublicKey normal. Las claves P-256 de software se exportan como PKCS#8 (derRepresentation, pemRepresentation) y se importan desde PKCS#8 y SEC1, en PEM o DER, pero no desde PKCS#8 cifrado, y CryptoKit no admite JWK.