Add the F# implementation.
[rup.git] / WebSharper / Crypto.fs
1 module RUP.Crypto
2
3 open System
4 open System.Net
5 open System.IO
6 open System.Security.Cryptography
7 open System.Text
8 open System.Web
9
10 let keySize = 128 // [bit].
11
12 type Key = string
13
14 let randBytes (nb : int) : byte array =
15 let result = Array.zeroCreate nb
16 use generator = new RNGCryptoServiceProvider ()
17 generator.GetBytes result
18 result
19
20 let generateKey () : Key =
21 let bytes = randBytes (keySize / 8)
22 Convert.ToBase64String bytes |> WebUtility.UrlEncode
23
24 let encrypt (key : Key) (name : string) : string =
25 let keyAsBytes = WebUtility.UrlDecode key |> Convert.FromBase64String
26 let iv = randBytes (keySize / 8)
27
28 use aes = new AesCryptoServiceProvider (KeySize = keySize) // Default mode is CBC.
29 use stream = new MemoryStream ()
30 use cryptoStream = new CryptoStream (stream, aes.CreateEncryptor (keyAsBytes, iv), CryptoStreamMode.Write)
31 let nameAsBytes = Encoding.UTF8.GetBytes name
32
33 use sha = SHA256.Create ()
34 let hash = sha.ComputeHash nameAsBytes
35
36 cryptoStream.Write (nameAsBytes, 0, nameAsBytes.Length)
37 cryptoStream.FlushFinalBlock ()
38 stream.Position <- 0L
39
40 let result = Array.zeroCreate (iv.Length + hash.Length + int stream.Length)
41
42 Array.blit iv 0 result 0 iv.Length
43 Array.blit hash 0 result iv.Length hash.Length
44
45 stream.Read (result, iv.Length + hash.Length, int stream.Length) |> ignore
46 "1" + Convert.ToBase64String result |> WebUtility.UrlEncode
47
48 let decrypt (key : Key) (cipher : string) (urlDecode : bool) : string =
49 let version = cipher.Substring (0, 1) |> int
50 if version <> 1 then
51 failwithf "Version not supported: %i" version
52
53 let keyAsBytes = WebUtility.UrlDecode key |> Convert.FromBase64String
54 let bytes = cipher.Substring 1 |> (if urlDecode then WebUtility.UrlDecode else id) |> Convert.FromBase64String
55
56 let iv = Array.zeroCreate (keySize / 8)
57 Array.blit bytes 0 iv 0 iv.Length
58
59 let hash = Array.zeroCreate 32
60 Array.blit bytes iv.Length hash 0 hash.Length
61
62 use aes = new AesCryptoServiceProvider (KeySize = keySize)
63 use stream = new MemoryStream (bytes, iv.Length + hash.Length, bytes.Length - iv.Length - hash.Length)
64 use cryptoStream = new CryptoStream (stream, aes.CreateDecryptor (keyAsBytes, iv), CryptoStreamMode.Read)
65 use streamResult = new MemoryStream ()
66 cryptoStream.CopyTo streamResult
67 streamResult.Position <- 0L
68 let result = Array.zeroCreate (int streamResult.Length)
69 streamResult.Read (result, 0, result.Length) |> ignore
70
71 use sha = SHA256.Create ()
72 let computedHash = sha.ComputeHash result
73
74 if hash <> computedHash then
75 failwith "hash mismatch"
76
77 Encoding.UTF8.GetString result