From: Ummon Date: Mon, 8 Dec 2014 17:25:33 +0000 (+0100) Subject: change RSA key size from 2048 to 3072. X-Git-Url: http://git.euphorik.ch/index.cgi?a=commitdiff_plain;h=c0d86015957eda6badbe9c6e5256807f9ab0c02f;p=crypto_lab2.git change RSA key size from 2048 to 3072. --- diff --git a/crypto-labo2-gburri.pdf b/crypto-labo2-gburri.pdf index bd1a2c2..2b164db 100644 Binary files a/crypto-labo2-gburri.pdf and b/crypto-labo2-gburri.pdf differ diff --git a/labo2-fsharp/CryptoFile/API.fs b/labo2-fsharp/CryptoFile/API.fs index f72395b..f542f49 100644 --- a/labo2-fsharp/CryptoFile/API.fs +++ b/labo2-fsharp/CryptoFile/API.fs @@ -30,8 +30,16 @@ module API = let internal (@@) a1 a2 = Array.append a1 a2 - let generatKeysPair : Key * Key = Crypto.generateRSAKeysPair + let test = 256 / 8 + let hmacSize = 256 / 8 // [byte]. + let signatureSize = Crypto.rsaKeySize / 8 // [byte]. + let keysSize = Crypto.rsaKeySize / 8 // [byte]. + let generatKeysPair () : Key * Key = Crypto.generateRSAKeysPair () + + // Format of the container: + // + // Where the sizes of the three first parts are given by 'hmacSize', 'signatureSize' and 'keysSize'. let encryptFile (inputFilePath : string) (outputFilePath : string) (signaturePrivKey: Key) (cryptPubKey : Key) = let keyAES, keyMAC, iv = Crypto.rand 16, Crypto.rand 32, Crypto.rand 16 let fileInfo = FileInfo (inputFilePath) @@ -39,7 +47,7 @@ module API = use outputStream = new FileStream (outputFilePath, FileMode.Create, FileAccess.Write) use writer = new BinaryWriter (outputStream) - outputStream.Position <- 32L + 256L // Skips mac and signature. They will be written later. + outputStream.Position <- (int64 <| hmacSize + signatureSize) // Skips mac and signature. They will be written later. Crypto.encryptRSA cryptPubKey (keyAES @@ keyMAC @@ iv) |> writer.Write @@ -71,12 +79,12 @@ module API = let decryptFile (sourceFilePath : string) (targetDirPath : string) (signaturePubKey: Key) (decryptPrivKey : Key) = use inputStream = new FileStream (sourceFilePath, FileMode.Open, FileAccess.Read) use reader = new BinaryReader (inputStream) - let mac = reader.ReadBytes 32 - let signature = reader.ReadBytes 256 + let mac = reader.ReadBytes hmacSize + let signature = reader.ReadBytes signatureSize let keys = - try reader.ReadBytes 256 |> Crypto.decryptRSA decryptPrivKey + try reader.ReadBytes keysSize |> Crypto.decryptRSA decryptPrivKey with - | :? Security.Cryptography.CryptographicException -> raise UnableToDecryptAESKeys + | :? Security.Cryptography.CryptographicException -> raise UnableToDecryptKeys let keyAES = keys.[0..15] let keyMAC = keys.[16..47] let iv = keys.[48..63] @@ -91,7 +99,7 @@ module API = raise SignatureMismatch // Decrypt metadata. - inputStream.Position <- 32L + 256L + 256L + inputStream.Position <- (int64 <| hmacSize + signatureSize + keysSize) use cryptoStream = Crypto.decryptAES keyAES iv inputStream let metadata = Metadata cryptoStream @@ -99,6 +107,6 @@ module API = let filePath = Path.Combine (targetDirPath, metadata.get MetadataKeys.filename) let modificationTime = DateTime (metadata.get MetadataKeys.modificationTime |> int64) let fileInfo = FileInfo filePath - using (fileInfo.Create ()) <| fun outputStream -> cryptoStream.CopyTo outputStream + using (fileInfo.Create ()) cryptoStream.CopyTo // We have to close the result file before updating the modification time. fileInfo.LastWriteTimeUtc <- modificationTime \ No newline at end of file diff --git a/labo2-fsharp/CryptoFile/Crypto.fs b/labo2-fsharp/CryptoFile/Crypto.fs index a7ba53c..4b893b0 100644 --- a/labo2-fsharp/CryptoFile/Crypto.fs +++ b/labo2-fsharp/CryptoFile/Crypto.fs @@ -4,65 +4,65 @@ open System open System.IO open System.Security.Cryptography -// Some cryptography primitives specific to CryptoFile. +// Some cryptography primitives specific to 'CryptoFile'. module internal Crypto = type Data = byte[] - let rsaKeySize = 2048 - let aesKeySize = 128 + let rsaKeySize = 3072 // [bit]. For encrypting and signing. + let aesKeySize = 128 // [bit]. exception KeySizeError exception IVSizeError - /// Returns a cryptographically strong sequence of bytes. + /// Return a cryptographically strong sequence of bytes. let rand size : byte[] = let result = Array.zeroCreate size use generator = new RNGCryptoServiceProvider () generator.GetBytes result result - /// Generates a new RSA key pair: (public * private). - let generateRSAKeysPair : Key * Key = + /// Generate a new RSA key pair: (public * private). + let generateRSAKeysPair () : Key * Key = use rsa = new RSACryptoServiceProvider (rsaKeySize) rsa.ToXmlString false, rsa.ToXmlString true let encryptRSA (publicKey: Key) (plaindata: Data) : Data = use rsa = new RSACryptoServiceProvider (rsaKeySize) rsa.FromXmlString publicKey - rsa.Encrypt (plaindata, true) // Uses padding OAEP (PKCS#1 v2). + rsa.Encrypt (plaindata, true) // Use padding OAEP (PKCS#1 v2). let decryptRSA (privateKey: Key) (cipherdata: Data) : Data = use rsa = new RSACryptoServiceProvider (rsaKeySize) rsa.FromXmlString privateKey - rsa.Decrypt (cipherdata, true) // Uses padding OAEP (PKCS#1 v2). + rsa.Decrypt (cipherdata, true) // Use padding OAEP (PKCS#1 v2). - /// Produces a signature from a given hash. - let signRSA (privKey: Key) (sha256: Data) : Data = + /// Produce a signature from the given data. + let signRSA (privKey: Key) (data: Data) : Data = use rsa = new RSACryptoServiceProvider (rsaKeySize) rsa.FromXmlString privKey - rsa.SignHash (sha256, CryptoConfig.MapNameToOID "SHA256") + rsa.SignHash (data, CryptoConfig.MapNameToOID "SHA256") - /// Verify a signature against a given hash. - let verifySignRSA (pubKey: Key) (sha256: Data) (signature: Data) : bool = + /// Verify a signature against the given data. + let verifySignRSA (pubKey: Key) (data: Data) (signature: Data) : bool = use rsa = new RSACryptoServiceProvider (rsaKeySize) rsa.FromXmlString pubKey - rsa.VerifyHash (sha256, CryptoConfig.MapNameToOID "SHA256", signature) + rsa.VerifyHash (data, CryptoConfig.MapNameToOID "SHA256", signature) - /// Returns an encrypted output stream. + /// Return an encrypted output stream. let encryptAES (key: byte[]) (iv: byte[]) (outputStream: Stream) : CryptoStream = if key.Length <> aesKeySize / 8 then raise KeySizeError if iv.Length <> 16 then raise IVSizeError use aes = new AesCryptoServiceProvider (KeySize = aesKeySize) // Default mode is CBC. new CryptoStream (outputStream, aes.CreateEncryptor (key, iv), CryptoStreamMode.Write) - /// Returns a decrypted input stream. + /// Return a decrypted input stream. let decryptAES (key: byte[]) (iv: byte[]) (inputStream: Stream) : CryptoStream = if key.Length <> aesKeySize / 8 then raise KeySizeError if iv.Length <> 16 then raise IVSizeError use aes = new AesCryptoServiceProvider (KeySize = aesKeySize) new CryptoStream (inputStream, aes.CreateDecryptor (key, iv), CryptoStreamMode.Read) - // Creates a stream to compute the HMAC-SHA256 against all data being written. + // Create a stream to compute the HMAC-SHA256 against all data being written. let HMACStream (key: byte[]) (outputStream: Stream) : Stream * HMACSHA256 = if key.Length <> 32 then raise KeySizeError let hmac = new HMACSHA256 (key) diff --git a/labo2-fsharp/CryptoFile/Types.fs b/labo2-fsharp/CryptoFile/Types.fs index 7520663..980fa68 100644 --- a/labo2-fsharp/CryptoFile/Types.fs +++ b/labo2-fsharp/CryptoFile/Types.fs @@ -4,4 +4,4 @@ type Key = string // For public and private RSA keys. exception IntegrityError exception SignatureMismatch -exception UnableToDecryptAESKeys +exception UnableToDecryptKeys diff --git a/labo2-fsharp/CryptoFile/UnitTests.fs b/labo2-fsharp/CryptoFile/UnitTests.fs index 7345538..51912d3 100644 --- a/labo2-fsharp/CryptoFile/UnitTests.fs +++ b/labo2-fsharp/CryptoFile/UnitTests.fs @@ -7,7 +7,7 @@ module UnitTests = open Crypto let testRSA () = - let kpub, kpriv = generateRSAKeysPair + let kpub, kpriv = generateRSAKeysPair () let plaintext = "Are you from the past?" let cipherdata = encryptRSA kpub (Encoding.UTF8.GetBytes plaintext) let decryptedData = decryptRSA kpriv cipherdata @@ -17,7 +17,7 @@ module UnitTests = printfn "testRSA OK" let testRSASignature () = - let kpub, kpriv = generateRSAKeysPair + let kpub, kpriv = generateRSAKeysPair () let plaintext = "Lightspeed is too slow. We'll have to go right to ludicrous speed!" use sha256 = new SHA256Managed () let signature = signRSA kpriv (sha256.ComputeHash (Encoding.UTF8.GetBytes plaintext)) diff --git a/labo2-fsharp/CryptoFileTests/Program.fs b/labo2-fsharp/CryptoFileTests/Program.fs index a0cfba9..509b6d7 100644 --- a/labo2-fsharp/CryptoFileTests/Program.fs +++ b/labo2-fsharp/CryptoFileTests/Program.fs @@ -1,7 +1,7 @@ module CryptoFileTests.Main open System -open System.IO; +open System.IO open CryptoFile let printUsage () = @@ -14,7 +14,7 @@ let printUsage () = // The private keys are stored in plain file! Do not let anyone access these files! module internal Keys = // Try to read the public key and private key from files. If one of a file doesn't exist - // a new key pair is created, stored the returned. + // a new key pair is created, stored then returned. let getKey (filenamePub: string) (filenamePriv: string) : Key * Key = try use srPub = new StreamReader (filenamePub) @@ -24,7 +24,7 @@ module internal Keys = | _ -> use swPub = new StreamWriter (filenamePub) use swPriv = new StreamWriter (filenamePriv) - let keyPub, keyPriv = API.generatKeysPair + let keyPub, keyPriv = API.generatKeysPair () swPub.Write keyPub swPriv.Write keyPriv keyPub, keyPriv diff --git a/labo2-fsharp/CryptoFileTests/Tests.fs b/labo2-fsharp/CryptoFileTests/Tests.fs index eb8a290..458b1be 100644 --- a/labo2-fsharp/CryptoFileTests/Tests.fs +++ b/labo2-fsharp/CryptoFileTests/Tests.fs @@ -5,7 +5,7 @@ open CryptoFile let doSomeTests () = printfn "===== Unit tests" - CryptoFile.UnitTests.runAllUnitTests () + UnitTests.runAllUnitTests () printfn "===== Unit tests OK" printfn "===== API tests" @@ -15,8 +15,8 @@ let doSomeTests () = File.WriteAllText (plainFilename, fileContent) - let keyCryptPub, keyCryptPriv = API.generatKeysPair - let keySigPub, keySigPriv = API.generatKeysPair + let keyCryptPub, keyCryptPriv = API.generatKeysPair () + let keySigPub, keySigPriv = API.generatKeysPair () let encrypt () = API.encryptFile plainFilename cipherFilename keySigPriv keyCryptPub @@ -25,11 +25,11 @@ let doSomeTests () = API.decryptFile cipherFilename "." keySigPub keyCryptPriv let incrementByteCipherFileAt position = - using (new FileStream (cipherFilename, FileMode.Open, FileAccess.ReadWrite)) - (fun fs -> fs.Position <- position - let byte = fs.ReadByte () |> byte - fs.Position <- position - fs.Write ([| byte + 1uy |], 0, 1)) // Automatically modulo 256. + use fs = new FileStream (cipherFilename, FileMode.Open, FileAccess.ReadWrite) + fs.Position <- position + let byte = fs.ReadByte () |> byte + fs.Position <- position + fs.Write ([| byte + 1uy |], 0, 1) // Modulo 256. encrypt () File.Delete plainFilename @@ -37,38 +37,48 @@ let doSomeTests () = assert (File.ReadAllText plainFilename = fileContent) printfn "== Altering the MAC..." - incrementByteCipherFileAt 0L - try - decrypt () - assert false - with - | error -> assert (error :? IntegrityError) + for i in 0 .. API.hmacSize - 1 do + printf "." + encrypt () + incrementByteCipherFileAt (int64 i) + try + decrypt () + assert false + with + | error -> assert (error :? IntegrityError) + printfn "" printfn "== Altering the signature..." - encrypt () - incrementByteCipherFileAt 32L - try - decrypt () - assert false - with - | error -> assert (error :? SignatureMismatch) + for i in 0 .. API.signatureSize - 1 do + printf "." + encrypt () + incrementByteCipherFileAt (int64 <| API.hmacSize + i) + try + decrypt () + assert false + with + | error -> assert (error :? SignatureMismatch) + printfn "" printfn "== Altering the keys..." - encrypt () - incrementByteCipherFileAt (32L + 256L) - try - decrypt () - assert false - with - | error -> assert (error :? UnableToDecryptAESKeys) + for i in 0 .. API.keysSize - 1 do + printf "." + encrypt () + incrementByteCipherFileAt (int64 <| API.hmacSize + API.signatureSize + i) + try + decrypt () + assert false + with + | error -> assert (error :? UnableToDecryptKeys) + printfn "" printfn "== Altering the cyphertext..." encrypt () - incrementByteCipherFileAt (32L + 256L + 256L) + incrementByteCipherFileAt (int64 <| API.hmacSize + API.signatureSize + API.keysSize) try decrypt () assert false - with + with | error -> assert (error :? IntegrityError) File.Delete cipherFilename diff --git a/rapport/main.tex b/rapport/main.tex index 7734371..3ae834d 100644 --- a/rapport/main.tex +++ b/rapport/main.tex @@ -19,6 +19,7 @@ \urldef{\rngcryptoserviceprovider}\url{http://msdn.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider%28v=vs.110%29.aspx} \urldef{\rsasecurity}\url{http://en.wikipedia.org/wiki/RSA_Security} \urldef{\wikiml}\url{http://en.wikipedia.org/wiki/ML_%28programming_language%29} +\urldef{\rsaxmlformat}\url{http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa.toxmlstring%28v=vs.110%29.aspx} \title{ICR - Labo \#2 : \textit{Conception et implémentation d'un container sécurisé pour des données médicales}} \author{G.Burri} @@ -62,6 +63,10 @@ Le but de ce laboratoire est de définir les algorithmes cryptographiques et leu \subsection{Quel est le niveau de sécurité que l'on souhaite atteindre ?} +Le niveau souhaité est de 128 bits. Cela implique l'utilisation d'une clef \emph{AES} de 128 bits et de clefs \emph{RSA} de 3072 bits d'après~\cite{wiki-key-size}. + +Les éléments de sécurité suivants sont requis : + \begin{itemize} \item Confidentialité : les données chiffrées ne doivent pas pouvoir être décryptées par un attaquant. \item Authenticité : un attaquant ne doit pas pouvoir forger un container. Une signature est réalisée à l'aide d'une paire de clefs \emph{RSA} publique-privée. @@ -76,7 +81,7 @@ Les méta-données ainsi que les données sont chiffrées ensemble. Voir le form \subsection{Comment s'assure-t-on que les données stockées sont authentiques ? Quels sont les risques à prendre en compte ?} -L'empreinte des données est signée à l'aide d'une clef privée donnée en paramètre de l'\emph{API} : ceci représente la signature qui est placée dans le container. Lors du déchiffrement, la clef publique correspondante est fournie puis utilisée pour déchiffrer l'empreinte qui est comparée à l'empreinte des données. +L'empreinte des données chiffrées est signée à l'aide d'une clef privée donnée en paramètre de l'\emph{API} : ceci représente la signature qui est placée dans le container. Lors du déchiffrement, la clef publique correspondante est fournie puis utilisée pour vérifier la signature avec l'empreinte des données chiffrées. \subsection{Comment s'assure-t-on que les données stockées sont intègres ?} @@ -91,8 +96,8 @@ Cela est réalisé avec un \emph{MAC}, dans notre cas nous utilisons \emph{HMAC- Concerne les clefs externes à l'\emph{API}. \begin{itemize} - \item Une paire de clefs \emph{RSA-2048} pour la signature. - \item Une paire de clefs \emph{RSA-2048} pour le chiffrement des clefs \emph{AES}. + \item Une paire de clefs \emph{RSA-3072} pour la signature. + \item Une paire de clefs \emph{RSA-3072} pour le chiffrement des clefs \emph{AES}. \end{itemize} @@ -111,12 +116,12 @@ Ces clefs sont générées aléatoirement à chaque création d'un container. \section{Choix des algorithmes et des paramètres} \begin{itemize} - \item \emph{RSA-2048} pour la signature ainsi que pour le chiffrage des clefs \emph{AES} et \emph{HMAC}. Le bourrage \emph{OAEP} (\emph{PKCS\#1 v2}) est utilisé ; + \item \emph{RSA-3072} pour la signature ainsi que pour le chiffrage des clefs \emph{AES} et \emph{HMAC}. Le bourrage \emph{OAEP} (\emph{PKCS\#1 v2}) est utilisé ; \item \emph{HMAC-SHA256} pour la vérification de l'intégrité ; \item \emph{AES-CBC128} pour le chiffrement symétrique du contenu du fichier et des méta-données associées. Le bourrage \emph{PKCS7} est utilisé. \end{itemize} -D'après~\cite{wiki-key-size}, la société \emph{RSA Security}\footnote{\rsasecurity} annonce qu'une taille de clefs \emph{RSA} de 2048 bits est suffisante jusqu'en 2030. Cela dépend également du niveau d'importance des documents que l'on souhaite chiffrer dans la mesure ou une attaque demande énormément de moyens. +D'après~\cite{wiki-key-size}, la société \emph{RSA Security}\footnote{\rsasecurity} annonce qu'une taille de clefs \emph{RSA} de 3072 bits est suffisante pour une utilisation au delà de 2030. Cela dépend également du niveau d'importance des documents que l'on souhaite chiffrer dans la mesure ou une attaque demande énormément de moyens. Toujours d'après~\cite{wiki-key-size}, une taille de clef \emph{AES} de 128 bits reste, actuellement, hors de portée de toutes attaques. @@ -129,7 +134,7 @@ Le format est défini comme suit en \emph{EBNF}. Les valeurs entre crochets corr \begin{lstlisting}[frame=single, breaklines, basicstyle=\ttfamily\footnotesize] container = header, ciphertext ; -header = mac[256], signature[2048], keys[2048] ; +header = mac[256], signature[3072], keys[3072] ; ciphertext = AES(plaintext) ; plaintext = meta-data, file-content ; meta-data = nb-meta-data[byte], { key-value-pair } ; @@ -139,7 +144,7 @@ string = size[vint], content-utf8 ; \texttt{nb-meta-data} est le nombre de paires clef-valeur des méta-données. -\texttt{keys} correspond aux clefs $k_c$ et $k_a$ ainsi qu'à l'\emph{IV}, le tout chiffré avec \emph{RSA-2048}. La taille des données chiffrées est égale à $k_c + k_a + iv = 128 + 256 + 128 = 512\,bits$. +\texttt{keys} correspond aux clefs $k_c$ et $k_a$ ainsi qu'à l'\emph{IV}, le tout chiffré avec \emph{RSA-3072}. La taille des données à chiffrer est égale à $k_c + k_a + iv = 128 + 256 + 128 = 512\,bits$. Les méta-données (\texttt{meta-data}) peuvent contenir, par exemple, le nom du fichier, sa date de création, ses droits, ou toutes autres données associées. @@ -279,6 +284,8 @@ module API = \end{lstlisting} \end{minipage} +Les formats des clefs, publique et privée, sont décrits sur cette page~\footnote{\rsaxmlformat}. + \subsection{Mesures de performance} @@ -296,12 +303,16 @@ Les tests sous \emph{Windows 8} ont été fait sur une machine ne possédant pas Taux \emph{CPU} & 1 x 100 \% & 1 x 100 \% & 1 x 100 \% & 1 x 100 \% \\ \end{tabular} +\subsubsection{Génération de paire de clefs \emph{RSA}} + +La génération de clefs \emph{RSA} est très lente sous \emph{Mono}. Pour une taille de 2048 bits cela prend environ une seconde, pour une taille de 3072 bits cela prend environ dix secondes. + \section{Analyse de la sécurité de l'implémentation} \subsection{Quelles sont les parties critiques du code et comment s'assure-t-on que ces parties soient correctement implémentées ?} -Le choix des algorithmes, de leurs paramètres et de leur implémentation est une partie critique. Il est possible de se référer aux recommandations de certains organismes comme par exemple le \emph{NIST}\footnote{\emph{ National Institute of Standards and Technology}}. +Le choix des algorithmes, de leurs paramètres et de leur implémentation est une partie critique. Il est possible de se référer aux recommandations de certains organismes comme par exemple le \emph{NIST}\footnote{\emph{National Institute of Standards and Technology}}. La génération des clefs \emph{AES} doit être faite avec un générateur cryptographique. Dans notre cas nous utilisons \emph{RNGCryptoServiceProvider}\footnote{\rngcryptoserviceprovider}. diff --git a/template.bt b/template.bt index 8ff158b..ca2c8fe 100644 --- a/template.bt +++ b/template.bt @@ -1,7 +1,7 @@ typedef struct { UBYTE mac[32] ; - UBYTE signature[256] ; - UBYTE key[256] ; + UBYTE signature[384] ; + UBYTE key[384] ; } Header; Header header;