/// <summary> /// Closes the package, optionally leaving the output stream open. /// </summary> /// <param name="leaveOutputOpen"><c>true</c> to leave the output stream open.</param> public void Close(bool leaveOutputOpen) { if (packageOut != null) { // Append the terminating entry. new PackageEntry(this).Serialize(packageOut); // Before we close the package, we need to compute the MD5 // hash of everything after the header in the output stream // and update the hash in the header. var header = new PackageHeader(); packageOut.Position = cbHeader; header.Hash = MD5Hasher.Compute(packageOut, packageOut.Length - cbHeader); packageOut.Position = 0; header.Seralize(packageOut); if (!leaveOutputOpen) { packageOut.Close(); } packageOut = null; } if (packageIn != null) { packageIn.Close(); packageIn = null; } }
private void CompareFiles(string path1, string path2) { EnhancedFileStream es1 = null; EnhancedFileStream es2 = null; try { es1 = new EnhancedFileStream(path1, FileMode.Open, FileAccess.Read); es2 = new EnhancedFileStream(path2, FileMode.Open, FileAccess.Read); Assert.AreEqual(es1.Length, es2.Length); CollectionAssert.AreEqual(MD5Hasher.Compute(es1, es1.Length), MD5Hasher.Compute(es2, es2.Length)); } finally { if (es1 != null) { es1.Close(); } if (es2 != null) { es2.Close(); } } }
/// <summary> /// Computes an MD5 hash code for the log entry /// </summary> /// <param name="extension">The extended entry information (or <c>null</c>).</param> /// <param name="entryType">The log entry type.</param> /// <param name="message">The log message (or <c>null</c>).</param> /// <param name="e">The logged exception (or <c>null</c>).</param> /// <returns>The computed hash.</returns> private static MD5Key ComputeMD5Hash(ISysLogEntryExtension extension, SysLogEntryType entryType, string message, Exception e) { StringBuilder sb = new StringBuilder(1024); if (extension != null) { sb.Append(extension.Format()); } sb.Append('\t'); sb.Append(entryType.ToString()); sb.Append('\t'); if (message != null) { sb.Append(message); } sb.Append('\t'); if (e != null) { sb.Append(e.GetType().FullName); sb.Append('\t'); sb.Append(e.Message); sb.Append('\t'); sb.Append(e.StackTrace.ToString()); } return(new MD5Key(MD5Hasher.Compute(sb.ToString()))); }
private static int Hash(string fileName) { try { byte[] hash; using (EnhancedStream es = new EnhancedFileStream(fileName, FileMode.Open, FileAccess.Read)) { hash = MD5Hasher.Compute(es, es.Length); } using (StreamWriter writer = new StreamWriter(fileName + ".md5", false, Helper.AnsiEncoding)) { writer.WriteLine("MD5={0}", Helper.ToHex(hash)); } File.Copy(fileName, fileName + ".setup"); return(0); } catch (Exception e) { Program.Error("Error ({0}): {1}", e.GetType().Name, e.Message); return(1); } }
public void WebTransferFile_UploadMD5() { // Test the file upload with MD5 validation scenario and // also test appending with position. WebTransferFile transferFile; Guid id; byte[] block1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; byte[] block2 = new byte[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; byte[] block; long pos; ClearFolder(); // Create a file id = Guid.NewGuid(); transferFile = new WebTransferFile(id, folder, new Uri("http://test.com"), ".pdf", true, true); Assert.AreEqual(id, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com"), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the first block pos = 0; transferFile.Append(pos, block1); // Append it again to simulate a duplicate transferFile.Append(pos, block1); pos += block1.Length; // Open an existing file transferFile = new WebTransferFile(id, folder, new Uri("http://test.com"), ".pdf", false, true); Assert.AreEqual(id, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com"), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the second block transferFile.Append(pos, block2); // Complete the upload and verify the file contents. transferFile.Commit(MD5Hasher.Compute(Helper.Concat(block1, block2))); Assert.AreEqual(id, transferFile.ID); Assert.IsFalse(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path)); Assert.AreEqual(new Uri(new Uri("http://test.com"), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); using (var stream = transferFile.GetStream()) { block = stream.ReadBytes((int)stream.Length); CollectionAssert.AreEqual(Helper.Concat(block1, block2), block); } }
/// <summary> /// Updates a message file's metadata. /// </summary> /// <param name="path">The file path.</param> /// <param name="msgInfo">The message metadata.</param> internal void WriteMessageMetadata(string path, QueuedMsgInfo msgInfo) { using (var fsMsg = new EnhancedFileStream(path, FileMode.Open, FileAccess.ReadWrite)) { // Seek past the message's header and body. int cbBody; byte[] md5Hash; fsMsg.Position = MsgHeaderSize; cbBody = fsMsg.ReadInt32(); fsMsg.Position = fsMsg.Position + cbBody; // Write the metadata and truncate the file. fsMsg.WriteString16(msgInfo.ToString()); fsMsg.SetLength(fsMsg.Position); // Regenerate the MD5 Hash fsMsg.Position = MsgMD5Offset + MD5Hasher.DigestSize; md5Hash = MD5Hasher.Compute(fsMsg, fsMsg.Length - fsMsg.Position); fsMsg.Position = MsgMD5Offset; fsMsg.WriteBytesNoLen(md5Hash); } }
/// <summary> /// Writes a message file. /// </summary> /// <param name="path">The file path.</param> /// <param name="msg">The message.</param> /// <param name="msgInfo">The message metadata.</param> internal void WriteMessage(string path, QueuedMsg msg, QueuedMsgInfo msgInfo) { byte[] md5Hash; using (var fsMsg = new EnhancedFileStream(path, FileMode.Create, FileAccess.ReadWrite)) { // Write the file header fsMsg.WriteInt32(MsgMagic); // Magic Number fsMsg.WriteInt32(0); // Format Version fsMsg.WriteInt32(0); // Reserved fsMsg.WriteBytesNoLen(md5Zeros); // MD5 hash placeholder // Write the message body. fsMsg.WriteBytes32(msg.BodyRaw); // Write the metadata. fsMsg.WriteString16(msgInfo.ToString()); // Compute and save the MD5 hash fsMsg.Position = MsgHeaderSize; md5Hash = MD5Hasher.Compute(fsMsg, fsMsg.Length - fsMsg.Position); fsMsg.Position = MsgMD5Offset; fsMsg.WriteBytesNoLen(md5Hash); } }
public void Hashers_MD5() { EnhancedMemoryStream ms; byte[] data; byte[] digest1, digest2; digest1 = MD5Hasher.Compute(new byte[] { 0, 1, 2, 3 }, 0, 4); Assert.AreEqual(16, digest1.Length); Assert.AreEqual(16, MD5Hasher.DigestSize); digest2 = MD5Hasher.Compute(new byte[] { 1, 1, 2, 3 }, 0, 4); Assert.AreNotEqual(digest1, digest2); digest1 = MD5Hasher.Compute(new byte[0]); Assert.AreEqual(16, digest1.Length); Assert.AreEqual(16, MD5Hasher.DigestSize); digest1 = MD5Hasher.Compute(new byte[] { 0, 1, 2, 3 }); ms = new EnhancedMemoryStream(); ms.Seek(0, SeekOrigin.Begin); ms.Write(new byte[] { 0, 1, 2, 3 }, 0, 4); ms.Seek(0, SeekOrigin.Begin); digest2 = MD5Hasher.Compute(ms, 4); CollectionAssert.AreEqual(digest1, digest2); Assert.AreEqual(16, digest2.Length); Assert.AreEqual(0, ms.Position); data = new byte[2048]; for (int i = 0; i < data.Length; i++) { data[i] = (byte)i; } digest1 = MD5Hasher.Compute(data); ms.Seek(0, SeekOrigin.Begin); ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); digest2 = MD5Hasher.Compute(ms, data.Length); CollectionAssert.AreEqual(digest1, digest2); Assert.AreEqual(16, digest2.Length); Assert.AreEqual(0, ms.Position); digest1 = MD5Hasher.Compute("hello"); digest2 = MD5Hasher.Compute("world"); CollectionAssert.AreNotEqual(digest1, digest2); CollectionAssert.AreEqual(digest1, MD5Hasher.Compute("hello")); // These really aren't very good tests for folding but // at least they'll verify that it doesn't crash Assert.AreEqual(MD5Hasher.FoldOnce(new byte[] { 0, 1, 2, 3 }), MD5Hasher.FoldOnce(new byte[] { 0, 1, 2, 3 })); Assert.AreNotEqual((object)MD5Hasher.FoldOnce(new byte[] { 1, 1, 2, 3 }), (object)MD5Hasher.FoldOnce(new byte[] { 0, 1, 2, 3 })); Assert.AreEqual(MD5Hasher.FoldTwice(new byte[] { 0, 1, 2, 3 }), MD5Hasher.FoldTwice(new byte[] { 0, 1, 2, 3 })); Assert.AreNotEqual(MD5Hasher.FoldTwice(new byte[] { 1, 1, 2, 3 }), MD5Hasher.FoldTwice(new byte[] { 0, 1, 2, 3 })); }
/// <summary> /// Reads the metadata from a message file. /// </summary> /// <param name="msgID">The message ID.</param> /// <param name="path">Fully qualified path to the message file.</param> /// <returns>The <see cref="QueuedMsgInfo" />.</returns> internal QueuedMsgInfo ReadMessageMetadata(Guid msgID, string path) { QueuedMsgInfo msgInfo; int cbBody; byte[] md5Hash; long savePos; using (var fsMsg = new EnhancedFileStream(path, FileMode.Open, FileAccess.ReadWrite)) { try { // Read the message file header if (fsMsg.ReadInt32() != MsgMagic) // Magic Number { throw new Exception(); } if (fsMsg.ReadInt32() != 0) // Format Version { throw new Exception(); } fsMsg.ReadInt32(); // Reserved // Verify that the MD5 hash saved in then file matches the // hash computed for the remainder of the file as it exists // right now. md5Hash = fsMsg.ReadBytes(MD5Hasher.DigestSize); savePos = fsMsg.Position; if (!Helper.ArrayEquals(md5Hash, MD5Hasher.Compute(fsMsg, fsMsg.Length - fsMsg.Position))) { throw new FormatException(string.Format("Message file [{0}] is corrupt. MD5 digests do not match.", path)); } fsMsg.Position = savePos; // Skip over the message body data cbBody = fsMsg.ReadInt32(); fsMsg.Position = fsMsg.Position + cbBody; // Read the metadata and add the provider specific information msgInfo = new QueuedMsgInfo(fsMsg.ReadString16()); msgInfo.PersistID = msgInfo.ID; msgInfo.ProviderData = path; return(msgInfo); } catch { throw new FormatException(string.Format("Bad message file [{0}].", path)); } } }
private void GetFileInfo(string fileName, out int size, out byte[] md5) { using (var es = new EnhancedFileStream(tempFolder + "\\" + fileName, FileMode.Open, FileAccess.Read)) { size = (int)es.Length; md5 = MD5Hasher.Compute(es, size); } }
public void AppPackage_MD5() { string tempPath = GetTempFileName(); string tempDir = Helper.AddTrailingSlash(Path.GetTempPath()); AppPackage package = null; MemoryStream ms; byte[] md5; try { package = AppPackage.Create(tempPath, AppRef.Parse("appref://myapps/mypackage.zip?version=1.2.3.4"), @" LaunchType = Test.MyType:MyAssembly.dll; LaunchMethod = Foo; LaunchArgs = Bar; "); using (ms = new MemoryStream(4096)) { for (int i = 0; i < 4096; i++) { ms.WriteByte((byte)i); } ms.Position = 0; package.AddFile("Test.dat", ms); } package.Close(); package = null; // Verify that a MD5 hash computed manually on the package file jibes // with what AppPackage computes. using (var fs = new EnhancedFileStream(tempPath, FileMode.Open, FileAccess.Read)) { md5 = MD5Hasher.Compute(fs, fs.Length); } package = AppPackage.Open(tempPath); CollectionAssert.AreEqual(md5, package.MD5); package.Close(); package = null; } finally { if (package != null) { package.Close(); } Delete(tempPath); } }
/// <summary> /// Computes, sets, and returns the response authenticator for the packet. /// </summary> /// <param name="request">The corresponding request packet.</param> /// <param name="secret">The shared secret for the NAS.</param> /// <returns>The computed response authenticator.</returns> /// <remarks> /// This is computed via a MD5 hash over the serialized request /// packet plus the serialized response attributes, plus the /// shared secret. /// </remarks> public byte[] ComputeResponseAuthenticator(RadiusPacket request, string secret) { byte[] serialized = this.ToArray(); byte[] bytes; bytes = Helper.Concat(Helper.Extract(serialized, 0, 4), request.Authenticator); bytes = Helper.Concat(bytes, Helper.Extract(serialized, HeaderSize)); bytes = Helper.Concat(bytes, Helper.ToAnsi(secret)); return(this.Authenticator = MD5Hasher.Compute(bytes)); }
/// <summary> /// Constructs an <b>Authorization</b> header value by computing the /// digest as defined by <a href="http://www.ietf.org/rfc/rfc2069.txt?number=2069">RFC 2069</a> /// and modified by <a href="http://www.ietf.org/rfc/rfc3261.txt?number=3261">RFC 3261</a> /// from the user credentials and <b>WWW-Authenticate</b> header values /// passed. /// </summary> /// <param name="authenticate">The <see cref="SipAuthenticateValue" /> specifying the authentication challenge.</param> /// <param name="userName">The user name.</param> /// <param name="password">The user's password.</param> /// <param name="method">The SIP method exactly as it </param> /// <param name="digestUri">The URI of the SIP server entity where the authorization value will be presented.</param> public SipAuthorizationValue(SipAuthenticateValue authenticate, string userName, string password, string method, string digestUri) { if (String.Compare(authenticate.Algorithm, "MD5") != 0) { throw new SipException("Unsupported digest algorithm [{0}].", authenticate.Algorithm, true); } base["algorithm"] = "MD5"; // Algorithm defaults to MD5 base["username"] = userName; base["realm"] = authenticate.Realm; base["uri"] = digestUri; base["nonce"] = authenticate.Nonce; // Compute the authorization digest. string key; byte[] hash; string ha1; string ha2; string response; // Compute: HA1 = H(username + ":" + realm + ":" + password) key = userName + ":" + authenticate.Realm + ":" + password; hash = MD5Hasher.Compute(Helper.ToUTF8(key)); // $todo(jeff.lill): Not complety sure of the encoding ha1 = Helper.ToHex(hash); // Compute: HA2 = H(method + ":" + digesturi) key = method.ToUpper() + ":" + digestUri; hash = MD5Hasher.Compute(Helper.ToUTF8(key)); // $todo(jeff.lill): Not complety sure of the encoding ha2 = Helper.ToHex(hash); // Compute: response = H(HA1 + ":" + nonce + ":" + HA2) key = ha1 + ":" + authenticate.Nonce + ":" + ha2; hash = MD5Hasher.Compute(Helper.ToUTF8(key)); // $todo(jeff.lill): Not complety sure of the encoding response = Helper.ToHex(hash); base["response"] = response; }
/// <summary> /// Completes the uploading of the file by verifying that its contents /// match the MD5 signature passed and then stripping the temporary /// upload extension from its file name. /// </summary> /// <param name="md5Signature">A 16-byte array with the file's MD-5 signature.</param> /// <exception cref="IOException">Thrown if the file does not exist or the signature doesn't match.</exception> /// <exception cref="ArgumentException">Thrown if is is not a upload file.</exception> /// <remarks> /// <note> /// This method <b>does not</b> throw an exception if this method has already /// been called to complete the upload and the method will delete the file if /// the signature doesn't match. /// </note> /// </remarks> public void Commit(byte[] md5Signature) { string uploadPath = this.Path + WebTransferCache.UploadExtension; byte[] actualSignature; if (!IsUploading) { throw new ArgumentException("File is not being uploaded."); } if (md5Signature.Length != MD5Hasher.DigestSize) { throw new ArgumentException("Invalid MD5 signature.", "md5Signature"); } if (File.Exists(uploadPath)) { using (var stream = GetStream()) { actualSignature = MD5Hasher.Compute(stream, stream.Length); } if (!Helper.ArrayEquals(md5Signature, actualSignature)) { Helper.DeleteFile(uploadPath); throw new IOException("Upload failure: MD5 signature mismatch."); } IsUploading = false; File.Move(uploadPath, this.Path); } else if (!File.Exists(this.Path)) { throw new IOException(string.Format("Upload file [{0}] does not exist.", this.Path)); } }
public void Hashers_HMAC_MD5() { byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; byte[] key; byte[] digest; key = new byte[] { 0, 1, 2, 3, 4, 5 }; digest = MD5Hasher.Compute(key, data); Assert.AreEqual(MD5Hasher.DigestSize, digest.Length); key = new byte[128]; for (int i = 0; i < key.Length; i++) { key[i] = (byte)i; } digest = MD5Hasher.Compute(key, data); Assert.AreEqual(MD5Hasher.DigestSize, digest.Length); // The data for this test came from RFC2104 key = new byte[16]; for (int i = 0; i < key.Length; i++) { key[i] = 0xAA; } data = new byte[50]; for (int i = 0; i < data.Length; i++) { data[i] = 0xDD; } digest = MD5Hasher.Compute(key, data); CollectionAssert.AreEqual(Helper.FromHex("56be34521d144c88dbb8c733f0e8b3f6"), digest); }
/// <summary> /// Handles the installation of an application's configuration file. /// </summary> /// <param name="iniFile">The application INI file name (e.g. "MyApp.ini").</param> /// <remarks> /// <para> /// This method provides the mechanism by which setup won't overwrite /// user changes to an configuration file when installing an updated /// version of the application. The method compares the MD5 hash of the /// base version of the INI file to the hash of the currently installed /// INI file. If the hashes match, the installed version will be overwritten /// by the base version. If the hashes differ, then the user will be /// notified and given the opportunity to choose whether to overwrite /// the installed INI. In either case, any installed INI file will be /// backed up. /// </para> /// <para> /// This method assumes that the base version of the INI file is named /// <b><file>.ini.base</b> and the file with the base MD5 hash is /// named <b><file>.ini.md5</b>. The method generates a new MD5 /// file as necessary. /// </para> /// </remarks> public void InstallIniFile(string iniFile) { string baseFile = iniFile + ".base"; byte[] baseMD5; byte[] curMD5; if (!File.Exists(installIniFolder + baseFile)) { throw new IOException(string.Format("[{0}] file needs to be added to the setup project.", baseFile)); } if (!File.Exists(installIniFolder + iniFile)) { // No INI file exists, so simply copy the base file over and generate // the MD5 hash. File.Copy(installIniFolder + baseFile, installIniFolder + iniFile); using (EnhancedStream es = new EnhancedFileStream(installIniFolder + iniFile, FileMode.Open, FileAccess.Read)) { curMD5 = MD5Hasher.Compute(es, es.Length); } using (StreamWriter writer = new StreamWriter(installIniFolder + iniFile + ".md5", false, Helper.AnsiEncoding)) { writer.WriteLine("MD5={0}", Helper.ToHex(curMD5)); } return; } // Compute the MD5 hash for the existing file and compare it to the // hash for the original base file (if there is one). using (EnhancedFileStream es = new EnhancedFileStream(installIniFolder + iniFile, FileMode.Open, FileAccess.Read)) { curMD5 = MD5Hasher.Compute(es, es.Length); } try { using (var reader = new StreamReader(installIniFolder + iniFile + ".md5", Helper.AnsiEncoding)) { string line; line = reader.ReadLine(); if (line == null) { throw new Exception(); } if (!line.StartsWith("MD5=")) { throw new Exception(); } baseMD5 = Helper.FromHex(line.Substring(4)); } } catch { baseMD5 = new byte[0]; // The hashes will always differ if the MD5 file doesn't exist or isn't valid. } // Make a backup copy of the current INI file. File.Copy(installIniFolder + iniFile, installIniFolder + Helper.ToIsoDate(DateTime.UtcNow).Replace(':', '-') + "-" + iniFile); if (Helper.ArrayEquals(baseMD5, curMD5)) { // If the base and current MD5 hashes are the same, we can be assured that the // user has not customized the INI file. In this case, we'll simply overwrite // the existing INI file with the new version. File.Delete(installIniFolder + iniFile); File.Copy(installIniFolder + baseFile, installIniFolder + iniFile); } else { // It looks like the user has modified the INI file. So prompt the user // to see if he wants to overwrite or not. if (MessageBox.Show(string.Format("The application configuration file [{0}] has been customized.\r\n\r\nDo you want to overwrite this with the new version?", iniFile), setupTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes) { File.Delete(installIniFolder + iniFile); File.Copy(installIniFolder + baseFile, installIniFolder + iniFile); } } // Generate the MD5 hash file from the base INI file. using (var es = new EnhancedFileStream(installIniFolder + baseFile, FileMode.Open, FileAccess.Read)) { baseMD5 = MD5Hasher.Compute(es, es.Length); } using (var writer = new StreamWriter(installIniFolder + iniFile + ".md5", false, Helper.AnsiEncoding)) { writer.WriteLine("MD5={0}", Helper.ToHex(baseMD5)); } // Perform any macro replacements. if (iniMacroReplacements.Count > 0) { using (var fs = new EnhancedFileStream(installIniFolder + iniFile, FileMode.Open, FileAccess.ReadWrite)) { foreach (var replacement in iniMacroReplacements) { fs.Position = 0; Config.EditMacro(fs, Helper.AnsiEncoding, replacement.Key, replacement.Value); } } } }
public void Initialize() { Helper.InitializeApp(Assembly.GetExecutingAssembly()); this.ADSettings = new ADTestSettings(); this.DB = SqlTestDatabase.Create(); this.AuthFilePath = Path.GetTempFileName(); //------------------------------------------------------------- // Initialize file authentication Helper.WriteToFile(this.AuthFilePath, @" file.com;file1;file-password1 file.com;file2;file-password2 "); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.File, "file.com", "file1", "file-password1")); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.File, "file.com", "file2", "file-password2")); //------------------------------------------------------------- // Initialize RADIUS authentication RadiusServerSettings radiusSettings = new RadiusServerSettings(); radiusSettings.NetworkBinding = NetworkBinding.Parse("ANY:52111"); radiusSettings.Devices.Add(new RadiusNasInfo(IPAddress.Loopback, this.RadiusSecret)); radiusSettings.Devices.Add(new RadiusNasInfo(NetHelper.GetActiveAdapter(), this.RadiusSecret)); this.RadiusServer = new RadiusServer(); this.RadiusServer.Start(radiusSettings); this.RadiusServer.LoadAccountsFromString(@" radius.com;radius1;radius-password1 radius.com;radius2;radius-password2 "); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Radius, "radius.com", "radius1", "radius-password1")); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Radius, "radius.com", "radius2", "radius-password2")); //------------------------------------------------------------- // Initialize config authentication Config.SetConfig(@" Accounts[0] = config.com;config1;config-password1 Accounts[1] = config.com;config2;config-password2 "); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Config, "config.com", "config1", "config-password1")); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Config, "config.com", "config2", "config-password2")); #if TEST_AD //------------------------------------------------------------- // Initialize active directory authentication #if !TEST_AD_LDAP if (ADSettings.NasSecret != string.Empty) // Disable the test if the NAS secret is blank #endif this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Ldap, ADSettings.Domain, ADSettings.Account, ADSettings.Password)); #endif //------------------------------------------------------------- // Initalize ODBC authentication SqlConnection sqlCon = null; SqlScriptRunner scriptRunner; MacroProcessor processor; string initScript = @" create table Accounts ( Realm varchar(64), Account varchar(64), Password varchar(64), MD5 varbinary(128), SHA1 varbinary(128), SHA256 varbinary(128), SHA512 varbinary(128) ) go insert into Accounts(Realm,Account,Password,MD5,SHA1,SHA256,SHA512) values ('odbc.com','odbc1','odbc-password1',$(md5-1),$(sha1-1),$(sha256-1),$(sha512-1)) insert into Accounts(Realm,Account,Password,MD5,SHA1,SHA256,SHA512) values ('odbc.com','odbc2','odbc-password2',$(md5-2),$(sha1-2),$(sha256-2),$(sha512-2)) go "; try { processor = new MacroProcessor(); processor.Add("md5-1", SqlHelper.Literal(MD5Hasher.Compute("odbc-password1"))); processor.Add("sha1-1", SqlHelper.Literal(SHA1Hasher.Compute("odbc-password1"))); processor.Add("sha256-1", SqlHelper.Literal(SHA256Hasher.Compute("odbc-password1"))); processor.Add("sha512-1", SqlHelper.Literal(SHA512Hasher.Compute("odbc-password1"))); processor.Add("md5-2", SqlHelper.Literal(MD5Hasher.Compute("odbc-password2"))); processor.Add("sha1-2", SqlHelper.Literal(SHA1Hasher.Compute("odbc-password2"))); processor.Add("sha256-2", SqlHelper.Literal(SHA256Hasher.Compute("odbc-password2"))); processor.Add("sha512-2", SqlHelper.Literal(SHA512Hasher.Compute("odbc-password2"))); initScript = processor.Expand(initScript); sqlCon = DB.OpenConnection(); scriptRunner = new SqlScriptRunner(initScript); scriptRunner.Run(sqlCon); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Odbc, "odbc.com", "odbc1", "odbc-password1")); this.Accounts.Add(new AuthTestAccount(AuthTestExtensionType.Odbc, "odbc.com", "odbc2", "odbc-password2")); } finally { if (sqlCon != null) { sqlCon.Close(); } } }
public void WebTransferCache_UploadBadMD5() { // Verify that we can detect a corrupted upload. WebTransferCache cache; WebTransferFile transferFile; Guid id; byte[] block1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; byte[] block2 = new byte[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; long pos; byte[] md5; ClearFolder(); cache = new WebTransferCache(new Uri("http://test.com"), folder, "", TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); cache.Start(); try { // Create a file transferFile = cache.GetUploadFile(Guid.Empty, ".pdf"); id = transferFile.ID; Assert.AreNotEqual(Guid.Empty, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the first block pos = 0; transferFile.Append(pos, block1); // Append it again to simulate a duplicate transferFile.Append(pos, block1); pos += block1.Length; // Open an existing file transferFile = cache.GetUploadFile(id, ".pdf"); Assert.AreEqual(id, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the second block transferFile.Append(pos, block2); // Complete the upload and verify the file contents. md5 = MD5Hasher.Compute(Helper.Concat(block1, block2)); md5[0] = (byte)~md5[0]; // Mess with the hash try { transferFile.Commit(md5); Assert.Fail("Expected an IOException detecting the MD5 signature mismatch."); } catch (IOException) { // Expected } } finally { cache.Stop(); } }
/// <summary> /// Encrypts a user password string by combining it with the shared NAS /// secret and the message authenticator as described in RFC 2865 page 27. /// </summary> /// <param name="userPassword">The user password to be encrypted.</param> /// <param name="secret">The shared NAS secret.</param> /// <returns>The encrypted password.</returns> /// <exception cref="RadiusException">Thrown if the password is too large or too small.</exception> public byte[] EncryptUserPassword(string userPassword, string secret) { var bs = new EnhancedBlockStream(128, 128); byte[] secretBytes = Helper.ToAnsi(secret); byte[] cypherBlock = new byte[16]; byte[] rawPwd; byte[] xorHash; byte[] pwd; int pos; try { rawPwd = Helper.ToAnsi(userPassword); if (rawPwd.Length == 0) { throw new RadiusException("Zero length password is not allowed."); } // Copy userPassword into pwd, padding the result out with zeros // to a multiple of 16 bytes if (rawPwd.Length % 16 == 0) { pwd = rawPwd; } else { pwd = new byte[(rawPwd.Length / 16 + 1) * 16]; Array.Copy(rawPwd, pwd, rawPwd.Length); } // The first XOR hash is MD5(secret + authenticator) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, this.Authenticator)); // Perform the encryption pos = 0; while (true) { // Cyperblock = XOR hash ^ next 16 bytes of password for (int i = 0; i < 16; i++) { cypherBlock[i] = (byte)(xorHash[i] ^ pwd[pos + i]); } bs.WriteBytesNoLen(cypherBlock); pos += 16; if (pos >= pwd.Length) { break; } // Next XOR hash is MD5(secret + cypherblock) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, cypherBlock)); } if (bs.Length > 128) { throw new RadiusException("Encrypted password exceeds 128 bytes."); } return(bs.ToArray()); } finally { bs.Close(); } }
/// <summary> /// Decrypts a user password by encrypted using <see cref="EncryptUserPassword" /> /// using the message authenticator and the shared NAS secret. /// </summary> /// <param name="encryptedPassword">The encrypted password.</param> /// <param name="secret">The shared NAS secret.</param> /// <returns>The decrypted password.</returns> /// <exception cref="RadiusException">Thrown if the encrypted password is invalid.</exception> public string DecryptUserPassword(byte[] encryptedPassword, string secret) { var bs = new EnhancedBlockStream(128, 128); byte[] secretBytes = Helper.ToAnsi(secret); byte[] clearBlock = new byte[16]; byte[] xorHash; byte[] decrypted; int pos; int zeroPos; try { // The encrypted password length must be a non-zero multiple of 16 bytes. if (encryptedPassword.Length == 0 || encryptedPassword.Length % 16 != 0) { throw new RadiusException("Encrypted user password length must be a positive multiple of 16 bytes."); } // The first XOR hash is MD5(secret + authenticator) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, this.Authenticator)); // Perform the decryption. The trick here is to unmunge the 16 byte // blocks by XORing them with the current XOR hash. pos = 0; while (true) { // clearBlock = XOR hash ^ next 16 encrypted bytes for (int i = 0; i < 16; i++) { clearBlock[i] = (byte)(xorHash[i] ^ encryptedPassword[pos + i]); } bs.WriteBytesNoLen(clearBlock); pos += 16; if (pos >= encryptedPassword.Length) { break; } // Next XOR hash = MD5(secret + last cypherblock) xorHash = MD5Hasher.Compute(Helper.Concat(secretBytes, Helper.Extract(encryptedPassword, pos - 16, 16))); } // Scan forward to the first zero byte. If we find one, we're going // to assume that it was a padding byte. decrypted = bs.ToArray(); zeroPos = -1; for (int i = 0; i < decrypted.Length; i++) { if (decrypted[i] == 0) { zeroPos = i; break; } } if (zeroPos == -1) { return(Helper.FromAnsi(decrypted)); } else { return(Helper.FromAnsi(decrypted, 0, zeroPos)); } } finally { bs.Close(); } }
public void WebTransferCache_Upload() { // Test the file upload scenario. WebTransferCache cache; WebTransferFile transferFile; Guid id; byte[] block1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; byte[] block2 = new byte[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; byte[] block; long pos; ClearFolder(); cache = new WebTransferCache(new Uri("http://test.com"), folder, "", TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); cache.Start(); try { // Create a file transferFile = cache.GetUploadFile(Guid.Empty, ".pdf"); id = transferFile.ID; Assert.AreNotEqual(Guid.Empty, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the first block pos = 0; transferFile.Append(pos, block1); // Append it again to simulate a duplicate transferFile.Append(pos, block1); pos += block1.Length; // Open an existing file transferFile = cache.GetUploadFile(id, ".pdf"); Assert.AreEqual(id, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the second block transferFile.Append(pos, block2); // Complete the upload and verify the file contents. transferFile.Commit(MD5Hasher.Compute(Helper.Concat(block1, block2))); Assert.AreEqual(id, transferFile.ID); Assert.IsFalse(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); using (var stream = transferFile.GetStream()) { block = stream.ReadBytes((int)stream.Length); CollectionAssert.AreEqual(Helper.Concat(block1, block2), block); } } finally { cache.Stop(); } }
public void WebTransferCache_UploadEncrypt() { // Test file upload with encryption. WebTransferCache cache; WebTransferFile transferFile; Guid id; byte[] block1 = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; byte[] block2 = new byte[] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; byte[] block; long pos; ClearFolder(); cache = new WebTransferCache(new Uri("http://test.com"), folder, "", TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); cache.Start(); try { // Create a file transferFile = cache.GetUploadFile(Guid.Empty, ".pdf"); id = transferFile.ID; Assert.AreNotEqual(Guid.Empty, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the first block pos = 0; transferFile.Append(pos, block1); // Append it again to simulate a duplicate transferFile.Append(pos, block1); pos += block1.Length; // Open an existing file transferFile = cache.GetUploadFile(id, ".pdf"); Assert.AreEqual(id, transferFile.ID); Assert.IsTrue(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path + WebTransferCache.UploadExtension)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); // Append the second block transferFile.Append(pos, block2); // Complete the upload and verify the file contents. transferFile.Commit(MD5Hasher.Compute(Helper.Concat(block1, block2))); Assert.AreEqual(id, transferFile.ID); Assert.IsFalse(transferFile.IsUploading); Assert.IsTrue(File.Exists(transferFile.Path)); Assert.AreEqual(new Uri(new Uri("http://test.com/" + cache.HashIDToSubFolder(transferFile.ID)), transferFile.ID.ToString("D") + ".pdf"), transferFile.Uri); using (var stream = transferFile.GetStream()) { block = stream.ReadBytes((int)stream.Length); CollectionAssert.AreEqual(Helper.Concat(block1, block2), block); } // Now encrypt the file and verify. var key = Crypto.GenerateSymmetricKey(CryptoAlgorithm.AES, 256); cache.EncryptFile(key, id, ".pdf"); using (var stream = transferFile.GetStream()) { block = stream.ReadBytes((int)stream.Length); CollectionAssert.AreNotEqual(Helper.Concat(block1, block2), block); // Shouldn't be equal any more due to the encryption. } // Decrypt and verify. using (var stream = transferFile.GetStream()) { using (var decryptor = new StreamDecryptor(key)) { var ms = new MemoryStream(); decryptor.Decrypt(stream, ms); CollectionAssert.AreEqual(Helper.Concat(block1, block2), ms.ToArray()); } } } finally { cache.Stop(); } }
/// <summary> /// Scans the input stream, validating its structure and loading /// the package entries. /// </summary> private void Load() { PackageHeader header; byte[] hash; packageIn.Position = 0; header = new PackageHeader(packageIn); cbHeader = (int)packageIn.Position; // Compute a MD5 hash on the rest of then file and compare // the result to what we read from the header. hash = MD5Hasher.Compute(packageIn, packageIn.Length - cbHeader); if (hash.Length != header.Hash.Length) { throw new PackageException(CorruptPackage); } for (int i = 0; i < hash.Length; i++) { if (hash[i] != header.Hash[i]) { throw new PackageException(CorruptPackage); } } // Read the package entry headers. packageIn.Position = cbHeader; while (true) { var entry = new PackageEntry(this, packageIn); if (entry.IsEol) { break; } entries.Add(entry.FullName.ToUpper(), entry); } // Walk back through the entries and link each entry to // its parent folder. foreach (PackageEntry entry in entries.Values) { PackageEntry parent; int pos; if (entry == root) { continue; } pos = entry.FullName.LastIndexOf('/'); if (pos == -1) { throw new PackageException(CorruptPackage); } if (pos == 0) { parent = root; } else { parent = (PackageEntry)entries[entry.FullName.Substring(0, pos).ToUpper()]; if (parent == null) { throw new PackageException(CorruptPackage); } } entry.SetParent(parent); parent.AddChild(entry); } }
/// <summary> /// Authenticates the account credentials against the authentication extension. /// </summary> /// <param name="realm">The authentication realm.</param> /// <param name="account">The account ID.</param> /// <param name="password">The password.</param> /// <returns>A <see cref="AuthenticationResult" /> instance with the result of the operation.</returns> /// <remarks> /// <para> /// The <see cref="AuthenticationResult.Status" /> property indicates the disposition /// of the authentication operation. Extensions will return <see cref="AuthenticationStatus.Authenticated" /> /// if the operation was successful. Authentication failures due to the /// sumbission of invalid credentials will be indicated by returning one of /// the error codes. Extensions may return specific error codes such as /// <see cref="AuthenticationStatus.BadPassword" /> and <see cref="AuthenticationStatus.BadAccount" /> /// or the generic error code <see cref="AuthenticationStatus.AccessDenied" />. /// </para> /// <para> /// The <see cref="AuthenticationResult.MaxCacheTime" /> returns as the maximum time the /// results of the authentication operation should be cached. /// </para> /// </remarks> /// <exception cref="AuthenticationException">Thrown for authentication related exception.</exception> public AuthenticationResult Authenticate(string realm, string account, string password) { OdbcConnection dbCon; OdbcCommand cmd; OdbcDataReader reader = null; MacroProcessor processor; string _conString; string query; int authCode; using (TimedLock.Lock(this)) { if (!IsOpen) { throw new AuthenticationException("Authentication extension is closed."); } cAuthentications++; _conString = conString; // Substitute the credentials into the query template. processor = new MacroProcessor(); processor.Add("realm", SqlHelper.Literal(realm)); processor.Add("account", SqlHelper.Literal(account)); processor.Add("password", SqlHelper.Literal(password)); processor.Add("md5-password", SqlHelper.Literal(MD5Hasher.Compute(password))); processor.Add("sha1-password", SqlHelper.Literal(SHA1Hasher.Compute(password))); processor.Add("sha256-password", SqlHelper.Literal(SHA256Hasher.Compute(password))); processor.Add("sha512-password", SqlHelper.Literal(SHA512Hasher.Compute(password))); query = processor.Expand(queryTemplate); } // Perform the query. dbCon = new OdbcConnection(_conString); dbCon.Open(); try { cmd = dbCon.CreateCommand(); cmd.CommandText = query; cmd.CommandType = CommandType.Text; perf.Queries.Increment(); reader = cmd.ExecuteReader(); if (!reader.Read()) { authCode = (int)AuthenticationStatus.AccessDenied; // Empty result set } else { object o = reader[0]; Type type = o.GetType(); if (type == typeof(byte)) { authCode = (int)(byte)o; } else if (type == typeof(int)) { authCode = (int)o; } else if (type == typeof(long)) { authCode = (int)(long)o; } else { throw new AuthenticationException("ODBC authenticate query returned a [{0}] instead of the expected [integer].", type.Name); } if (authCode < 0 || authCode > 5) { throw new AuthenticationException("ODBC authenticate query returned the invalid return code [{0}]. Valid codes range from 0..5", authCode); } } } catch (Exception e) { perf.Exceptions.Increment(); throw new AuthenticationException(e); } finally { if (reader != null) { reader.Close(); } dbCon.Close(); } return(new AuthenticationResult((AuthenticationStatus)authCode, maxCacheTime)); }