Common: - password file check extended
This commit is contained in:
parent
80c53f5b32
commit
5393db957b
@ -12,19 +12,37 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// </summary>
|
||||
public class MeterPwdFile
|
||||
{
|
||||
// The FwUpdateSw references this string
|
||||
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>
|
||||
/// Hashed password file return
|
||||
/// </summary>
|
||||
public Byte[] HashedPasswordFile
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctor for usage of build of password file without writing it
|
||||
/// </summary>
|
||||
/// <remarks date="2023-Oct-17" author="Thomas Wiedebusch">
|
||||
/// - Initial.
|
||||
/// </remarks>
|
||||
public MeterPwdFile()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
/// <param name="genesisMeter"></param>
|
||||
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
|
||||
/// <remarks date="2019-May-02" author="Thomas Wiedebusch">
|
||||
/// - Initial.
|
||||
/// </remarks>
|
||||
public MeterPwdFile(GenesisMeter genesisMeter)
|
||||
@ -45,11 +63,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
|
||||
/// - Initial.
|
||||
/// </remarks>
|
||||
public Boolean SetHashedPwdFile(Byte[] hashedPwdFile)
|
||||
private Boolean SetHashedPwdFile(Byte[] hashedPwdFile)
|
||||
{
|
||||
if (MeterPwdDb.PwdFileLength != hashedPwdFile.Length) return false;
|
||||
_hashedPasswordFile = new Byte[hashedPwdFile.Length];
|
||||
_hashedPasswordFile = hashedPwdFile;
|
||||
HashedPasswordFile = new Byte[hashedPwdFile.Length];
|
||||
HashedPasswordFile = hashedPwdFile;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -113,37 +131,95 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
|
||||
/// <summary>
|
||||
/// Build the password file out of the clear text passwords.
|
||||
/// The Level 3 password is already preset.
|
||||
/// The Level 3 password is already preset with skeletonKey.
|
||||
///
|
||||
/// The skeleton key has to be unequal to the level 8 password, otherwise the skeleton key has been
|
||||
/// overwritten with this password level 8 locking out all applications which need this key at the
|
||||
/// level 3 to login in the configuration of the Cordonel.
|
||||
///
|
||||
/// ATTENTION - PRECONDITIONS:
|
||||
/// All passwords have to be preset in the list of passwords:
|
||||
/// Level 1: random from password service
|
||||
/// Level 2: random from password service
|
||||
/// Level 3: skeletonKey, this is the initial password used in production, used by applications to
|
||||
/// login to the configuration system of the Cordonel
|
||||
/// Level 4: random from password service
|
||||
/// Level 5: random from password service
|
||||
/// Level 6: random from password service
|
||||
/// Level 7: random from password service
|
||||
/// Level 8: random from password service, this is the passwordLvl8 used in production after
|
||||
/// password file installation
|
||||
///
|
||||
/// Checks ( each failed check is going to throw an exception):
|
||||
/// - Checks for individual clear text password length,
|
||||
/// - Checks if list of clear text passwords are containing 8 levels,
|
||||
/// - Checks that password level 3 is set to skeleton key,
|
||||
/// - Checks that skeleton key is unequal to password level 8,
|
||||
/// - Check file size of generated password file.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pwdLevels">8 sorted passwords levels: 1, 2, 3, 4, 5, 6, 7, 8</param>
|
||||
/// <param name="passwords">8 sorted passwords levels: 1, 2, skeletonKey, 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">
|
||||
/// <remarks date="2020-Mai-15" author="Roland Drabesch">
|
||||
/// - 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)
|
||||
/// <remarks date="2023-Oct-11" author="Thomas Wiedebusch">
|
||||
/// - Throw exception if clear text password length is out of range.
|
||||
/// - Throw exception if password list is out of range.
|
||||
/// - Throw exception if password level3 does not contain the skeleton key.
|
||||
/// - Throw exception if skeletonKey is overwritten with passwordLvl8.
|
||||
/// - Throw exception if password file is out of range.
|
||||
/// </remarks>
|
||||
public Boolean BuildPwdFile(List<Byte[]> passwords, UInt64 processorUid, String dbSkeletonKey)
|
||||
{
|
||||
if (!Encoding.UTF8.GetBytes(dbSkeletonKey).SequenceEqual(pwdLevels[2]))
|
||||
// The length of each password has to be 12 bytes
|
||||
if (passwords.Any( x => x.Length != MeterPwdDb.ClearTextPwdLength))
|
||||
{
|
||||
throw new ApplicationException("Level 3 Password is unequal to the old Data Base Content!");
|
||||
throw new ApplicationException("Individual password length is out of range! " +
|
||||
$"Expected length for each password: {MeterPwdDb.ClearTextPwdLength}");
|
||||
}
|
||||
var ret = new List<Byte>();
|
||||
// The password list has to contain 8 passwords
|
||||
if (passwords.Count != MeterPwdDb.PwdLevels)
|
||||
{
|
||||
throw new ApplicationException("Number of passwords is out of range! " +
|
||||
$"Expected: {MeterPwdDb.PwdLevels}, " +
|
||||
$"Transmitted: {passwords.Count }");
|
||||
}
|
||||
// The skeleton key has to be preset on level 3
|
||||
if (!Encoding.UTF8.GetBytes(dbSkeletonKey).SequenceEqual(passwords[MeterPwdDb.SkeletonKeyIdx]))
|
||||
{
|
||||
throw new ApplicationException("SkeletonKey is not set on password file level 3!");
|
||||
}
|
||||
// The skeleton key has to be unequal to the level 8 password
|
||||
if (Encoding.UTF8.GetBytes(dbSkeletonKey).SequenceEqual(passwords[MeterPwdDb.PwdLevel8Idx]))
|
||||
{
|
||||
throw new ApplicationException("SkeletonKey is overwritten with password level 8!");
|
||||
}
|
||||
var hashedPasswords = new List<Byte>();
|
||||
|
||||
foreach (var keyLvl in pwdLevels)
|
||||
foreach (var password in passwords)
|
||||
{
|
||||
using (var sha1 = new System.Security.Cryptography.SHA1Managed())
|
||||
{
|
||||
var hash = sha1.ComputeHash(keyLvl);
|
||||
ret.AddRange(hash.ToList());
|
||||
var passwordHash = sha1.ComputeHash(password);
|
||||
hashedPasswords.AddRange(passwordHash.ToList());
|
||||
}
|
||||
}
|
||||
_hashedPasswordFile = ret.ToArray();
|
||||
// The password file size has to be 160 bytes
|
||||
if (hashedPasswords.ToArray().Length != MeterPwdDb.PwdFileLength)
|
||||
{
|
||||
throw new ApplicationException("Generated password file is out of range! " +
|
||||
$"Expected: {MeterPwdDb.PwdFileLength}, " +
|
||||
$"Transmitted: {hashedPasswords.ToArray().Length }");
|
||||
}
|
||||
HashedPasswordFile = hashedPasswords.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -177,12 +253,33 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the meter password file, which was generated at <see cref="BuildPwdFile"/> or
|
||||
/// This function combines the write, read back and verification of the password file.
|
||||
/// Write password file <see cref="WriteMeterPwdFile"/> and compare it <see cref="VerifyMeterPwdFile"/>
|
||||
/// The hashed password file can be set or has been preprocessed using the <see cref="BuildPwdFile"/>
|
||||
/// </summary>
|
||||
/// <param name="hashedPwdFile">optional hashed password file with constant length as byte array </param>
|
||||
/// <returns>true if password file could be written, read back byte by byte
|
||||
/// and compared being identical </returns>
|
||||
/// <remarks date="2023-Oct-11" author="Thomas Wiedebusch">
|
||||
/// - Initial, imported from ProductionProcessPasswordFile.
|
||||
/// </remarks>
|
||||
public Boolean WriteAndVerifyMeterPwdFile(Byte[] hashedPwdFile = null)
|
||||
{
|
||||
if (UnlockEraseWriteMeterPwdFile())
|
||||
{
|
||||
if (WriteMeterPwdFile(hashedPwdFile))
|
||||
{
|
||||
return VerifyMeterPwdFile();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <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>
|
||||
/// <param name="hashedPwdFile">optional 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">
|
||||
@ -198,33 +295,36 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
|
||||
if (!SetHashedPwdFile(hashedPwdFile)) return false;
|
||||
}
|
||||
|
||||
if (_hashedPasswordFile != null && _meterFile != null && _genesisMeter != null
|
||||
&& _genesisMeter.IsLoggedOn && _hashedPasswordFile.Length == MeterPwdDb.PwdFileLength)
|
||||
if (HashedPasswordFile != null && _meterFile != null && _genesisMeter != null
|
||||
&& _genesisMeter.IsLoggedOn && HashedPasswordFile.Length == MeterPwdDb.PwdFileLength)
|
||||
{
|
||||
return _meterFile.WriteMeterFile(StrPasswordFileName, _hashedPasswordFile);
|
||||
return _meterFile.WriteMeterFile(StrPasswordFileName, HashedPasswordFile);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Verify Meter password file
|
||||
/// generated passowrd file has to be present
|
||||
/// Verify Meter password file.
|
||||
/// <param name="hashedPwdFile">optional hashed password file with constant length as byte array </param>
|
||||
/// <returns>true if the read value is identical with the preset or given hashed value</returns>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2023-JAn-24" author="Thomas Wiedebusch&Roalnd Drabesch">
|
||||
/// <remarks date="2023-Jan-24" author="Thomas Wiedebusch/Roland Drabesch">
|
||||
/// - Initial.
|
||||
/// </remarks>
|
||||
|
||||
public Boolean VerifyMeterPwdFile()
|
||||
public Boolean VerifyMeterPwdFile(Byte[] hashedPwdFile = null)
|
||||
{
|
||||
if (_hashedPasswordFile == null)
|
||||
if (hashedPwdFile != null)
|
||||
{
|
||||
if (!SetHashedPwdFile(hashedPwdFile)) return false;
|
||||
}
|
||||
if (HashedPasswordFile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _meterFile.VerifyMeterFile(StrPasswordFileName, _hashedPasswordFile);
|
||||
|
||||
return _meterFile.VerifyMeterFile(StrPasswordFileName, HashedPasswordFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -16,9 +16,24 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd
|
||||
public class MeterPwdDb
|
||||
{
|
||||
/// <summary>
|
||||
/// password file length is 160 bytes (8 passwords a 20 bytes SHA1)
|
||||
/// 8 passwords needed for clear the text passwords ans the password file
|
||||
/// </summary>
|
||||
public const Int32 PwdFileLength = 160;
|
||||
public const Int32 PwdLevels = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Each password will generate a 20 bytes hash constant due to SHA1 algorithm
|
||||
/// </summary>
|
||||
public const Int32 HashedPwdLength = 20;
|
||||
|
||||
/// <summary>
|
||||
/// password file length is 160 bytes (8 passwords a 20 bytes hashed)
|
||||
/// </summary>
|
||||
public const Int32 PwdFileLength = PwdLevels * HashedPwdLength;
|
||||
|
||||
/// <summary>
|
||||
/// 8 passwords needed for the password file
|
||||
/// </summary>
|
||||
public const Int32 ClearTextPwdLength = 12;
|
||||
|
||||
/// <summary>
|
||||
/// Skeleton key index in password list.
|
||||
@ -60,7 +75,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd
|
||||
/// </summary>
|
||||
public List<Byte[]> ListOfPasswords = new List<Byte[]>();
|
||||
/// <summary>
|
||||
/// Ident for password server (BSI) can be radioaddress or pcbid
|
||||
/// Ident for password server (BSI) can be radio address or pcbid
|
||||
/// </summary>
|
||||
public String PasswordFileIdent { get; set; }
|
||||
/// <summary>
|
||||
|
||||
@ -56,7 +56,10 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collect order number, radio address, skeleton key, password hashes and passwords from DB.
|
||||
/// Overwrites the (production) password used for production in DB.
|
||||
/// Initially this (production) password is set to the skeletonKey as the password file is not installed.
|
||||
/// After installation of the password file the (production) password has to be set to the level 8 password
|
||||
/// being able to access the meter with the production tools and the GTB.
|
||||
/// </summary>
|
||||
/// <param name="pcbId">input the PcbId</param>
|
||||
/// <param name="passwordContainer">output of passwordContainer</param>
|
||||
|
||||
@ -154,6 +154,7 @@ namespace ProductionUiCordonel.ProductionProcesses.Actions
|
||||
throw;
|
||||
}
|
||||
//;
|
||||
//TODO ROLAND only new meters can be tested which have a LUT installed
|
||||
if (Meter.LutCrc != $@"0x{int.Parse(res[8]):X4}")
|
||||
{
|
||||
currentProcessState = ProductionProcessState.Error;
|
||||
|
||||
@ -135,8 +135,17 @@ namespace ProductionUiCordonel.ProductionProcesses.Actions
|
||||
#region realWork
|
||||
public Boolean CreatePasswordFile()
|
||||
{
|
||||
_meterPwdFile = new MeterPwdFile(Meter);
|
||||
return _meterPwdFile.BuildPwdFile(_pwdContainer.ListOfPasswords, 1, _pwdContainer.Skeleton);
|
||||
try
|
||||
{
|
||||
_meterPwdFile = new MeterPwdFile(Meter);
|
||||
return _meterPwdFile.BuildPwdFile(_pwdContainer.ListOfPasswords, 1, _pwdContainer.Skeleton);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// log error message of wrong generated password file
|
||||
NewStatus(e.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public Boolean WriteAndVerifyPasswordFileToMeter()
|
||||
{
|
||||
|
||||
18
Common/Ui/GenesisToolBox/frmPassword.Designer.cs
generated
18
Common/Ui/GenesisToolBox/frmPassword.Designer.cs
generated
@ -32,7 +32,7 @@
|
||||
this.btnGet = new System.Windows.Forms.Button();
|
||||
this.btnSet = new System.Windows.Forms.Button();
|
||||
this.txtPcbID = new System.Windows.Forms.TextBox();
|
||||
this.txtPassword = new System.Windows.Forms.TextBox();
|
||||
this.txtSkeletonKey = new System.Windows.Forms.TextBox();
|
||||
this.lblPcbID = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.grpSetup = new System.Windows.Forms.GroupBox();
|
||||
@ -84,13 +84,13 @@
|
||||
this.txtPcbID.TabIndex = 2;
|
||||
this.txtPcbID.TextChanged += new System.EventHandler(this.txtPcbID_TextChanged);
|
||||
//
|
||||
// txtPassword
|
||||
// txtSkeletonKey
|
||||
//
|
||||
this.txtPassword.Location = new System.Drawing.Point(118, 67);
|
||||
this.txtPassword.Name = "txtPassword";
|
||||
this.txtPassword.Size = new System.Drawing.Size(102, 20);
|
||||
this.txtPassword.TabIndex = 3;
|
||||
this.txtPassword.TextChanged += new System.EventHandler(this.txtPassword_TextChanged);
|
||||
this.txtSkeletonKey.Location = new System.Drawing.Point(118, 67);
|
||||
this.txtSkeletonKey.Name = "txtSkeletonKey";
|
||||
this.txtSkeletonKey.Size = new System.Drawing.Size(102, 20);
|
||||
this.txtSkeletonKey.TabIndex = 3;
|
||||
this.txtSkeletonKey.TextChanged += new System.EventHandler(this.txtPassword_TextChanged);
|
||||
//
|
||||
// lblPcbID
|
||||
//
|
||||
@ -203,7 +203,7 @@
|
||||
this.grpDataBase.Controls.Add(this.barProgressDb);
|
||||
this.grpDataBase.Controls.Add(this.label1);
|
||||
this.grpDataBase.Controls.Add(this.lblPcbID);
|
||||
this.grpDataBase.Controls.Add(this.txtPassword);
|
||||
this.grpDataBase.Controls.Add(this.txtSkeletonKey);
|
||||
this.grpDataBase.Controls.Add(this.txtPcbID);
|
||||
this.grpDataBase.Controls.Add(this.btnSet);
|
||||
this.grpDataBase.Controls.Add(this.btnGet);
|
||||
@ -303,7 +303,7 @@
|
||||
private System.Windows.Forms.Button btnGet;
|
||||
private System.Windows.Forms.Button btnSet;
|
||||
private System.Windows.Forms.TextBox txtPcbID;
|
||||
private System.Windows.Forms.TextBox txtPassword;
|
||||
private System.Windows.Forms.TextBox txtSkeletonKey;
|
||||
private System.Windows.Forms.Label lblPcbID;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.GroupBox grpSetup;
|
||||
|
||||
@ -104,14 +104,15 @@ namespace Xylem.Common.Ui.GenesisToolBox
|
||||
}
|
||||
var responseJson = sr.ReadToEnd();
|
||||
//TODO THW hide skeleton key if read from DB
|
||||
txtPassword.Text = responseJson.Trim('"');
|
||||
//txtSkeletonKey.Text = responseJson.Trim('"');
|
||||
txtSkeletonKey.Text = a.Skeleton;
|
||||
txtLvl8.Text = autoPassword;
|
||||
lblConnectDb.Text = @"Connected to PWD DB";
|
||||
lblConnectDb.ForeColor = Color.Green;
|
||||
|
||||
////TODO TEST ONLY remove here _isReadyPasswordFile
|
||||
_isReadyPasswordFile = true;
|
||||
btnWritePwdFile.Enabled = true;
|
||||
//btnWritePwdFile.Enabled = true;
|
||||
////TODO remove until here
|
||||
|
||||
}));
|
||||
@ -140,7 +141,7 @@ namespace Xylem.Common.Ui.GenesisToolBox
|
||||
var passwordSetServiceUrl = ServiceUrls.GenesisSetPasswordServiceUrl();
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
var http = (HttpWebRequest)WebRequest.Create(passwordSetServiceUrl + txtPcbID.Text
|
||||
+ "&Password=" + txtPassword.Text);
|
||||
+ "&Password=" + txtSkeletonKey.Text);
|
||||
var response = (HttpWebResponse)http.GetResponse();
|
||||
var responseStream = response.GetResponseStream();
|
||||
if (responseStream == null)
|
||||
@ -250,7 +251,7 @@ namespace Xylem.Common.Ui.GenesisToolBox
|
||||
lblConnectPcb.Text = StrNotConnected;
|
||||
lblConnectPcb.ForeColor = Color.Red;
|
||||
txtPcbID.Text = "";
|
||||
txtPassword.Text = "";
|
||||
txtSkeletonKey.Text = "";
|
||||
btnGet.Enabled = false;
|
||||
btnSet.Enabled = false;
|
||||
if (!string.IsNullOrEmpty(cbComSlot.SelectedItem.ToString()) &&
|
||||
@ -324,7 +325,7 @@ namespace Xylem.Common.Ui.GenesisToolBox
|
||||
lblConnectDb.ForeColor = Color.Red;
|
||||
btnConnect.Enabled = true;
|
||||
txtPcbID.Text = "";
|
||||
txtPassword.Text = "";
|
||||
txtSkeletonKey.Text = "";
|
||||
btnGet.Enabled = false;
|
||||
btnSet.Enabled = false;
|
||||
btnWritePwdFile.Enabled = false;
|
||||
@ -343,13 +344,13 @@ namespace Xylem.Common.Ui.GenesisToolBox
|
||||
}
|
||||
lblConnectDb.Text = StrNotConnected;
|
||||
lblConnectDb.ForeColor = Color.Red;
|
||||
txtPassword.Text = "";
|
||||
txtSkeletonKey.Text = "";
|
||||
}
|
||||
|
||||
private void txtPassword_TextChanged(Object sender, EventArgs e)
|
||||
{
|
||||
//setting is only allowed if genesis is not connected
|
||||
if (txtPcbID.Text.Length > 7 && txtPassword.Text.Length == 12
|
||||
if (txtPcbID.Text.Length > 7 && txtSkeletonKey.Text.Length == 12
|
||||
&& lblConnectPcb.Text == StrNotConnected)
|
||||
{
|
||||
btnSet.Enabled = true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user