aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Communications/Cache/CryptoGridAssetClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Communications/Cache/CryptoGridAssetClient.cs')
-rw-r--r--OpenSim/Framework/Communications/Cache/CryptoGridAssetClient.cs559
1 files changed, 0 insertions, 559 deletions
diff --git a/OpenSim/Framework/Communications/Cache/CryptoGridAssetClient.cs b/OpenSim/Framework/Communications/Cache/CryptoGridAssetClient.cs
deleted file mode 100644
index 4e35f5b..0000000
--- a/OpenSim/Framework/Communications/Cache/CryptoGridAssetClient.cs
+++ /dev/null
@@ -1,559 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://www.openmetaverse.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27/*
28 * This file includes content derived from Obviex.
29 * Copyright (C) 2002 Obviex(TM). All rights reserved.
30 * http://www.obviex.com/samples/Encryption.aspx
31 */
32
33using System;
34using System.Collections.Generic;
35using System.IO;
36using System.Reflection;
37using System.Security.Cryptography;
38using System.Text;
39using System.Xml.Serialization;
40using log4net;
41using OpenSim.Framework.Servers.HttpServer;
42
43namespace OpenSim.Framework.Communications.Cache
44{
45 public class CryptoGridAssetClient : AssetServerBase
46 {
47
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49
50 private string _assetServerUrl;
51 private bool m_encryptOnUpload;
52 private RjinKeyfile m_encryptKey;
53 private readonly Dictionary<string,RjinKeyfile> m_keyfiles = new Dictionary<string, RjinKeyfile>();
54
55 #region IPlugin
56
57 public override string Name
58 {
59 get { return "Crypto"; }
60 }
61
62 public override string Version
63 {
64 get { return "1.0"; }
65 }
66
67 public override void Initialise(ConfigSettings p_set, string p_url, string p_dir, bool p_t)
68 {
69 m_log.Debug("[CRYPTOGRID] Plugin configured initialisation");
70 Initialise(p_url, p_dir, p_t);
71 }
72
73 #endregion
74
75 #region Keyfile Classes
76 [Serializable]
77 public class RjinKeyfile
78 {
79 public string Secret;
80 public string AlsoKnownAs;
81 public int Keysize;
82 public string IVBytes;
83 public string Description = "OpenSim Key";
84
85 private static string SHA1Hash(byte[] bytes)
86 {
87 SHA1 sha1 = SHA1CryptoServiceProvider.Create();
88 byte[] dataMd5 = sha1.ComputeHash(bytes);
89 StringBuilder sb = new StringBuilder();
90 for (int i = 0; i < dataMd5.Length; i++)
91 sb.AppendFormat("{0:x2}", dataMd5[i]);
92 return sb.ToString();
93 }
94
95 public void GenerateRandom()
96 {
97 RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();
98
99 byte[] genSec = new byte[32];
100 byte[] genAKA = new byte[32];
101 byte[] genIV = new byte[32];
102
103 Gen.GetBytes(genSec);
104 Gen.GetBytes(genAKA);
105 Gen.GetBytes(genIV);
106
107 Secret = SHA1Hash(genSec);
108 AlsoKnownAs = SHA1Hash(genAKA);
109 IVBytes = SHA1Hash(genIV).Substring(0, 16);
110 Keysize = 256;
111 }
112 }
113 #endregion
114
115 #region Rjindael
116 /// <summary>
117 /// This class uses a symmetric key algorithm (Rijndael/AES) to encrypt and
118 /// decrypt data. As long as encryption and decryption routines use the same
119 /// parameters to generate the keys, the keys are guaranteed to be the same.
120 /// The class uses static functions with duplicate code to make it easier to
121 /// demonstrate encryption and decryption logic. In a real-life application,
122 /// this may not be the most efficient way of handling encryption, so - as
123 /// soon as you feel comfortable with it - you may want to redesign this class.
124 /// </summary>
125 public class UtilRijndael
126 {
127 /// <summary>
128 /// Encrypts specified plaintext using Rijndael symmetric key algorithm
129 /// and returns a base64-encoded result.
130 /// </summary>
131 /// <param name="plainText">
132 /// Plaintext value to be encrypted.
133 /// </param>
134 /// <param name="passPhrase">
135 /// Passphrase from which a pseudo-random password will be derived. The
136 /// derived password will be used to generate the encryption key.
137 /// Passphrase can be any string. In this example we assume that this
138 /// passphrase is an ASCII string.
139 /// </param>
140 /// <param name="saltValue">
141 /// Salt value used along with passphrase to generate password. Salt can
142 /// be any string. In this example we assume that salt is an ASCII string.
143 /// </param>
144 /// <param name="hashAlgorithm">
145 /// Hash algorithm used to generate password. Allowed values are: "MD5" and
146 /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
147 /// </param>
148 /// <param name="passwordIterations">
149 /// Number of iterations used to generate password. One or two iterations
150 /// should be enough.
151 /// </param>
152 /// <param name="initVector">
153 /// Initialization vector (or IV). This value is required to encrypt the
154 /// first block of plaintext data. For RijndaelManaged class IV must be
155 /// exactly 16 ASCII characters long.
156 /// </param>
157 /// <param name="keySize">
158 /// Size of encryption key in bits. Allowed values are: 128, 192, and 256.
159 /// Longer keys are more secure than shorter keys.
160 /// </param>
161 /// <returns>
162 /// Encrypted value formatted as a base64-encoded string.
163 /// </returns>
164 public static byte[] Encrypt(byte[] plainText,
165 string passPhrase,
166 string saltValue,
167 string hashAlgorithm,
168 int passwordIterations,
169 string initVector,
170 int keySize)
171 {
172 // Convert strings into byte arrays.
173 // Let us assume that strings only contain ASCII codes.
174 // If strings include Unicode characters, use Unicode, UTF7, or UTF8
175 // encoding.
176 byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
177 byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
178
179 // Convert our plaintext into a byte array.
180 // Let us assume that plaintext contains UTF8-encoded characters.
181 byte[] plainTextBytes = plainText;
182
183 // First, we must create a password, from which the key will be derived.
184 // This password will be generated from the specified passphrase and
185 // salt value. The password will be created using the specified hash
186 // algorithm. Password creation can be done in several iterations.
187 PasswordDeriveBytes password = new PasswordDeriveBytes(
188 passPhrase,
189 saltValueBytes,
190 hashAlgorithm,
191 passwordIterations);
192
193 // Use the password to generate pseudo-random bytes for the encryption
194 // key. Specify the size of the key in bytes (instead
195 // of bits).
196 #pragma warning disable 0618
197 byte[] keyBytes = password.GetBytes(keySize / 8);
198 #pragma warning restore 0618
199
200 // Create uninitialized Rijndael encryption object.
201 RijndaelManaged symmetricKey = new RijndaelManaged();
202
203 // It is reasonable to set encryption mode to Cipher Block Chaining
204 // (CBC). Use default options for other symmetric key parameters.
205 symmetricKey.Mode = CipherMode.CBC;
206
207 // Generate encryptor from the existing key bytes and initialization
208 // vector. Key size will be defined based on the number of the key
209 // bytes.
210 ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
211 keyBytes,
212 initVectorBytes);
213
214 // Define memory stream which will be used to hold encrypted data.
215 MemoryStream memoryStream = new MemoryStream();
216
217 // Define cryptographic stream (always use Write mode for encryption).
218 CryptoStream cryptoStream = new CryptoStream(memoryStream,
219 encryptor,
220 CryptoStreamMode.Write);
221 // Start encrypting.
222 cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
223
224 // Finish encrypting.
225 cryptoStream.FlushFinalBlock();
226
227 // Convert our encrypted data from a memory stream into a byte array.
228 byte[] cipherTextBytes = memoryStream.ToArray();
229
230 // Close both streams.
231 memoryStream.Close();
232 cryptoStream.Close();
233
234 // Return encrypted string.
235 return cipherTextBytes;
236 }
237
238 /// <summary>
239 /// Decrypts specified ciphertext using Rijndael symmetric key algorithm.
240 /// </summary>
241 /// <param name="cipherText">
242 /// Base64-formatted ciphertext value.
243 /// </param>
244 /// <param name="passPhrase">
245 /// Passphrase from which a pseudo-random password will be derived. The
246 /// derived password will be used to generate the encryption key.
247 /// Passphrase can be any string. In this example we assume that this
248 /// passphrase is an ASCII string.
249 /// </param>
250 /// <param name="saltValue">
251 /// Salt value used along with passphrase to generate password. Salt can
252 /// be any string. In this example we assume that salt is an ASCII string.
253 /// </param>
254 /// <param name="hashAlgorithm">
255 /// Hash algorithm used to generate password. Allowed values are: "MD5" and
256 /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
257 /// </param>
258 /// <param name="passwordIterations">
259 /// Number of iterations used to generate password. One or two iterations
260 /// should be enough.
261 /// </param>
262 /// <param name="initVector">
263 /// Initialization vector (or IV). This value is required to encrypt the
264 /// first block of plaintext data. For RijndaelManaged class IV must be
265 /// exactly 16 ASCII characters long.
266 /// </param>
267 /// <param name="keySize">
268 /// Size of encryption key in bits. Allowed values are: 128, 192, and 256.
269 /// Longer keys are more secure than shorter keys.
270 /// </param>
271 /// <returns>
272 /// Decrypted string value.
273 /// </returns>
274 /// <remarks>
275 /// Most of the logic in this function is similar to the Encrypt
276 /// logic. In order for decryption to work, all parameters of this function
277 /// - except cipherText value - must match the corresponding parameters of
278 /// the Encrypt function which was called to generate the
279 /// ciphertext.
280 /// </remarks>
281 public static byte[] Decrypt(byte[] cipherText,
282 string passPhrase,
283 string saltValue,
284 string hashAlgorithm,
285 int passwordIterations,
286 string initVector,
287 int keySize)
288 {
289 // Convert strings defining encryption key characteristics into byte
290 // arrays. Let us assume that strings only contain ASCII codes.
291 // If strings include Unicode characters, use Unicode, UTF7, or UTF8
292 // encoding.
293 byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
294 byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
295
296 // Convert our ciphertext into a byte array.
297 byte[] cipherTextBytes = cipherText;
298
299 // First, we must create a password, from which the key will be
300 // derived. This password will be generated from the specified
301 // passphrase and salt value. The password will be created using
302 // the specified hash algorithm. Password creation can be done in
303 // several iterations.
304 PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase,
305 saltValueBytes,
306 hashAlgorithm,
307 passwordIterations);
308
309 // Use the password to generate pseudo-random bytes for the encryption
310 // key. Specify the size of the key in bytes (instead
311 // of bits).
312 #pragma warning disable 0618
313 byte[] keyBytes = password.GetBytes(keySize / 8);
314 #pragma warning restore 0618
315
316 // Create uninitialized Rijndael encryption object.
317 RijndaelManaged symmetricKey = new RijndaelManaged();
318
319 // It is reasonable to set encryption mode to Cipher Block Chaining
320 // (CBC). Use default options for other symmetric key parameters.
321 symmetricKey.Mode = CipherMode.CBC;
322
323 // Generate decryptor from the existing key bytes and initialization
324 // vector. Key size will be defined based on the number of the key
325 // bytes.
326 ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
327 keyBytes,
328 initVectorBytes);
329
330 // Define memory stream which will be used to hold encrypted data.
331 MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
332
333 // Define cryptographic stream (always use Read mode for encryption).
334 CryptoStream cryptoStream = new CryptoStream(memoryStream,
335 decryptor,
336 CryptoStreamMode.Read);
337
338 // Since at this point we don't know what the size of decrypted data
339 // will be, allocate the buffer long enough to hold ciphertext;
340 // plaintext is never longer than ciphertext.
341 byte[] plainTextBytes = new byte[cipherTextBytes.Length];
342
343 // Start decrypting.
344 int decryptedByteCount = cryptoStream.Read(plainTextBytes,
345 0,
346 plainTextBytes.Length);
347
348 // Close both streams.
349 memoryStream.Close();
350 cryptoStream.Close();
351
352 byte[] plainText = new byte[decryptedByteCount];
353 int i;
354 for (i = 0; i < decryptedByteCount; i++)
355 plainText[i] = plainTextBytes[i];
356
357 // Return decrypted string.
358 return plainText;
359 }
360 }
361 #endregion
362
363 public CryptoGridAssetClient() {}
364
365 public CryptoGridAssetClient(string serverUrl, string keydir, bool decOnly)
366 {
367 m_log.Debug("[CRYPTOGRID] Direct constructor");
368 Initialise(serverUrl, keydir, decOnly);
369 }
370
371 public void Initialise(string serverUrl, string keydir, bool decOnly)
372 {
373
374 m_log.Debug("[CRYPTOGRID] Common constructor");
375
376 _assetServerUrl = serverUrl;
377
378 string[] keys = Directory.GetFiles(keydir, "*.deckey");
379 foreach (string key in keys)
380 {
381 XmlSerializer xs = new XmlSerializer(typeof (RjinKeyfile));
382 FileStream file = new FileStream(key, FileMode.Open, FileAccess.Read);
383
384 RjinKeyfile rjkey = (RjinKeyfile) xs.Deserialize(file);
385
386 file.Close();
387
388 m_keyfiles.Add(rjkey.AlsoKnownAs, rjkey);
389 }
390
391
392 keys = Directory.GetFiles(keydir, "*.enckey");
393 if (keys.Length == 1)
394 {
395 string Ekey = keys[0];
396 XmlSerializer Exs = new XmlSerializer(typeof (RjinKeyfile));
397 FileStream Efile = new FileStream(Ekey, FileMode.Open, FileAccess.Read);
398
399 RjinKeyfile Erjkey = (RjinKeyfile) Exs.Deserialize(Efile);
400
401 Efile.Close();
402
403 m_keyfiles.Add(Erjkey.AlsoKnownAs, Erjkey);
404
405 m_encryptKey = Erjkey;
406 } else
407 {
408 if (keys.Length > 1)
409 throw new Exception(
410 "You have more than one asset *encryption* key. (You should never have more than one)," +
411 "If you downloaded this key from someone, rename it to <filename>.deckey to convert it to" +
412 "a decryption-only key.");
413
414 m_log.Warn("No encryption key found, generating a new one for you...");
415 RjinKeyfile encKey = new RjinKeyfile();
416 encKey.GenerateRandom();
417
418 m_encryptKey = encKey;
419
420 FileStream encExportFile = new FileStream("mysecretkey_rename_me.enckey",FileMode.CreateNew);
421 XmlSerializer xs = new XmlSerializer(typeof(RjinKeyfile));
422 xs.Serialize(encExportFile, encKey);
423 encExportFile.Flush();
424 encExportFile.Close();
425
426 m_log.Info(
427 "Encryption file generated, please rename 'mysecretkey_rename_me.enckey' to something more appropriate (however preserve the file extension).");
428 }
429
430 // If Decrypt-Only, dont encrypt on upload
431 m_encryptOnUpload = !decOnly;
432 }
433
434 private static void EncryptAssetBase(AssetBase x, RjinKeyfile file)
435 {
436 // Make a salt
437 RNGCryptoServiceProvider RandomGen = new RNGCryptoServiceProvider();
438 byte[] rand = new byte[32];
439 RandomGen.GetBytes(rand);
440
441 string salt = Convert.ToBase64String(rand);
442
443 x.Data = UtilRijndael.Encrypt(x.Data, file.Secret, salt, "SHA1", 2, file.IVBytes, file.Keysize);
444 x.Description = String.Format("ENCASS#:~:#{0}#:~:#{1}#:~:#{2}#:~:#{3}",
445 "OPENSIM_AES_AF1",
446 file.AlsoKnownAs,
447 salt,
448 x.Description);
449 }
450
451 private bool DecryptAssetBase(AssetBase x)
452 {
453 // Check it's encrypted first.
454 if (!x.Description.Contains("ENCASS"))
455 return true;
456
457 // ENCASS:ALG:AKA:SALT:Description
458 // 0 1 2 3 4
459 string[] splitchars = new string[1];
460 splitchars[0] = "#:~:#";
461
462 string[] meta = x.Description.Split(splitchars, StringSplitOptions.None);
463 if (meta.Length < 5)
464 {
465 m_log.Warn("[ENCASSETS] Recieved Encrypted Asset, but header is corrupt");
466 return false;
467 }
468
469 // Check if we have a matching key
470 if (m_keyfiles.ContainsKey(meta[2]))
471 {
472 RjinKeyfile deckey = m_keyfiles[meta[2]];
473 x.Description = meta[4];
474 switch (meta[1])
475 {
476 case "OPENSIM_AES_AF1":
477 x.Data = UtilRijndael.Decrypt(x.Data,
478 deckey.Secret,
479 meta[3],
480 "SHA1",
481 2,
482 deckey.IVBytes,
483 deckey.Keysize);
484 // Decrypted Successfully
485 return true;
486 default:
487 m_log.Warn(
488 "[ENCASSETS] Recieved Encrypted Asset, but we dont know how to decrypt '" + meta[1] + "'.");
489 // We dont understand this encryption scheme
490 return false;
491 }
492 }
493
494 m_log.Warn("[ENCASSETS] Recieved Encrypted Asset, but we do not have the decryption key.");
495 return false;
496 }
497
498 #region IAssetServer Members
499
500 protected override AssetBase GetAsset(AssetRequest req)
501 {
502#if DEBUG
503 //m_log.DebugFormat("[GRID ASSET CLIENT]: Querying for {0}", req.AssetID.ToString());
504#endif
505
506 RestClient rc = new RestClient(_assetServerUrl);
507 rc.AddResourcePath("assets");
508 rc.AddResourcePath(req.AssetID.ToString());
509 if (req.IsTexture)
510 rc.AddQueryParameter("texture");
511
512 rc.RequestMethod = "GET";
513
514 Stream s = rc.Request();
515
516 if (s == null)
517 return null;
518
519 if (s.Length > 0)
520 {
521 XmlSerializer xs = new XmlSerializer(typeof(AssetBase));
522
523 AssetBase encAsset = (AssetBase)xs.Deserialize(s);
524
525 // Try decrypt it
526 if (DecryptAssetBase(encAsset))
527 return encAsset;
528 }
529
530 return null;
531 }
532
533 public override void UpdateAsset(AssetBase asset)
534 {
535 throw new Exception("The method or operation is not implemented.");
536 }
537
538 public override void StoreAsset(AssetBase asset)
539 {
540 if (m_encryptOnUpload)
541 EncryptAssetBase(asset, m_encryptKey);
542
543 try
544 {
545 string assetUrl = _assetServerUrl + "/assets/";
546
547 m_log.InfoFormat("[CRYPTO GRID ASSET CLIENT]: Sending store request for asset {0}", asset.FullID);
548
549 RestObjectPoster.BeginPostObject<AssetBase>(assetUrl, asset);
550 }
551 catch (Exception e)
552 {
553 m_log.ErrorFormat("[CRYPTO GRID ASSET CLIENT]: {0}", e);
554 }
555 }
556
557 #endregion
558 }
559}