mirror of
https://github.com/KeyKeeperApp/KeyKeeper.git
synced 2026-05-04 05:46:29 +03:00
add TOTP parameters to the file format
This commit is contained in:
@@ -25,6 +25,11 @@ static class FileFormatConstants
|
|||||||
public const byte LOGIN_FIELD_ACCOUNT_NUMBER_ID = 0x03;
|
public const byte LOGIN_FIELD_ACCOUNT_NUMBER_ID = 0x03;
|
||||||
public const byte LOGIN_FIELD_NOTES_ID = 0x04;
|
public const byte LOGIN_FIELD_NOTES_ID = 0x04;
|
||||||
public const byte LOGIN_FIELD_CUSTOM_ID = 0xff; // пока не используется
|
public const byte LOGIN_FIELD_CUSTOM_ID = 0xff; // пока не используется
|
||||||
|
public const byte TOTP_ABSENT = 0x00;
|
||||||
|
public const byte TOTP_PRESENT = 0x01;
|
||||||
|
public const byte TOTP_ALGO_SHA1 = 0x00;
|
||||||
|
public const byte TOTP_ALGO_SHA256 = 0x01;
|
||||||
|
public const byte TOTP_ALGO_SHA512 = 0x02;
|
||||||
public const byte GROUP_TYPE_ROOT = 0x00;
|
public const byte GROUP_TYPE_ROOT = 0x00;
|
||||||
public const byte GROUP_TYPE_DEFAULT = 0x01;
|
public const byte GROUP_TYPE_DEFAULT = 0x01;
|
||||||
public const byte GROUP_TYPE_FAVOURITES = 0x02;
|
public const byte GROUP_TYPE_FAVOURITES = 0x02;
|
||||||
|
|||||||
@@ -46,31 +46,37 @@ public abstract class PassStoreEntry
|
|||||||
BinaryReader rd = new(str);
|
BinaryReader rd = new(str);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rd.Read7BitEncodedInt();
|
int entryLength = rd.Read7BitEncodedInt();
|
||||||
|
byte[] entryData = rd.ReadBytes(entryLength);
|
||||||
|
if (entryData.Length < entryLength)
|
||||||
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
|
|
||||||
|
MemoryStream entryStream = new(entryData);
|
||||||
|
BinaryReader entryRd = new(entryStream);
|
||||||
|
|
||||||
byte[] uuidBuffer = new byte[16];
|
byte[] uuidBuffer = new byte[16];
|
||||||
if (rd.Read(uuidBuffer) < 16)
|
if (entryRd.Read(uuidBuffer) < 16)
|
||||||
throw PassStoreFileException.UnexpectedEndOfFile;
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
Guid id = new Guid(uuidBuffer);
|
Guid id = new Guid(uuidBuffer);
|
||||||
|
|
||||||
ulong timestamp = FileFormatUtil.ReadVarUint16(str);
|
ulong timestamp = FileFormatUtil.ReadVarUint16(entryStream);
|
||||||
DateTime createdAt = DateTimeOffset.FromUnixTimeSeconds((long)timestamp).UtcDateTime;
|
DateTime createdAt = DateTimeOffset.FromUnixTimeSeconds((long)timestamp).UtcDateTime;
|
||||||
timestamp = FileFormatUtil.ReadVarUint16(str);
|
timestamp = FileFormatUtil.ReadVarUint16(entryStream);
|
||||||
DateTime modifiedAt = DateTimeOffset.FromUnixTimeSeconds((long)timestamp).UtcDateTime;
|
DateTime modifiedAt = DateTimeOffset.FromUnixTimeSeconds((long)timestamp).UtcDateTime;
|
||||||
|
|
||||||
if (rd.Read(uuidBuffer) < 16)
|
if (entryRd.Read(uuidBuffer) < 16)
|
||||||
throw PassStoreFileException.UnexpectedEndOfFile;
|
throw PassStoreFileException.UnexpectedEndOfFile;
|
||||||
Guid iconType = new Guid(uuidBuffer);
|
Guid iconType = new Guid(uuidBuffer);
|
||||||
|
|
||||||
string name = FileFormatUtil.ReadU16TaggedString(str);
|
string name = FileFormatUtil.ReadU16TaggedString(entryStream);
|
||||||
|
|
||||||
byte entryType = rd.ReadByte();
|
byte entryType = entryRd.ReadByte();
|
||||||
if (entryType == ENTRY_GROUP_ID)
|
if (entryType == ENTRY_GROUP_ID)
|
||||||
{
|
{
|
||||||
return PassStoreEntryGroup.ReadFromStream(str, id, createdAt, modifiedAt, iconType, name);
|
return PassStoreEntryGroup.ReadFromStream(entryStream, id, createdAt, modifiedAt, iconType, name);
|
||||||
} else if (entryType == ENTRY_PASS_ID)
|
} else if (entryType == ENTRY_PASS_ID)
|
||||||
{
|
{
|
||||||
return PassStoreEntryPassword.ReadFromStream(str, id, createdAt, modifiedAt, iconType, name);
|
return PassStoreEntryPassword.ReadFromStream(entryStream, id, createdAt, modifiedAt, iconType, name);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
throw PassStoreFileException.InvalidPassStoreEntry;
|
throw PassStoreFileException.InvalidPassStoreEntry;
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ public class PassStoreEntryPassword : PassStoreEntry
|
|||||||
public LoginField Username { get; set; }
|
public LoginField Username { get; set; }
|
||||||
public LoginField Password { get; set; }
|
public LoginField Password { get; set; }
|
||||||
public List<LoginField> ExtraFields { get; set; }
|
public List<LoginField> ExtraFields { get; set; }
|
||||||
|
public TotpParameters? Totp { get; set; }
|
||||||
|
|
||||||
public PassStoreEntryPassword(Guid id, DateTime createdAt, DateTime modifiedAt, Guid iconType, string name, LoginField username, LoginField password, List<LoginField>? extras = null)
|
public PassStoreEntryPassword(Guid id, DateTime createdAt, DateTime modifiedAt, Guid iconType, string name,
|
||||||
|
LoginField username, LoginField password,
|
||||||
|
List<LoginField>? extras = null, TotpParameters? totp = null)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
CreationDate = createdAt;
|
CreationDate = createdAt;
|
||||||
@@ -21,6 +24,7 @@ public class PassStoreEntryPassword : PassStoreEntry
|
|||||||
Username = username;
|
Username = username;
|
||||||
Password = password;
|
Password = password;
|
||||||
ExtraFields = extras ?? new();
|
ExtraFields = extras ?? new();
|
||||||
|
Totp = totp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PassStoreEntry ReadFromStream(Stream str, Guid id, DateTime createdAt, DateTime modifiedAt, Guid iconType, string name)
|
public static PassStoreEntry ReadFromStream(Stream str, Guid id, DateTime createdAt, DateTime modifiedAt, Guid iconType, string name)
|
||||||
@@ -34,6 +38,27 @@ public class PassStoreEntryPassword : PassStoreEntry
|
|||||||
int extraFieldCount = rd.Read7BitEncodedInt();
|
int extraFieldCount = rd.Read7BitEncodedInt();
|
||||||
for (; extraFieldCount > 0; extraFieldCount--)
|
for (; extraFieldCount > 0; extraFieldCount--)
|
||||||
entry.ExtraFields.Add(ReadField(str));
|
entry.ExtraFields.Add(ReadField(str));
|
||||||
|
|
||||||
|
int totpPresence = str.ReadByte();
|
||||||
|
if (totpPresence == TOTP_PRESENT)
|
||||||
|
{
|
||||||
|
TotpAlgorithm algo = rd.ReadByte() switch
|
||||||
|
{
|
||||||
|
TOTP_ALGO_SHA256 => TotpAlgorithm.SHA256,
|
||||||
|
TOTP_ALGO_SHA512 => TotpAlgorithm.SHA512,
|
||||||
|
_ => TotpAlgorithm.SHA1,
|
||||||
|
};
|
||||||
|
byte digits = rd.ReadByte();
|
||||||
|
int period = rd.Read7BitEncodedInt();
|
||||||
|
string secret = FileFormatUtil.ReadU16TaggedString(str);
|
||||||
|
string issuer = FileFormatUtil.ReadU16TaggedString(str);
|
||||||
|
string accountName = FileFormatUtil.ReadU16TaggedString(str);
|
||||||
|
entry.Totp = new TotpParameters(
|
||||||
|
secret, algo, digits, period,
|
||||||
|
issuer.Length > 0 ? issuer : null,
|
||||||
|
accountName.Length > 0 ? accountName : null);
|
||||||
|
}
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
} catch (EndOfStreamException)
|
} catch (EndOfStreamException)
|
||||||
{
|
{
|
||||||
@@ -58,6 +83,28 @@ public class PassStoreEntryPassword : PassStoreEntry
|
|||||||
wr.Write7BitEncodedInt(ExtraFields.Count);
|
wr.Write7BitEncodedInt(ExtraFields.Count);
|
||||||
foreach (LoginField field in ExtraFields)
|
foreach (LoginField field in ExtraFields)
|
||||||
WriteField(str, field);
|
WriteField(str, field);
|
||||||
|
|
||||||
|
if (Totp is TotpParameters totp)
|
||||||
|
{
|
||||||
|
str.WriteByte(TOTP_PRESENT);
|
||||||
|
byte algoByte = totp.Algorithm switch
|
||||||
|
{
|
||||||
|
TotpAlgorithm.SHA256 => TOTP_ALGO_SHA256,
|
||||||
|
TotpAlgorithm.SHA512 => TOTP_ALGO_SHA512,
|
||||||
|
_ => TOTP_ALGO_SHA1,
|
||||||
|
};
|
||||||
|
str.WriteByte(algoByte);
|
||||||
|
str.WriteByte((byte)totp.Digits);
|
||||||
|
wr.Write7BitEncodedInt(totp.Period);
|
||||||
|
FileFormatUtil.WriteU16TaggedString(str, totp.Secret);
|
||||||
|
FileFormatUtil.WriteU16TaggedString(str, totp.Issuer ?? "");
|
||||||
|
FileFormatUtil.WriteU16TaggedString(str, totp.AccountName ?? "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str.WriteByte(TOTP_ABSENT);
|
||||||
|
}
|
||||||
|
|
||||||
return str.ToArray();
|
return str.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,4 +133,4 @@ public class PassStoreEntryPassword : PassStoreEntry
|
|||||||
field.Value = FileFormatUtil.ReadU16TaggedString(str);
|
field.Value = FileFormatUtil.ReadU16TaggedString(str);
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user