using System; using System.Collections.Generic; using System.Linq; using System.Text; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore; using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd; namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile { /// /// Password file operations /// public class MeterPwdFile { public const String StrPasswordFileName = "0\\password"; private readonly GenesisMeter _genesisMeter; private readonly MeterFile _meterFile; private Byte[] _hashedPasswordFile; //seven passwords are needed for Level 1, 2, 4, 5, 6, 7, Level 3 will be generated //out of the ProcessorUID. //private const Int32 PwdLevels = 7; /// /// Ctor /// /// /// /// - Initial. /// public MeterPwdFile(GenesisMeter genesisMeter) { _genesisMeter = genesisMeter; _meterFile = new MeterFile(_genesisMeter); } /// /// Set the already hashed password file if this is already pre-generated. /// This is the preparation to the . /// The password file has a unique length of . /// /// hashed password file with constant length as byte array /// /// true if length matches the expectations /// /// - Initial. /// public Boolean SetHashedPwdFile(Byte[] hashedPwdFile) { if (MeterPwdDb.PwdFileLength != hashedPwdFile.Length) return false; _hashedPasswordFile = new Byte[hashedPwdFile.Length]; _hashedPasswordFile = hashedPwdFile; return true; } ///// ///// Build the password file out of the clear text passwords and the processor UID, ///// password Level 3 has to be build out of the Processor UID. ///// ///// 7 sorted passwords levels: 1, 2, 4, 5, 6, 7, 8 ///// unique ID of processor used for password level 3 ///// SkeletonKey from data base for comparison ///// ///// ///// - Initial. ///// ///// ///// - Insert of Level 3 password with skeletonKey, ///// - SHA 1 of passwords. ///// ///// ///// - . ///// ///// ///// - Set Level 3 password to modified from processor UID, ///// - Removed skeletonKey as this cannot be used here! ///// ///// ///// - Set Level 3 password to dbSkeletonKey if processorUid is unknown (processorUid == 1)! ///// //public Boolean BuildPwdFile(List pwdLevels, UInt64 processorUid, String dbSkeletonKey) //{ // if (pwdLevels.Count != PwdLevels || processorUid == 0 || // string.IsNullOrEmpty(dbSkeletonKey) && processorUid == 1) // { // return false; // } // // selective Level 3 password // if (processorUid == 1 && !string.IsNullOrEmpty(dbSkeletonKey)) // { // pwdLevels.Insert(2, Encoding.UTF8.GetBytes(dbSkeletonKey)); // } // else // { // pwdLevels.Insert(2,Encoding.UTF8.GetBytes(BuildLevel3Pwd(processorUid))); // } // var ret = new List(); // foreach (var keyLvl in pwdLevels) // { // using (var sha1 = new System.Security.Cryptography.SHA1Managed()) // { // var hash = sha1.ComputeHash(keyLvl); // ret.AddRange(hash.ToList()); // } // } // _hashedPasswordFile = ret.ToArray(); // return true; //} /// /// Build the password file out of the clear text passwords. /// The Level 3 password is already preset. /// /// 8 sorted passwords levels: 1, 2, 3, 4, 5, 6, 7, 8 /// unique ID of processor used for password level 3 /// SkeletonKey for comparison /// /// /// - Initial. /// /// /// - All 8 password levels will be passed, /// - Password Level 3 will be compared with data base skeletonKey, /// - SHA 1 of passwords. /// public Boolean BuildPwdFile(List pwdLevels, UInt64 processorUid, String dbSkeletonKey) { if (!Encoding.UTF8.GetBytes(dbSkeletonKey).SequenceEqual(pwdLevels[2])) { throw new ApplicationException("Level 3 Password is unequal to the old Data Base Content!"); } var ret = new List(); foreach (var keyLvl in pwdLevels) { using (var sha1 = new System.Security.Cryptography.SHA1Managed()) { var hash = sha1.ComputeHash(keyLvl); ret.AddRange(hash.ToList()); } } _hashedPasswordFile = ret.ToArray(); return true; } /// /// Build skeleton key out of unique processor Id /// /// /// /// /// - Initial. /// public static String BuildSkeletonKey(UInt64 processorUid) { return $"{(UInt16)((processorUid >> 48) ^ (processorUid >> 32)):X4}" + $"{(UInt32)((processorUid >> 16) ^ processorUid):X8}"; } /// /// Build level 3 password out of unique processor Id /// /// /// /// /// - Initial. /// public static String BuildLevel3Pwd(UInt64 processorUid) { //password level 3 algorithm return $"{(UInt16)((processorUid >> 48) ^ processorUid):X4}" + $"{(UInt32)((processorUid >> 32) ^ processorUid):X8}"; } /// /// Write the meter password file, which was generated at or /// use the already hashed password file if this is already pre-generated. /// The password file has a unique length of . /// /// hashed password file with constant length as byte array /// /// true if length matches the expectations and password file could be written /// /// /// - Initial. /// /// /// - Modified to take optional hashed password file as input. /// public Boolean WriteMeterPwdFile(Byte[] hashedPwdFile = null) { if (hashedPwdFile != null) { if (!SetHashedPwdFile(hashedPwdFile)) return false; } if (_hashedPasswordFile != null && _meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn && _hashedPasswordFile.Length == MeterPwdDb.PwdFileLength) { return _meterFile.WriteMeterFile(StrPasswordFileName, _hashedPasswordFile); } return false; } /// /// Verify Meter password file /// generated passowrd file has to be present /// /// /// /// - Initial. /// public Boolean VerifyMeterPwdFile() { if (_hashedPasswordFile == null) { return false; } return _meterFile.VerifyMeterFile(StrPasswordFileName, _hashedPasswordFile); } /// /// Erase the meter password file /// /// /// /// - Initial. /// public Boolean EraseMeterPwdFile() { if (_meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn) { return _meterFile.EraseMeterFile(StrPasswordFileName); } return false; } /// /// Unlock access to password file for write and erase /// /// /// /// - Initial. /// public Boolean UnlockEraseWriteMeterPwdFile() { if (_meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn) { return _meterFile.UnlockEraseWriteMeterFile(StrPasswordFileName); } return false; } } }