f85825faaad12687ff600e06f1f06cbbd13b73bf
[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 // TODO: return a Result<string, EncryptError>
25 let encrypt (key : Key) (name : string) : string =
26 let keyAsBytes = WebUtility.UrlDecode key |> Convert.FromBase64String
27 let iv = randBytes (keySize / 8)
28
29 use aes = new AesCryptoServiceProvider (KeySize = keySize) // Default mode is CBC.
30 use stream = new MemoryStream ()
31 use cryptoStream = new CryptoStream (stream, aes.CreateEncryptor (keyAsBytes, iv), CryptoStreamMode.Write)
32 let nameAsBytes = Encoding.UTF8.GetBytes name
33
34 use sha = SHA256.Create ()
35 let hash = sha.ComputeHash nameAsBytes
36
37 cryptoStream.Write (nameAsBytes, 0, nameAsBytes.Length)
38 cryptoStream.FlushFinalBlock ()
39 stream.Position <- 0L
40
41 let result = Array.zeroCreate (iv.Length + hash.Length + int stream.Length)
42
43 Array.blit iv 0 result 0 iv.Length
44 Array.blit hash 0 result iv.Length hash.Length
45
46 stream.Read (result, iv.Length + hash.Length, int stream.Length) |> ignore
47 "1" + Convert.ToBase64String result |> WebUtility.UrlEncode
48
49 // TODO: return a Result<string, DecryptError>
50 let decrypt (key : Key) (cipher : string) (urlDecode : bool) : string =
51 let version = cipher.Substring (0, 1) |> int
52 if version <> 1 then
53 failwithf "Version not supported: %i" version
54
55 let keyAsBytes = WebUtility.UrlDecode key |> Convert.FromBase64String
56 let bytes = cipher.Substring 1 |> (if urlDecode then WebUtility.UrlDecode else id) |> Convert.FromBase64String
57
58 let iv = Array.zeroCreate (keySize / 8)
59 Array.blit bytes 0 iv 0 iv.Length
60
61 let hash = Array.zeroCreate 32
62 Array.blit bytes iv.Length hash 0 hash.Length
63
64 use aes = new AesCryptoServiceProvider (KeySize = keySize)
65 use stream = new MemoryStream (bytes, iv.Length + hash.Length, bytes.Length - iv.Length - hash.Length)
66 use cryptoStream = new CryptoStream (stream, aes.CreateDecryptor (keyAsBytes, iv), CryptoStreamMode.Read)
67 use streamResult = new MemoryStream ()
68 cryptoStream.CopyTo streamResult
69 streamResult.Position <- 0L
70 let result = Array.zeroCreate (int streamResult.Length)
71 streamResult.Read (result, 0, result.Length) |> ignore
72
73 use sha = SHA256.Create ()
74 let computedHash = sha.ComputeHash result
75
76 if hash <> computedHash then
77 failwith "hash mismatch"
78
79 Encoding.UTF8.GetString result