263 lines
10 KiB
C#
263 lines
10 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Password file operations
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Ctor
|
|
/// </summary>
|
|
/// <param name="genesisMeter"></param>
|
|
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public MeterPwdFile(GenesisMeter genesisMeter)
|
|
{
|
|
_genesisMeter = genesisMeter;
|
|
_meterFile = new MeterFile(_genesisMeter);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the already hashed password file if this is already pre-generated.
|
|
/// This is the preparation to the <see cref="WriteMeterPwdFile"/>.
|
|
/// The password file has a unique length of <see cref="MeterPwdDb.PwdFileLength"/>.
|
|
/// </summary>
|
|
/// <param name="hashedPwdFile">hashed password file with constant length as byte array
|
|
/// </param>
|
|
/// <returns>true if length matches the expectations</returns>
|
|
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public Boolean SetHashedPwdFile(Byte[] hashedPwdFile)
|
|
{
|
|
if (MeterPwdDb.PwdFileLength != hashedPwdFile.Length) return false;
|
|
_hashedPasswordFile = new Byte[hashedPwdFile.Length];
|
|
_hashedPasswordFile = hashedPwdFile;
|
|
return true;
|
|
}
|
|
|
|
///// <summary>
|
|
///// 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.
|
|
///// </summary>
|
|
///// <param name="pwdLevels">7 sorted passwords levels: 1, 2, 4, 5, 6, 7, 8</param>
|
|
///// <param name="processorUid">unique ID of processor used for password level 3</param>
|
|
///// <param name="dbSkeletonKey">SkeletonKey from data base for comparison </param>
|
|
///// <returns></returns>
|
|
///// <remarks date="2019-Mai-15" author="Thomas Wiedebusch">
|
|
///// - Initial.
|
|
///// </remarks>
|
|
///// <remarks date="2020-Mai-15" author="Roland Drabesch">
|
|
///// - Insert of Level 3 password with skeletonKey,
|
|
///// - SHA 1 of passwords.
|
|
///// </remarks>
|
|
///// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
|
|
///// - .
|
|
///// </remarks>
|
|
///// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
|
|
///// - Set Level 3 password to modified <see cref="BuildLevel3Pwd"/> from processor UID,
|
|
///// - Removed skeletonKey as this cannot be used here!
|
|
///// </remarks>
|
|
///// <remarks date="2021-Feb-01" author="Thomas Wiedebusch">
|
|
///// - Set Level 3 password to dbSkeletonKey if processorUid is unknown (processorUid == 1)!
|
|
///// </remarks>
|
|
//public Boolean BuildPwdFile(List<Byte[]> 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<Byte>();
|
|
|
|
// 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;
|
|
//}
|
|
|
|
/// <summary>
|
|
/// Build the password file out of the clear text passwords.
|
|
/// The Level 3 password is already preset.
|
|
/// </summary>
|
|
/// <param name="pwdLevels">8 sorted passwords levels: 1, 2, 3, 4, 5, 6, 7, 8</param>
|
|
/// <param name="processorUid">unique ID of processor used for password level 3</param>
|
|
/// <param name="dbSkeletonKey">SkeletonKey for comparison </param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Mai-15" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Mai-15" author="Roland Drahbesh">
|
|
/// - All 8 password levels will be passed,
|
|
/// - Password Level 3 will be compared with data base skeletonKey,
|
|
/// - SHA 1 of passwords.
|
|
/// </remarks>
|
|
public Boolean BuildPwdFile(List<Byte[]> 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<Byte>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build skeleton key out of unique processor Id
|
|
/// </summary>
|
|
/// <param name="processorUid"></param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Mai-03" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public static String BuildSkeletonKey(UInt64 processorUid)
|
|
{
|
|
return $"{(UInt16)((processorUid >> 48) ^ (processorUid >> 32)):X4}" +
|
|
$"{(UInt32)((processorUid >> 16) ^ processorUid):X8}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build level 3 password out of unique processor Id
|
|
/// </summary>
|
|
/// <param name="processorUid"></param>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Mai-03" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public static String BuildLevel3Pwd(UInt64 processorUid)
|
|
{
|
|
//password level 3 algorithm
|
|
return $"{(UInt16)((processorUid >> 48) ^ processorUid):X4}" +
|
|
$"{(UInt32)((processorUid >> 32) ^ processorUid):X8}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write the meter password file, which was generated at <see cref="BuildPwdFile"/> or
|
|
/// use the already hashed password file if this is already pre-generated.
|
|
/// The password file has a unique length of <see cref="MeterPwdDb.PwdFileLength"/>.
|
|
/// </summary>
|
|
/// <param name="hashedPwdFile">hashed password file with constant length as byte array
|
|
/// </param>
|
|
/// <returns>true if length matches the expectations and password file could be written
|
|
/// </returns>
|
|
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
|
|
/// - Modified to take optional hashed password file as input.
|
|
/// </remarks>
|
|
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;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Verify Meter password file
|
|
/// generated passowrd file has to be present
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2023-JAn-24" author="Thomas Wiedebusch&Roalnd Drabesch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
|
|
public Boolean VerifyMeterPwdFile()
|
|
{
|
|
if (_hashedPasswordFile == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return _meterFile.VerifyMeterFile(StrPasswordFileName, _hashedPasswordFile);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erase the meter password file
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public Boolean EraseMeterPwdFile()
|
|
{
|
|
if (_meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn)
|
|
{
|
|
return _meterFile.EraseMeterFile(StrPasswordFileName);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unlock access to password file for write and erase
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
|
|
/// - Initial.
|
|
/// </remarks>
|
|
public Boolean UnlockEraseWriteMeterPwdFile()
|
|
{
|
|
if (_meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn)
|
|
{
|
|
return _meterFile.UnlockEraseWriteMeterFile(StrPasswordFileName);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|