예제 #1
0
        public void TestRfc2898DeriveBytes()
        {
            byte[] password = new byte[20];
            byte[] salt = new byte[20];
            int bytes = 64;

            for (int iterations = 1000; iterations <= 10000; iterations += 1000)
            {
                Tester.RandomGenerator.NextBytes(password);
                Tester.RandomGenerator.NextBytes(salt);

                using (PBKDF2<HMACSHA1> pbkdf2 = new PBKDF2<HMACSHA1>(password, salt, iterations))
                using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, salt, iterations))
                {
                    byte[] pbkdf2Bytes = pbkdf2.GetBytes(bytes);
                    byte[] rfc2898Bytes = rfc2898.GetBytes(bytes);

                    Assert.AreEqual(bytes, rfc2898Bytes.Length);
                    Assert.AreEqual(bytes, pbkdf2Bytes.Length);

                    for (int i = 0; i < bytes; i++)
                        Assert.AreEqual(rfc2898Bytes[i], pbkdf2Bytes[i]);
                }
            }
        }
        // the core function of the PBKDF which does all the iterations
        // per the spec section 5.2 step 3
        private byte[] _F(uint I)
        {
            //NOTE: SPEC IS MISLEADING!!!
            //THE HMAC FUNCTIONS ARE KEYED BY THE PASSWORD! NEVER THE SALT!
            byte[] bufferU   = null;
            byte[] bufferOut = null;
            byte[] _int      = PBKDF2 <T> .IntToBytes(I);

            _hmac     = new T();
            _hmac.Key = (_P);     // KEY BY THE PASSWORD!
            _hmac.TransformBlock(_S, 0, _S.Length, _S, 0);
            _hmac.TransformFinalBlock(_int, 0, _int.Length);
            bufferU   = _hmac.Hash;
            bufferOut = (byte[])bufferU.Clone();
            for (int c = 1; c < _C; c++)
            {
                _hmac.Initialize();
                _hmac.Key = _P;      // KEY BY THE PASSWORD!
                bufferU   = _hmac.ComputeHash(bufferU);
                _Xor(ref bufferOut, bufferU);
            }
            return(bufferOut);
        }
		public void PBKDF2_SHA512()
		{
			// tests with salt less than 8 bytes are skipped since our implementation throws on such weak salts.

			var result = new PBKDF2(HMACFactories.HMACSHA512,
					password: Encoding.ASCII.GetBytes("passwordPASSWORDpassword"),
					salt: Encoding.ASCII.GetBytes("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
					iterations: 4096).GetBytes(64);

			var expected = "8c 05 11 f4 c6 e5 97 c6 ac 63 15 d8 f0 36 2e 22 5f 3c 50 14 95 ba 23 b8 68 c0 05 17 4d c4 ee 71 11 5b 59 f9 e6 0c d9 53 2f a3 3e 0f 75 ae fe 30 22 5c 58 3a 18 6c d8 2b d4 da ea 97 24 a3 d3 b8".Replace(" ", "").FromBase16();
			Assert.IsTrue(Enumerable.SequenceEqual(expected, result));
		}
		public void PBKDF2_SHA256()
		{
			// tests with salt less than 8 bytes are skipped since our implementation throws on such weak salts.

			var result = new PBKDF2(HMACFactories.HMACSHA256,
					password: Encoding.ASCII.GetBytes("passwordPASSWORDpassword"),
					salt: Encoding.ASCII.GetBytes("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
					iterations: 4096).GetBytes(40);

			var expected = "34 8c 89 db cb d3 2b 2f 32 d8 14 b8 11 6e 84 cf 2b 17 34 7e bc 18 00 18 1c 4e 2a 1f b8 dd 53 e1 c6 35 51 8c 7d ac 47 e9".Replace(" ", "").FromBase16();
			Assert.IsTrue(Enumerable.SequenceEqual(expected, result));
		}
		public void PBKDF2_SHA1()
		{
			// tests with salt less than 8 bytes are skipped since our implementation throws on such weak salts.

			var result = new PBKDF2(HMACFactories.HMACSHA1,
					password: Encoding.ASCII.GetBytes("passwordPASSWORDpassword"),
					salt: Encoding.ASCII.GetBytes("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
					iterations: 4096).GetBytes(25);

			var expected = "3d 2e ec 4f e4 1c 84 9b 80 c8 d8 36 62 c0 e4 4a 8b 29 1a 96 4c f2 f0 70 38".Replace(" ", "").FromBase16();
			Assert.IsTrue(Enumerable.SequenceEqual(expected, result));
		}
예제 #6
0
		public static void TestPBKDF2()
		{
			PBKDF2 kg;
			byte[] DK;

			byte[] tv1 = new byte[] { 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, 0x2f, 0xe0, 0x37, 0xa6 };
			kg = new PBKDF2(Encoding.Default.GetBytes("password"), Encoding.Default.GetBytes("salt"), 1);
			DK = kg.GetBytes(20);
			Assert.AreEqual(DK, tv1);

			byte[] tv2 = new byte[] { 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, 0xd8, 0xde, 0x89, 0x57 };
			kg = new PBKDF2(Encoding.Default.GetBytes("password"), Encoding.Default.GetBytes("salt"), 2);
			DK = kg.GetBytes(20);
			Assert.AreEqual(DK, tv2);

			byte[] tv3 = new byte[] { 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, 0x65, 0xa4, 0x29, 0xc1 };
			kg = new PBKDF2(Encoding.Default.GetBytes("password"), Encoding.Default.GetBytes("salt"), 4096);
			DK = kg.GetBytes(20);
			Assert.AreEqual(DK, tv3);

			byte[] tv4 = new byte[] { 0x2f, 0x25, 0x5b, 0x3a, 0x95, 0x46, 0x3c, 0x76, 0x62, 0x1f, 0x06, 0x80, 0xa2, 0xb3, 0x35, 0xad, 0x90, 0x3b, 0x85, 0xde };
			kg = new PBKDF2(Encoding.Default.GetBytes("VXFr[24c=6(D8He"), Encoding.Default.GetBytes("salt"), 1000);
			DK = kg.GetBytes(20);
			Assert.AreEqual(DK, tv4);
		}
예제 #7
0
		/// <summary>
		/// Decrypt a hex-coded string using our MD5 or PBKDF2 generated key
		/// </summary>
		/// <param name="data">data string to be decrypted</param>
		/// <param name="key">decryption key</param>
		/// <param name="PBKDF2">flag to indicate we are using PBKDF2 to generate derived key</param>
		/// <returns>hex coded decrypted string</returns>
		public static string Decrypt(string data, string password, bool PBKDF2)
		{
			byte[] key;
			byte[] saltBytes = Authenticator.StringToByteArray(data.Substring(0, SALT_LENGTH * 2));

			if (PBKDF2 == true)
			{
				// extract the salt from the data
				byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

				// build our PBKDF2 key
#if NETCF
			PBKDF2 kg = new PBKDF2(passwordBytes, saltbytes, 2000);
#else
				Rfc2898DeriveBytes kg = new Rfc2898DeriveBytes(passwordBytes, saltBytes, PBKDF2_ITERATIONS);
#endif
				key = kg.GetBytes(PBKDF2_KEYSIZE);
			}
			else
			{
				// extract the salt from the data
				byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
				key = new byte[saltBytes.Length + passwordBytes.Length];
				Array.Copy(saltBytes, key, saltBytes.Length);
				Array.Copy(passwordBytes, 0, key, saltBytes.Length, passwordBytes.Length);
				// build out combined key
				SHA256Managed md5 =new SHA256Managed();
				key = md5.ComputeHash(key);
			}

			// extract the actual data to be decrypted
			byte[] inBytes = Authenticator.StringToByteArray(data.Substring(SALT_LENGTH * 2));

			// get cipher
			BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new BlowfishEngine(), new ISO10126d2Padding());
			cipher.Init(false, new KeyParameter(key));

			// decrypt the data
			int osize = cipher.GetOutputSize(inBytes.Length);
			byte[] outBytes = new byte[osize];
			try
			{
				int olen = cipher.ProcessBytes(inBytes, 0, inBytes.Length, outBytes, 0);
				olen += cipher.DoFinal(outBytes, olen);
				if (olen < osize)
				{
					byte[] t = new byte[olen];
					Array.Copy(outBytes, 0, t, 0, olen);
					outBytes = t;
				}
			}
			catch (Exception)
			{
				// an exception is due to bad password
				throw new BadPasswordException();
			}

			// return encoded string
			return Authenticator.ByteArrayToString(outBytes);
		}
예제 #8
0
		/// <summary>
		/// Encrypt a string with a given key
		/// </summary>
		/// <param name="plain">data to encrypt - hex representation of byte array</param>
		/// <param name="key">key to use to encrypt</param>
		/// <returns>hex coded encrypted string</returns>
		public static string Encrypt(string plain, string password)
		{
			byte[] inBytes = Authenticator.StringToByteArray(plain);
			byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

			// build a new salt
			RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider();
			byte[] saltbytes = new byte[SALT_LENGTH];
			rg.GetBytes(saltbytes);
			string salt = Authenticator.ByteArrayToString(saltbytes);

			// build our PBKDF2 key
#if NETCF
			PBKDF2 kg = new PBKDF2(passwordBytes, saltbytes, PBKDF2_ITERATIONS);
#else
			Rfc2898DeriveBytes kg = new Rfc2898DeriveBytes(passwordBytes, saltbytes, PBKDF2_ITERATIONS);
#endif
			byte[] key = kg.GetBytes(PBKDF2_KEYSIZE);

			// get our cipher
			BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new BlowfishEngine(), new ISO10126d2Padding());
			cipher.Init(true, new KeyParameter(key));

			// encrypt data
			int osize = cipher.GetOutputSize(inBytes.Length);
			byte[] outBytes = new byte[osize];
			int olen = cipher.ProcessBytes(inBytes, 0, inBytes.Length, outBytes, 0);
			olen += cipher.DoFinal(outBytes, olen);
			if (olen < osize)
			{
				byte[] t = new byte[olen];
				Array.Copy(outBytes, 0, t, 0, olen);
				outBytes = t;
			}

			// return encoded byte->hex string
			return salt + Authenticator.ByteArrayToString(outBytes);
		}
예제 #9
0
		/// <summary>
		/// Encrypt a string with a given key
		/// </summary>
		/// <param name="plain">data to encrypt - hex representation of byte array</param>
		/// <param name="password">key to use to encrypt</param>
		/// <returns>hex coded encrypted string</returns>
		public static string Encrypt(string plain, string password)
		{
			byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

			// build a new salt
			RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider();
			byte[] saltbytes = new byte[SALT_LENGTH];
			rg.GetBytes(saltbytes);
			string salt = Authenticator.ByteArrayToString(saltbytes);

			// build our PBKDF2 key
#if NETCF
			PBKDF2 kg = new PBKDF2(passwordBytes, saltbytes, PBKDF2_ITERATIONS);
#else
			Rfc2898DeriveBytes kg = new Rfc2898DeriveBytes(passwordBytes, saltbytes, PBKDF2_ITERATIONS);
#endif
			byte[] key = kg.GetBytes(PBKDF2_KEYSIZE);

			return salt + Encrypt(plain, key);
		}
		public void PBKDF2_SHA512()
		{
			// tests with salt less than 8 bytes are skipped since our implementation throws on such weak salts.
			var hmacFactories = new Func<HMAC>[] { () => new HMACSHA512(), HMACFactories.HMACSHA512 };
			foreach (var hmacFactory in hmacFactories)
			{
				var result = new PBKDF2(hmacFactory,
					password: Encoding.ASCII.GetBytes("passwordPASSWORDpassword"),
					salt: Encoding.ASCII.GetBytes("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
					iterations: 4096).GetBytes(64);

				var expected = "8c 05 11 f4 c6 e5 97 c6 ac 63 15 d8 f0 36 2e 22 5f 3c 50 14 95 ba 23 b8 68 c0 05 17 4d c4 ee 71 11 5b 59 f9 e6 0c d9 53 2f a3 3e 0f 75 ae fe 30 22 5c 58 3a 18 6c d8 2b d4 da ea 97 24 a3 d3 b8".Replace(" ", "").FromBase16();
				Assert.IsTrue(Enumerable.SequenceEqual(expected, result));

				result = new PBKDF2(hmacFactory,
						password: Encoding.ASCII.GetBytes("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04Uz3ebEAhzZ4ve1A2wg5CnLXdZC5Y7gwfVgbEgZSTmoYQSzC5OW4dfrjqiwApTACO6xoOL1AjWj6X6f6qFfF8TVmOzU9RhOd1N4QtzWI4fP6FYttNz5FuLdtYVXWVXH2Tf7I9fieMeWCHTMkM4VcmQyQHpbcP8MEb5f1g6Ckg5xk3HQr3wMBvQcOHpCPy1K8HCM7a5wkPDhgVA0BVmwNpsRIbDQZRtHK6dT6bGyalp6gbFZBuBHwD86gTzkrFY7HkOVrgc0gJcGJZe65Ce8v4Jn5OzkuVsiU8efm2Pw2RnbpWSAr7SkVdCwXK2XSJDQ5fZ4HBEz9VTFYrG23ELuLjvx5njOLNgDAJuf5JB2tn4nMjjcnl1e8qcYVwZqFzEv2zhLyDWMkV4tzl4asLnvyAxTBkxPRZj2pRABWwb3kEofpsHYxMTAn38YSpZreoXipZWBnu6HDURaruXaIPYFPYHl9Ls9wsuD7rzaGfbOyfVgLIGK5rODphwRA7lm88bGKY8b7tWOtepyEvaLxMI7GZF5ScwpZTYeEDNUKPzvM2Im9zehIaznpguNdNXNMLWnwPu4H6zEvajkw3G3ucSiXKmh6XNe3hkdSANm3vnxzRXm4fcuzAx68IElXE2bkGFElluDLo6EsUDWZ4JIWBVaDwYdJx8uCXbQdoifzCs5kuuClaDaDqIhb5hJ2WR8mxiueFsS0aDGdIYmye5svmNmzQxFmdOkHoF7CfwuU1yy4uEEt9vPSP2wFp1dyaMvJW68vtB4kddLmI6gIgVVcT6ZX1Qm6WsusPrdisPLB2ScodXojCbL3DLj6PKG8QDVMWTrL1TpafT2wslRledWIhsTlv2mI3C066WMcTSwKLXdEDhVvFJ6ShiLKSN7gnRrlE0BnAw"),
						salt: Encoding.ASCII.GetBytes("saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6PlBdILBOkKUB6TGTPJXh1tpdOHTG6KuIvcbQp9qWjaf1uxAKgiTtYRIHhxjJI2viVa6fDZ67QOouOaf2RXQhpsWaTtAVnff6PIFcvJhdPDFGV5nvmZWoCZQodj6yXRDHPw9PyF0iLYm9uFtEunlAAxGB5qqea4X5tZvB1OfLVwymY3a3JPjdxTdvHxCHbqqE0zip61JNqdmeWxGtlRBC6CGoCiHO4XxHCntQBRJDcG0zW7joTdgtTBarsQQhlLXBGMNBSNmmTbDf3hFtawUBCJH18IAiRMwyeQJbJ2bERsY3MVRPuYCf4Au7gN72iGh1lRktSQtEFye7pO46kMXRrEjHQWXInMzzy7X2StXUzHVTFF2VdOoKn0WUqFNvB6PF7qIsOlYKj57bi1Psa34s85WxMSbTkhrd7VHdHZkTVaWdraohXYOePdeEvIwObCGEXkETUzqM5P2yzoBOJSdjpIYaa8zzdLD3yrb1TwCZuJVxsrq0XXY6vErU4QntsW0972XmGNyumFNJiPm4ONKh1RLvS1kddY3nm8276S4TUuZfrRQO8QxZRNuSaZI8JRZp5VojB5DktuMxAQkqoPjQ5Vtb6oXeOyY591CB1MEW1fLTCs0NrL321SaNRMqza1ETogAxpEiYwZ6pIgnMmSqNMRdZnCqA4gMWw1lIVATWK83OCeicNRUNOdfzS7A8vbLcmvKPtpOFvhNzwrrUdkvuKvaYJviQgeR7snGetO9JLCwIlHIj52gMCNU18d32SJl7Xomtl3wIe02SMvq1i1BcaX7lXioqWGmgVqBWU3fsUuGwHi6RUKCCQdEOBfNo2WdpFaCflcgnn0O6jVHCqkv8cQk81AqS00rAmHGCNTwyA6Tq5TXoLlDnC8gAQjDUsZp0z"),
						iterations: 100000).GetBytes(64 + 1);

				expected = "B8674F6C0CC9F8CF1F1874534FD5AF01FC1504D76C2BC2AA0A75FE4DD5DFD1DAF60EA7C85F122BCEEB8772659D601231607726998EAC3F6AAB72EFF7BA349F7FD7".FromBase16();
				Assert.IsTrue(Enumerable.SequenceEqual(expected, result));
			}
		}
		public void PBKDF2_SHA384()
		{
			byte[] result, expected;
			var hmacFactories = new Func<HMAC>[] { () => new HMACSHA384(), HMACFactories.HMACSHA384 };
			foreach (var hmacFactory in hmacFactories)
			{
				result = new PBKDF2(hmacFactory,
					password: Encoding.ASCII.GetBytes("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqK"),
					salt: Encoding.ASCII.GetBytes("saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcG"),
					iterations: 1).GetBytes(48);

				expected = "0644A3489B088AD85A0E42BE3E7F82500EC18936699151A2C90497151BAC7BB69300386A5E798795BE3CEF0A3C803227".FromBase16();
				Assert.IsTrue(Enumerable.SequenceEqual(expected, result));

				result = new PBKDF2(hmacFactory,
						password: Encoding.ASCII.GetBytes("passDATAb00AB7YxDTTlRH2dqxDx19GDxDV1zFMz7E6QVqKIzwOtMnlxQLttpE57Un4u12D2YD7oOPpiEvCDYvntXEe4NNPLCnGGeJArbYDEu6xDoCfWH6kbuV6awi04Uz3ebEAhzZ4ve1A2wg5CnLXdZC5Y7gwfVgbEgZSTmoYQSzC5OW4dfrjqiwApTACO6xoOL1AjWj6X6f6qFfF8TVmOzU9RhOd1N4QtzWI4fP6FYttNz5FuLdtYVXWVXH2Tf7I9fieMeWCHTMkM4VcmQyQHpbcP8MEb5f1g6Ckg5xk3HQr3wMBvQcOHpCPy1K8HCM7a5wkPDhgVA0BVmwNpsRIbDQZRtHK6dT6bGyalp6gbFZBuBHwD86gTzkrFY7HkOVrgc0gJcGJZe65Ce8v4Jn5OzkuVsiU8efm2Pw2RnbpWSAr7SkVdCwXK2XSJDQ5fZ4HBEz9VTFYrG23ELuLjvx5njOLNgDAJuf5JB2tn4nMjjcnl1e8qcYVwZqFzEv2zhLyDWMkV4tzl4asLnvyAxTBkxPRZj2pRABWwb3kEofpsHYxMTAn38YSpZreoXipZWBnu6HDURaruXaIPYFPYHl9Ls9wsuD7rzaGfbOyfVgLIGK5rODphwRA7lm88bGKY8b7tWOtepyEvaLxMI7GZF5ScwpZTYeEDNUKPzvM2Im9zehIaznpguNdNXNMLWnwPu4H6zEvajkw3G3ucSiXKmh6XNe3hkdSANm3vnxzRXm4fcuzAx68IElXE2bkGFElluDLo6EsUDWZ4JIWBVaDwYdJx8uCXbQdoifzCs5kuuClaDaDqIhb5hJ2WR8mxiueFsS0aDGdIYmye5svmNmzQxFmdOkHoF7CfwuU1yy4uEEt9vPSP2wFp1dyaMvJW68vtB4kddLmI6gIgVVcT6ZX1Qm6WsusPrdisPLB2ScodXojCbL3DLj6PKG8QDVMWTrL1TpafT2wslRledWIhsTlv2mI3C066WMcTSwKLXdEDhVvFJ6ShiLKSN7gnRrlE0BnAw"),
						salt: Encoding.ASCII.GetBytes("saltKEYbcTcXHCBxtjD2PnBh44AIQ6XUOCESOhXpEp3HrcGMwbjzQKMSaf63IJemkURWoqHusIeVB8Il91NjiCGQacPUu9qTFaShLbKG0Yj4RCMV56WPj7E14EMpbxy6PlBdILBOkKUB6TGTPJXh1tpdOHTG6KuIvcbQp9qWjaf1uxAKgiTtYRIHhxjJI2viVa6fDZ67QOouOaf2RXQhpsWaTtAVnff6PIFcvJhdPDFGV5nvmZWoCZQodj6yXRDHPw9PyF0iLYm9uFtEunlAAxGB5qqea4X5tZvB1OfLVwymY3a3JPjdxTdvHxCHbqqE0zip61JNqdmeWxGtlRBC6CGoCiHO4XxHCntQBRJDcG0zW7joTdgtTBarsQQhlLXBGMNBSNmmTbDf3hFtawUBCJH18IAiRMwyeQJbJ2bERsY3MVRPuYCf4Au7gN72iGh1lRktSQtEFye7pO46kMXRrEjHQWXInMzzy7X2StXUzHVTFF2VdOoKn0WUqFNvB6PF7qIsOlYKj57bi1Psa34s85WxMSbTkhrd7VHdHZkTVaWdraohXYOePdeEvIwObCGEXkETUzqM5P2yzoBOJSdjpIYaa8zzdLD3yrb1TwCZuJVxsrq0XXY6vErU4QntsW0972XmGNyumFNJiPm4ONKh1RLvS1kddY3nm8276S4TUuZfrRQO8QxZRNuSaZI8JRZp5VojB5DktuMxAQkqoPjQ5Vtb6oXeOyY591CB1MEW1fLTCs0NrL321SaNRMqza1ETogAxpEiYwZ6pIgnMmSqNMRdZnCqA4gMWw1lIVATWK83OCeicNRUNOdfzS7A8vbLcmvKPtpOFvhNzwrrUdkvuKvaYJviQgeR7snGetO9JLCwIlHIj52gMCNU18d32SJl7Xomtl3wIe02SMvq1i1BcaX7lXioqWGmgVqBWU3fsUuGwHi6RUKCCQdEOBfNo2WdpFaCflcgnn0O6jVHCqkv8cQk81AqS00rAmHGCNTwyA6Tq5TXoLlDnC8gAQjDUsZp0z"),
						iterations: 100000).GetBytes(48 + 1);

				expected = "7BADBDA9DBE9D5AB9237268D57ABB235B6B729AEFA9CACDF5E3007136F1178231FCFFE3E6437D9EF713EC32887C4B42674".FromBase16();
				Assert.IsTrue(Enumerable.SequenceEqual(expected, result));
			}
		}
예제 #12
0
        public string Hash(string password, string userSalt)
        {
            using (SHA512 alg = SHA512.Create())
            {
                // _hashArraySize is always a power of 2
                int bitmask = _memoryBlocks - 1;

                // we have a single running hash, but then we combine it with "randomly"-selected cells from the array
                var combinedHash = new byte[(MULTIPLIER + 1) * PasswordHasher.DigestSize];  /* add one because we'll store the running hash there */

            #if DEBUG
                _hashes = new List<string>();
                _visitCounts = new int[_memoryBlocks];
                _hashesPerCell = new List<string>[_memoryBlocks];
                _hashToCells = new Dictionary<string, int[]>();

                for (int i = 0; i < _hashesPerCell.Length; i++ )
                    _hashesPerCell[i] = new List<string>();
            #endif

                byte[] hash = new byte[PasswordHasher.DigestSize];  // the running hash

                // PBKDF2-HMAC-SHA512 the user's password with (system salt + user salt) and use it to fill _hashArray
                var pbkdf2 = new PBKDF2<HMACSHA512>(Encoding.ASCII.GetBytes(password), Encoding.ASCII.GetBytes(_systemSalt + userSalt ?? ""), 1);
                var bytes = pbkdf2.GetBytes(_fillAmount + PasswordHasher.DigestSize);

                /* initialize the running hash to what comes out of PBKDF2 after the _hashArray is filled */
                Buffer.BlockCopy(bytes, _fillAmount, hash, 0, PasswordHasher.DigestSize);

                int blockStart = 0;
                int[] blockStarts = new int[MULTIPLIER];

                // We only PBKDF2 enough for 1/16th of the _hashArray, so duplicate that "_fillAmount" 16 times
                for (var i = 0; i < MULTIPLIER; i++)
                {
                    Buffer.BlockCopy(bytes, 0, _hashArray, blockStart, _fillAmount);
                    blockStart += _fillAmount;
                }

            #if DEBUG
                _originalArray = new byte[_hashArray.Length];
                Buffer.BlockCopy(_hashArray, 0, _originalArray, 0, _hashArray.Length);
            #endif

                // now "randomly mix up" the hash array
                for (int i = 0; i < _mixingIterations; i++)
                {
            #if DEBUG
                    AddHash(hash);
            #endif
                    int combinedHashEnd = PasswordHasher.DigestSize;

                    // combine the running hash with...
                    Buffer.BlockCopy(hash, 0, combinedHash, 0, PasswordHasher.DigestSize);

                    // ..."randomly"-selected cells in the hash array
                    for (int m = 0; m < MULTIPLIER; m++ )
                    {
                        // create a random int from bytes in the running hash and interpret the int as which cell to get a hash from.
                        // Since hashes are 64 bytes long and ints are 4 bytes, we can only get 16 random indexes from the hash,
                        // which is why MULTIPLIER is 16.
                        int nextIndex = (hash[m * 4] + (hash[m * 4 + 1] << 8) + (hash[m * 4 + 2] << 16) +
                            (hash[m * 4 + 3] << 24)) & bitmask;

                        // add that selected hash to the combined hash
                        blockStarts[m] = blockStart = nextIndex * PasswordHasher.DigestSize;
                        Buffer.BlockCopy(_hashArray, blockStart, combinedHash, combinedHashEnd, PasswordHasher.DigestSize);
                        combinedHashEnd += PasswordHasher.DigestSize;
            #if DEBUG
                        _visitCounts[nextIndex]++;
            #endif
                    }

                    // update the running hash
                    hash = alg.ComputeHash(combinedHash);

            #if DEBUG
                    var base64hash = Convert.ToBase64String(hash);
                    _hashToCells[base64hash] = new int[MULTIPLIER];
            #endif

                    for (int m = 0; m < MULTIPLIER; m++)
                    {
                        blockStart = blockStarts[m];
                        // xor the selected hash with the running hash so that the hash array is constantly being modified
                        for (int b = 0; b < PasswordHasher.DigestSize; b++)
                            _hashArray[blockStart + b] ^= hash[b];
            #if DEBUG
                        int hashIndex = blockStart / PasswordHasher.DigestSize;
                        _hashesPerCell[hashIndex].Add(base64hash);
                        _hashToCells[base64hash][m] = hashIndex;
            #endif
                    }
                }

                return Convert.ToBase64String(alg.ComputeHash(_hashArray, 0, MemoryUsage));
            }
        }