From: Greg Burri Date: Fri, 12 Jul 2019 20:46:19 +0000 (+0200) Subject: Add the F# implementation. X-Git-Url: http://git.euphorik.ch/index.cgi?a=commitdiff_plain;h=3579135886b85a80569501f1abb5ae7a56f6865e;p=rup.git Add the F# implementation. --- diff --git a/WebSharper/Crypto.fs b/WebSharper/Crypto.fs new file mode 100644 index 0000000..5ad3d99 --- /dev/null +++ b/WebSharper/Crypto.fs @@ -0,0 +1,77 @@ +module RUP.Crypto + +open System +open System.Net +open System.IO +open System.Security.Cryptography +open System.Text +open System.Web + +let keySize = 128 // [bit]. + +type Key = string + +let randBytes (nb : int) : byte array = + let result = Array.zeroCreate nb + use generator = new RNGCryptoServiceProvider () + generator.GetBytes result + result + +let generateKey () : Key = + let bytes = randBytes (keySize / 8) + Convert.ToBase64String bytes |> WebUtility.UrlEncode + +let encrypt (key : Key) (name : string) : string = + let keyAsBytes = WebUtility.UrlDecode key |> Convert.FromBase64String + let iv = randBytes (keySize / 8) + + use aes = new AesCryptoServiceProvider (KeySize = keySize) // Default mode is CBC. + use stream = new MemoryStream () + use cryptoStream = new CryptoStream (stream, aes.CreateEncryptor (keyAsBytes, iv), CryptoStreamMode.Write) + let nameAsBytes = Encoding.UTF8.GetBytes name + + use sha = SHA256.Create () + let hash = sha.ComputeHash nameAsBytes + + cryptoStream.Write (nameAsBytes, 0, nameAsBytes.Length) + cryptoStream.FlushFinalBlock () + stream.Position <- 0L + + let result = Array.zeroCreate (iv.Length + hash.Length + int stream.Length) + + Array.blit iv 0 result 0 iv.Length + Array.blit hash 0 result iv.Length hash.Length + + stream.Read (result, iv.Length + hash.Length, int stream.Length) |> ignore + "1" + Convert.ToBase64String result |> WebUtility.UrlEncode + +let decrypt (key : Key) (cipher : string) (urlDecode : bool) : string = + let version = cipher.Substring (0, 1) |> int + if version <> 1 then + failwithf "Version not supported: %i" version + + let keyAsBytes = WebUtility.UrlDecode key |> Convert.FromBase64String + let bytes = cipher.Substring 1 |> (if urlDecode then WebUtility.UrlDecode else id) |> Convert.FromBase64String + + let iv = Array.zeroCreate (keySize / 8) + Array.blit bytes 0 iv 0 iv.Length + + let hash = Array.zeroCreate 32 + Array.blit bytes iv.Length hash 0 hash.Length + + use aes = new AesCryptoServiceProvider (KeySize = keySize) + use stream = new MemoryStream (bytes, iv.Length + hash.Length, bytes.Length - iv.Length - hash.Length) + use cryptoStream = new CryptoStream (stream, aes.CreateDecryptor (keyAsBytes, iv), CryptoStreamMode.Read) + use streamResult = new MemoryStream () + cryptoStream.CopyTo streamResult + streamResult.Position <- 0L + let result = Array.zeroCreate (int streamResult.Length) + streamResult.Read (result, 0, result.Length) |> ignore + + use sha = SHA256.Create () + let computedHash = sha.ComputeHash result + + if hash <> computedHash then + failwith "hash mismatch" + + Encoding.UTF8.GetString result \ No newline at end of file