Files
laatzen/Common/Hardware/WaterMeter/Genesis/GenesisFile/MeterPwdFile.cs
T
2023-01-24 16:36:01 +01:00

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;
}
}
}