WingMan – Diff between revs 10 and 12
?pathlinks?
Rev 10 | Rev 12 | |||
---|---|---|---|---|
1 | using System; |
1 | using System; |
|
2 | using System.IO; |
2 | using System.IO; |
|
- | 3 | using System.Linq; |
||
3 | using System.Security.Cryptography; |
4 | using System.Security.Cryptography; |
|
4 | using System.Text; |
5 | using System.Text; |
|
- | 6 | using System.Threading.Tasks; |
||
5 | |
7 | |
|
6 | namespace WingMan.Utilities |
8 | namespace WingMan.Utilities |
|
7 | { |
9 | { |
|
8 | public static class AES |
10 | public static class AES |
|
9 | { |
11 | { |
|
- | 12 | private const int AesKeyIterations = 4096; |
||
10 | private const int AesBlockSize = 128; |
13 | private const int AesBlockSize = 128; |
|
11 | private const CipherMode AesCipherMode = CipherMode.CBC; |
14 | private const CipherMode AesCipherMode = CipherMode.CBC; |
|
12 | private const PaddingMode AesPaddingMode = PaddingMode.PKCS7; |
15 | private const PaddingMode AesPaddingMode = PaddingMode.PKCS7; |
|
13 | private const int AesKeySaltBytes = 16; |
16 | private const int AesKeySaltBytes = 16; |
|
14 | private static readonly RNGCryptoServiceProvider Rng = new RNGCryptoServiceProvider(); |
17 | private static readonly RNGCryptoServiceProvider Rng = new RNGCryptoServiceProvider(); |
|
15 | |
18 | |
|
16 | /////////////////////////////////////////////////////////////////////////// |
19 | /////////////////////////////////////////////////////////////////////////// |
|
17 | // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
20 | // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
|
18 | /////////////////////////////////////////////////////////////////////////// |
21 | /////////////////////////////////////////////////////////////////////////// |
|
19 | /// <summary> |
22 | /// <summary> |
|
20 | /// Encrypts a string given a key and initialization vector. |
23 | /// Encrypts a string given a key and initialization vector. |
|
21 | /// </summary> |
24 | /// </summary> |
|
22 | /// <param name="data">the string to encrypt</param> |
25 | /// <param name="data">the string to encrypt</param> |
|
23 | /// <param name="key">the encryption key</param> |
26 | /// <param name="key">the encryption key</param> |
|
24 | /// <param name="separator">the separator to use between the cyphertext and the IV</param> |
27 | /// <param name="separator">the separator to use between the cyphertext and the IV</param> |
|
25 | /// <returns>Base64 encoded encrypted data</returns> |
28 | /// <returns>Base64 encoded encrypted data</returns> |
|
26 | public static byte[] Encrypt(byte[] data, string key, string separator = ":") |
29 | public static async Task<byte[]> Encrypt(byte[] data, string key, string separator = ":") |
|
27 | { |
30 | { |
|
28 | using (var rijdanelManaged = new RijndaelManaged()) |
31 | using (var rijdanelManaged = new RijndaelManaged()) |
|
29 | { |
32 | { |
|
30 | // FIPS-197 / CBC |
33 | // FIPS-197 / CBC |
|
31 | rijdanelManaged.BlockSize = AesBlockSize; |
34 | rijdanelManaged.BlockSize = AesBlockSize; |
|
32 | rijdanelManaged.Mode = AesCipherMode; |
35 | rijdanelManaged.Mode = AesCipherMode; |
|
33 | rijdanelManaged.Padding = AesPaddingMode; |
36 | rijdanelManaged.Padding = AesPaddingMode; |
|
34 | |
37 | |
|
35 | // Compute the salt and the IV from the key. |
38 | // Compute the salt and the IV from the key. |
|
36 | var salt = new byte[AesKeySaltBytes]; |
39 | var salt = new byte[AesKeySaltBytes]; |
|
37 | Rng.GetBytes(salt); |
40 | Rng.GetBytes(salt); |
|
38 | using (var derivedKey = new Rfc2898DeriveBytes(key, salt)) |
41 | var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations); |
|
39 | { |
- | ||
40 | rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8); |
42 | rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8); |
|
41 | rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8); |
43 | rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8); |
|
42 | |
44 | |
|
- | 45 | using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV)) |
||
- | 46 | { |
||
43 | using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV)) |
47 | using (var memoryStream = new MemoryStream()) |
|
44 | { |
48 | { |
|
45 | using (var memoryStream = new MemoryStream()) |
49 | using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) |
|
46 | { |
50 | { |
|
47 | using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) |
51 | using (var inputStream = new MemoryStream(data)) |
|
48 | { |
- | ||
49 | using (var inputStream = new MemoryStream(data)) |
- | ||
50 | { |
52 | { |
|
51 | inputStream.CopyTo(cryptoStream); |
53 | await inputStream.CopyToAsync(cryptoStream); |
|
52 | cryptoStream.FlushFinalBlock(); |
- | ||
53 | |
- | ||
54 | inputStream.Position = 0L; |
54 | cryptoStream.FlushFinalBlock(); |
|
55 | |
55 | |
|
56 | var payload = memoryStream.ToArray(); |
- | ||
57 | |
- | ||
58 | var base64Salt = Convert.ToBase64String(salt); |
- | ||
59 | var base64Payload = Convert.ToBase64String(payload); |
- | ||
60 | |
56 | inputStream.Position = 0L; |
|
61 | return Encoding.UTF8.GetBytes($"{base64Salt}{separator}{base64Payload}"); |
57 | |
|
62 | } |
58 | return salt.Concat(memoryStream.ToArray()).ToArray(); |
|
63 | } |
59 | } |
|
64 | } |
60 | } |
|
65 | } |
61 | } |
|
66 | } |
62 | } |
|
67 | } |
63 | } |
|
68 | } |
64 | } |
|
69 | |
65 | |
|
70 | /////////////////////////////////////////////////////////////////////////// |
66 | /////////////////////////////////////////////////////////////////////////// |
|
71 | // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
67 | // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 // |
|
72 | /////////////////////////////////////////////////////////////////////////// |
68 | /////////////////////////////////////////////////////////////////////////// |
|
73 | /// <summary> |
69 | /// <summary> |
|
74 | /// Decrypts a Base64 encoded string using AES with a given key and initialization vector. |
70 | /// Decrypts a Base64 encoded string using AES with a given key and initialization vector. |
|
75 | /// </summary> |
71 | /// </summary> |
|
76 | /// <param name="data"> |
72 | /// <param name="data"> |
|
77 | /// a string consisting of the cyphertext to decrypt in Base64 and the IV in Base64 separated by the |
73 | /// a string consisting of the cyphertext to decrypt in Base64 and the IV in Base64 separated by the |
|
78 | /// separator |
74 | /// separator |
|
79 | /// </param> |
75 | /// </param> |
|
80 | /// <param name="key">the encryption key</param> |
76 | /// <param name="key">the encryption key</param> |
|
81 | /// <param name="separator">the separator to use between the cyphertext and the IV</param> |
77 | /// <param name="separator">the separator to use between the cyphertext and the IV</param> |
|
82 | /// <returns>the decrypted data</returns> |
78 | /// <returns>the decrypted data</returns> |
|
83 | public static byte[] Decrypt(byte[] data, string key, string separator = ":") |
79 | public static async Task<byte[]> Decrypt(byte[] data, string key, string separator = ":") |
|
84 | { |
80 | { |
|
85 | var input = Encoding.UTF8.GetString(data); |
81 | var salt = data.Take(AesKeySaltBytes).ToArray(); |
|
86 | |
- | ||
87 | // retrieve the salt from the data. |
- | ||
88 | var segments = input.Split(new[] {separator}, StringSplitOptions.None); |
82 | var text = data.Skip(AesKeySaltBytes).ToArray(); |
|
89 | if (segments.Length != 2) |
- | ||
90 | throw new ArgumentException("Invalid data: " + input); |
- | ||
91 | |
83 | |
|
92 | using (var rijdanelManaged = new RijndaelManaged()) |
84 | using (var rijdanelManaged = new RijndaelManaged()) |
|
93 | { |
85 | { |
|
94 | // FIPS-197 / CBC |
86 | // FIPS-197 / CBC |
|
95 | rijdanelManaged.BlockSize = AesBlockSize; |
87 | rijdanelManaged.BlockSize = AesBlockSize; |
|
96 | rijdanelManaged.Mode = AesCipherMode; |
88 | rijdanelManaged.Mode = AesCipherMode; |
|
97 | rijdanelManaged.Padding = AesPaddingMode; |
89 | rijdanelManaged.Padding = AesPaddingMode; |
|
98 | |
90 | |
|
99 | // Retrieve the key and the IV from the salt. |
91 | // Retrieve the key and the IV from the salt. |
|
100 | using (var derivedKey = new Rfc2898DeriveBytes(key, Convert.FromBase64String(segments[0].Trim()))) |
92 | var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations); |
|
101 | { |
- | ||
102 | rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8); |
93 | rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8); |
|
103 | rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8); |
94 | rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8); |
|
104 | |
95 | |
|
- | 96 | using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV)) |
||
- | 97 | { |
||
105 | using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV)) |
98 | using (var memoryStream = new MemoryStream(text)) |
|
106 | { |
99 | { |
|
107 | using (var memoryStream = new MemoryStream(Convert.FromBase64String(segments[1].Trim()))) |
100 | using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) |
|
108 | { |
101 | { |
|
109 | using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) |
102 | using (var outputStream = new MemoryStream()) |
|
110 | { |
103 | { |
|
- | 104 | await cryptoStream.CopyToAsync(outputStream); |
||
111 | using (var streamReader = new StreamReader(cryptoStream)) |
105 | |
|
- | 106 | outputStream.Position = 0L; |
||
112 | { |
107 | |
|
113 | return Encoding.UTF8.GetBytes(streamReader.ReadToEnd()); |
- | ||
114 | } |
108 | return outputStream.ToArray(); |
|
115 | } |
109 | } |
|
116 | } |
110 | } |
|
117 | } |
111 | } |
|
118 | } |
112 | } |
|
119 | } |
113 | } |
|
120 | } |
114 | } |
|
121 | |
115 | |
|
122 | public static string ExpandKey(string password, int size = 32) |
116 | public static string ExpandKey(string password, int size = 32) |
|
123 | { |
117 | { |
|
124 | var sb = new StringBuilder(password); |
118 | var sb = new StringBuilder(password); |
|
125 | do |
119 | do |
|
126 | { |
120 | { |
|
127 | sb.Append(password); |
121 | sb.Append(password); |
|
128 | } while (sb.Length < size); |
122 | } while (sb.Length < size); |
|
129 | |
123 | |
|
130 | return sb.ToString(0, size); |
124 | return sb.ToString(0, size); |
|
131 | } |
125 | } |
|
132 | } |
126 | } |
|
133 | } |
127 | } |
|
134 | |
128 | |