CryptoKit est le framework qu’utilisent les applications Swift des plateformes Apple pour le hachage, les signatures, l’accord de clé, l’AEAD et HPKE. Il est livré avec le système d’exploitation, si bien que sa version est celle de l’OS ; le SDK indique P-256 comme disponible à partir d’iOS 13 et de macOS 10.15, et ML-KEM, ML-DSA et X-Wing à partir d’iOS 26 et de macOS 26. Les faits présentés ici ont été vérifiés sous macOS 27.0.1 avec Swift 6.4 sur un Mac avec puce Apple, où les deux exemples ont été compilés et exécutés ; iOS n’a pas été vérifié. Sous Linux, la même API est fournie par swift-crypto.
Une signature P-256 est une valeur ECDSASignature qui a deux formes : rawRepresentation, le r‖s de 64 octets qu’utilisent JWS (ES256) et WebCrypto, et derRepresentation, la forme DER qu’attendent X.509, OpenSSL et Java. Envoyer la mauvaise forme est un bug d’interopérabilité courant ; l’exemple convertit explicitement, et le convertisseur DER ⇄ brut affiche les deux. L’analyse est stricte et lève des erreurs : des octets bruts passés à init(derRepresentation:) lèvent une CryptoKitASN1Error (truncatedASN1Field pour la signature de test de la RFC 6979, mais le cas dépend des octets), et du DER passé à init(rawRepresentation:) lève incorrectParameterSize, tandis que isValidSignature renvoie simplement false.
Les nonces sont aléatoires (deux signatures d’un même message différaient, et la clé de test de la RFC 6979 n’a pas produit le r de la RFC), et s n’est pas normalisé : des valeurs high-S apparaissent, et une signature dont s a été remplacé par n − s a été vérifiée. signature(for: data) hache avec la fonction de hachage qui correspond à la courbe (SHA-256, SHA-384, SHA-512), mais signature(for: digest) accepte n’importe quel type de condensat : un condensat SHA-256 a été signé et vérifié avec une clé P-384, et un condensat SHA-512 sur P-256 a été tronqué à ses 32 premiers octets, ce que Security.framework a confirmé. En cas de pré-hachage, passez vous-même le condensat correspondant.
Les signatures Ed25519 (Curve25519.Signing) sont aléatoires : avec la clé de test de la RFC 8032, la signature du message de test produite par CryptoKit différait de celle de la RFC et de sa propre signature précédente, et pourtant les deux ont été vérifiées, y compris avec Node.js. Les tests qui comparent les octets de signature aux vecteurs de la RFC 8032 échouent donc avec CryptoKit sous macOS ; vérifiez plutôt les signatures. Les valeurs S non canoniques sont rejetées, mais l’élément neutre a été accepté comme clé publique, et une signature avec R = élément neutre et S = 0 a été vérifiée pour un message arbitraire. Rejetez vous-même les clés de petit ordre lorsqu’une signature doit être liée à un signataire précis. Les clés Ed25519 n’existent que sous forme de 32 octets bruts ; il n’en existe aucune forme PEM ou DER.
Les types post-quantiques sont ML-KEM-768 et ML-KEM-1024, ML-DSA-65 et ML-DSA-87 (il n’y a ni ML-KEM-512, ni ML-DSA-44, ni SLH-DSA), ainsi que le KEM hybride X-Wing (XWingMLKEM768X25519), également proposé comme suite de chiffrement HPKE. La signature ML-DSA est en mode hedged (avec aléa) et accepte une chaîne de contexte FIPS 204, que le vérificateur doit reprendre à l’identique. La décapsulation ML-KEM d’un texte chiffré endommagé renvoie une clé différente au lieu de lever une erreur. Les clés privées ne sont stockées que sous forme de graines (64 octets pour ML-KEM, 32 pour ML-DSA) ou de graine accompagnée d’un contrôle d’intégrité : une clé privée développée exportée par une autre bibliothèque ne peut donc pas être importée. Essayez ces schémas dans l’outil ML-KEM et l’outil de signatures post-quantiques.
SecureEnclave.P256.Signing.PrivateKey() crée une clé à l’intérieur de la Secure Enclave, et sur ce Mac, les types ML-KEM et ML-DSA placés sous SecureEnclave l’ont fait aussi. Ces types n’exposent aucune clé privée brute, DER ou PEM (seulement dataRepresentation, une référence qui a permis de rouvrir la clé), tandis que la clé publique est une P256.Signing.PublicKey ou une MLDSA65.PublicKey ordinaire. Les clés P-256 logicielles s’exportent en PKCS#8 (derRepresentation, pemRepresentation) et s’importent depuis PKCS#8 et SEC1, en PEM ou en DER, mais pas depuis du PKCS#8 chiffré, et CryptoKit ne prend pas en charge JWK.