rename folder keepass2android => keepass2android-app

This commit is contained in:
Philipp Crocoll
2025-01-07 11:20:08 +01:00
parent 738d59dbda
commit 409f6b9981
783 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using KeePassLib.Utility;
namespace KeePass.DataExchange
{
public sealed class CsvOptions
{
private char m_chFieldSep = ',';
public char FieldSeparator
{
get { return m_chFieldSep; }
set { m_chFieldSep = value; }
}
private char m_chRecSep = '\n';
public char RecordSeparator
{
get { return m_chRecSep; }
set { m_chRecSep = value; }
}
private char m_chTextQual = '\"';
public char TextQualifier
{
get { return m_chTextQual; }
set { m_chTextQual = value; }
}
private bool m_bBackEscape = true;
public bool BackslashIsEscape
{
get { return m_bBackEscape; }
set { m_bBackEscape = value; }
}
private bool m_bTrimFields = true;
public bool TrimFields
{
get { return m_bTrimFields; }
set { m_bTrimFields = value; }
}
private string m_strNewLineSeq = "\r\n";
public string NewLineSequence
{
get { return m_strNewLineSeq; }
set
{
if (value == null) throw new ArgumentNullException("value");
m_strNewLineSeq = value;
}
}
}
public sealed class CsvStreamReaderEx
{
private CharStream m_sChars;
private CsvOptions m_opt;
public CsvStreamReaderEx(string strData)
{
Init(strData, null);
}
public CsvStreamReaderEx(string strData, CsvOptions opt)
{
Init(strData, opt);
}
private void Init(string strData, CsvOptions opt)
{
if (strData == null) throw new ArgumentNullException("strData");
m_opt = (opt ?? new CsvOptions());
string strInput = strData;
// Normalize to Unix "\n" right now; the new lines are
// converted back in the <c>AddField</c> method
strInput = StrUtil.NormalizeNewLines(strInput, false);
strInput = strInput.Trim(new char[] { (char)0 });
m_sChars = new CharStream(strInput);
}
public string[] ReadLine()
{
char chFirst = m_sChars.PeekChar();
if (chFirst == char.MinValue) return null;
List<string> v = new List<string>();
StringBuilder sb = new StringBuilder();
bool bInText = false;
char chFS = m_opt.FieldSeparator, chRS = m_opt.RecordSeparator;
char chTQ = m_opt.TextQualifier;
while (true)
{
char ch = m_sChars.ReadChar();
if (ch == char.MinValue) break;
Debug.Assert(ch != '\r'); // Was normalized to Unix "\n"
if ((ch == '\\') && m_opt.BackslashIsEscape)
{
char chEsc = m_sChars.ReadChar();
if (chEsc == char.MinValue) break;
if (chEsc == 'n') sb.Append('\n');
else if (chEsc == 'r') sb.Append('\r');
else if (chEsc == 't') sb.Append('\t');
else if (chEsc == 'u')
{
char chNum1 = m_sChars.ReadChar();
char chNum2 = m_sChars.ReadChar();
char chNum3 = m_sChars.ReadChar();
char chNum4 = m_sChars.ReadChar();
if (chNum4 != char.MinValue) // Implies the others
{
StringBuilder sbNum = new StringBuilder();
sbNum.Append(chNum3); // Little-endian
sbNum.Append(chNum4);
sbNum.Append(chNum1);
sbNum.Append(chNum2);
byte[] pbNum = MemUtil.HexStringToByteArray(sbNum.ToString());
ushort usNum = MemUtil.BytesToUInt16(pbNum);
sb.Append((char)usNum);
}
}
else sb.Append(chEsc);
}
else if (ch == chTQ)
{
if (!bInText) bInText = true;
else // bInText
{
char chNext = m_sChars.PeekChar();
if (chNext == chTQ)
{
m_sChars.ReadChar();
sb.Append(chTQ);
}
else bInText = false;
}
}
else if ((ch == chRS) && !bInText) break;
else if (bInText) sb.Append(ch);
else if (ch == chFS)
{
AddField(v, sb.ToString());
if (sb.Length > 0) sb.Remove(0, sb.Length);
}
else sb.Append(ch);
}
// Debug.Assert(!bInText);
AddField(v, sb.ToString());
return v.ToArray();
}
private void AddField(List<string> v, string strField)
{
// Escape characters might have been used to insert
// new lines that might not conform to Unix "\n"
strField = StrUtil.NormalizeNewLines(strField, false);
// Transform to final form of new lines
strField = strField.Replace("\n", m_opt.NewLineSequence);
if (m_opt.TrimFields) strField = strField.Trim();
v.Add(strField);
}
}
}

View File

@@ -0,0 +1,187 @@
/*
OtpKeyProv Plugin
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using KeePassLib.Utility;
namespace OtpKeyProv
{
public static class EncodingUtil
{
private const string FmtHex = "Hex";
private const string FmtBase64 = "Base64";
private const string FmtBase32 = "Base32";
private const string FmtUtf8 = "UTF-8";
private const string FmtDec = "Dec";
public static readonly string[] Formats = new string[]{
FmtHex, FmtBase64, FmtUtf8, FmtDec, FmtBase32
};
public static OtpDataFmt? GetOtpDataFormat(String strFmt)
{
if(strFmt == null) return null; // No assert
if(strFmt == FmtHex) return OtpDataFmt.Hex;
if(strFmt == FmtBase64) return OtpDataFmt.Base64;
if(strFmt == FmtBase32) return OtpDataFmt.Base32;
if(strFmt == FmtUtf8) return OtpDataFmt.Utf8;
if(strFmt == FmtDec) return OtpDataFmt.Dec;
return null;
}
public static byte[] ParseKey(string strKey, OtpDataFmt fmt)
{
if(strKey == null) { Debug.Assert(false); return null; }
strKey = strKey.Trim();
if(strKey.Length == 0) return null; // No assert
if(fmt == OtpDataFmt.Hex)
{
strKey = strKey.Replace(" ", string.Empty);
strKey = strKey.Replace("\t", string.Empty);
strKey = strKey.Replace("\r", string.Empty);
strKey = strKey.Replace("\n", string.Empty);
if((strKey.Length % 2) == 1) strKey = "0" + strKey;
return MemUtil.HexStringToByteArray(strKey);
}
else if(fmt == OtpDataFmt.Base64)
{
try { return Convert.FromBase64String(strKey); }
catch(Exception) { }
}
else if(fmt == OtpDataFmt.Base32)
return ParseBase32(strKey);
else if(fmt == OtpDataFmt.Utf8)
{
try { return StrUtil.Utf8.GetBytes(strKey); }
catch(Exception) { }
}
else if(fmt == OtpDataFmt.Dec)
{
ulong u;
if(ulong.TryParse(strKey, out u))
{
byte[] pb = MemUtil.UInt64ToBytes(u);
Array.Reverse(pb); // Little endian -> big endian
return pb;
}
}
return null;
}
public static ulong? ParseCounter(string strCounter, OtpDataFmt fmt)
{
byte[] pb = ParseKey(strCounter, fmt);
if(pb == null) return null;
if(pb.Length > 8) return null;
Array.Reverse(pb); // Big endian -> little endian
byte[] pb8 = new byte[8];
Array.Copy(pb, 0, pb8, 0, pb.Length);
return MemUtil.BytesToUInt64(pb8); // Little endian
}
private const string Base32Alph = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
/// <summary>
/// Decode base32 strings according to RFC 4648.
/// </summary>
private static byte[] ParseBase32(string str)
{
if((str == null) || ((str.Length % 8) != 0)) return null;
List<byte> l = new List<byte>();
for(int i = 0; i < str.Length; i += 8)
{
ulong u = 0;
int nBits = 0;
for(int j = 0; j < 8; ++j)
{
char ch = char.ToUpper(str[i + j]);
if(ch == '=') break;
int iValue = Base32Alph.IndexOf(ch);
if(iValue < 0) return null;
u <<= 5;
u += (ulong)iValue;
nBits += 5;
}
int nBitsTooMany = (nBits % 8);
u >>= nBitsTooMany;
nBits -= nBitsTooMany;
Debug.Assert((nBits % 8) == 0);
int idxNewBytes = l.Count;
while(nBits > 0)
{
l.Add((byte)(u & 0xFF));
u >>= 8;
nBits -= 8;
}
l.Reverse(idxNewBytes, l.Count - idxNewBytes);
}
return l.ToArray();
}
internal static void SelfTest()
{
#if DEBUG
byte[] pbRes = ParseBase32("MY======");
byte[] pbExp = Encoding.ASCII.GetBytes("f");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-1");
pbRes = ParseBase32("MZXQ====");
pbExp = Encoding.ASCII.GetBytes("fo");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-2");
pbRes = ParseBase32("MZXW6===");
pbExp = Encoding.ASCII.GetBytes("foo");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-3");
pbRes = ParseBase32("MZXW6YQ=");
pbExp = Encoding.ASCII.GetBytes("foob");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-4");
pbRes = ParseBase32("MZXW6YTB");
pbExp = Encoding.ASCII.GetBytes("fooba");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-5");
pbRes = ParseBase32("MZXW6YTBOI======");
pbExp = Encoding.ASCII.GetBytes("foobar");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-6");
pbRes = ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======");
pbExp = Encoding.ASCII.GetBytes("Key provider based on one-time passwords.");
if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-7");
#endif
}
}
}

View File

@@ -0,0 +1,179 @@
/*
This file was modified my Philipp Crocoll, 2013. Based on:
OtpKeyProv Plugin
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using keepass2android;
using keepass2android.Io;
namespace OtpKeyProv
{
public sealed class OathHotpKeyProv
/*removed base class KeyProvider because "synchronous" interface is not suitable on Android*/
{
public const string AuxFileExt = ".otp.xml";
private const string ProvType = "OATH HOTP / RFC 4226";
private const string ProvVersion = "2.0"; // File version, not OtpKeyProv version
public static string Name
{
get { return "One-Time Passwords (OATH HOTP)"; }
}
public const string ShortProductName = "OtpKeyProv";
public const string ProductName = "OtpKeyProv KeePass Plugin";
/*
private static byte[] Open(KeyProviderQueryContext ctx, OtpInfo otpInfo)
{
if(otpInfo.Type != ProvType)
{
MessageService.ShowWarning("Unknown OTP generator type!");
return null;
}
OtpKeyPromptForm dlg = new OtpKeyPromptForm();
dlg.InitEx(otpInfo, ctx);
if(UIUtil.ShowDialogAndDestroy(dlg) != DialogResult.OK)
return null;
if(!CreateAuxFile(otpInfo, ctx)) return null;
return otpInfo.Secret;
}
* */
/// <summary>
/// Sets the "Secret" field in otpInfo based on the list of entered OTPs (lOtps) or the entered secret itself which is in format fmt
/// </summary>
/// based on the code in OtpKeyPromptForm.cs
public void SetSecret(OtpInfo otpInfo, List<string> lOtps, string secret, OtpDataFmt? fmt)
{
byte[] pbSecret = EncodingUtil.ParseKey(secret,
(fmt.HasValue ? fmt.Value : OtpDataFmt.Hex));
if (pbSecret != null)
{
otpInfo.Secret = pbSecret;
return;
}
if (!string.IsNullOrEmpty(otpInfo.EncryptedSecret)) // < v2.0
{
byte[] pbKey32 = OtpUtil.KeyFromOtps(lOtps.ToArray(), 0,
lOtps.Count, Convert.FromBase64String(
otpInfo.TransformationKey), otpInfo.TransformationRounds);
if (pbKey32 == null) throw new InvalidOperationException();
pbSecret = OtpUtil.DecryptData(otpInfo.EncryptedSecret,
pbKey32, Convert.FromBase64String(otpInfo.EncryptionIV));
if (pbSecret == null) throw new InvalidOperationException();
otpInfo.Secret = pbSecret;
otpInfo.Counter += (ulong) otpInfo.OtpsRequired;
}
else // >= v2.0, supporting look-ahead
{
bool bSuccess = false;
for (int i = 0; i < otpInfo.EncryptedSecrets.Count; ++i)
{
OtpEncryptedData d = otpInfo.EncryptedSecrets[i];
pbSecret = OtpUtil.DecryptSecret(d, lOtps.ToArray(), 0,
lOtps.Count);
if (pbSecret != null)
{
otpInfo.Secret = pbSecret;
otpInfo.Counter += ((ulong) otpInfo.OtpsRequired +
(ulong) i);
bSuccess = true;
break;
}
}
if (!bSuccess) throw new InvalidOperationException();
}
}
public static bool CreateAuxFile(OtpInfo otpInfo,
KeyProviderQueryContext ctx, IOConnectionInfo auxFileIoc)
{
otpInfo.Type = ProvType;
otpInfo.Version = ProvVersion;
otpInfo.Generator = ProductName;
otpInfo.EncryptSecret();
if(!OtpInfo.Save(auxFileIoc, otpInfo))
{
MessageService.ShowWarning("Failed to save auxiliary OTP info file:",
UrlUtil.GetFileName(auxFileIoc.Path));
return false;
}
return true;
}
public static void CreateOtpSecret(List<string> lOtps, OtpInfo otpInfo)
{
byte[] pbSecret;
if (!string.IsNullOrEmpty(otpInfo.EncryptedSecret)) // < v2.0
{
byte[] pbKey32 = OtpUtil.KeyFromOtps(lOtps.ToArray(), 0,
lOtps.Count, Convert.FromBase64String(
otpInfo.TransformationKey), otpInfo.TransformationRounds);
if (pbKey32 == null) throw new InvalidOperationException();
pbSecret = OtpUtil.DecryptData(otpInfo.EncryptedSecret,
pbKey32, Convert.FromBase64String(otpInfo.EncryptionIV));
if (pbSecret == null) throw new InvalidOperationException();
otpInfo.Secret = pbSecret;
otpInfo.Counter += otpInfo.OtpsRequired;
}
else // >= v2.0, supporting look-ahead
{
bool bSuccess = false;
for (int i = 0; i < otpInfo.EncryptedSecrets.Count; ++i)
{
OtpEncryptedData d = otpInfo.EncryptedSecrets[i];
pbSecret = OtpUtil.DecryptSecret(d, lOtps.ToArray(), 0,
lOtps.Count);
if (pbSecret != null)
{
otpInfo.Secret = pbSecret;
otpInfo.Counter += ((ulong) otpInfo.OtpsRequired +
(ulong) i);
bSuccess = true;
break;
}
}
if (!bSuccess) throw new InvalidOperationException();
}
}
}
}

View File

@@ -0,0 +1,79 @@
using System.IO;
using System.Xml.Serialization;
using KeePassLib.Serialization;
using OtpKeyProv;
using keepass2android.Io;
using System.Xml;
using Android.Content;
namespace keepass2android.addons.OtpKeyProv
{
/// <summary>
/// Class which provides caching for OtpInfo-files. This is an extension to CachingFileStorage required to handle conflicts directly when loading.
/// </summary>
class OtpAuxCachingFileStorage: CachingFileStorage
{
private readonly IOtpAuxCacheSupervisor _cacheSupervisor;
internal interface IOtpAuxCacheSupervisor: ICacheSupervisor
{
/// <summary>
/// called when there was a conflict which was resolved by using the remote file.
/// </summary>
void ResolvedCacheConflictByUsingRemote(IOConnectionInfo ioc);
/// <summary>
/// called when there was a conflict which was resolved by using the local file.
/// </summary>
void ResolvedCacheConflictByUsingLocal(IOConnectionInfo ioc);
}
public OtpAuxCachingFileStorage(IFileStorage cachedStorage, Context context, IOtpAuxCacheSupervisor cacheSupervisor)
: base(cachedStorage, context, cacheSupervisor)
{
_cacheSupervisor = cacheSupervisor;
}
protected override Stream OpenFileForReadWithConflict(IOConnectionInfo ioc, string cachedFilePath)
{
OtpInfo remoteOtpInfo, localOtpInfo;
//load both files
XmlSerializer xs = new XmlSerializer(typeof(OtpInfo));
using (var cacheStream = File.OpenRead(cachedFilePath))
{
XmlReaderSettings settings = new XmlReaderSettings() { XmlResolver = null, DtdProcessing = DtdProcessing.Ignore};
var reader = XmlReader.Create(cacheStream, settings);
localOtpInfo = (OtpInfo) xs.Deserialize(reader);
}
using (Stream remoteStream = _cachedStorage.OpenFileForRead(ioc))
{
remoteOtpInfo = (OtpInfo)xs.Deserialize(remoteStream);
}
//see which OtpInfo has the bigger Counter value and use this one:
if (localOtpInfo.Counter > remoteOtpInfo.Counter)
{
//overwrite the remote file
UpdateRemoteFile(File.OpenRead(cachedFilePath),
ioc,
App.Kp2a.GetBooleanPreference(PreferenceKey.UseFileTransactions),
GetBaseVersionHash(ioc)
);
_cacheSupervisor.ResolvedCacheConflictByUsingRemote(ioc);
}
else
{
//overwrite the local file:
UpdateCacheFromRemote(ioc, cachedFilePath);
_cacheSupervisor.ResolvedCacheConflictByUsingLocal(ioc);
}
//now return the local file in any way:
return File.OpenRead(cachedFilePath);
}
}
}

View File

@@ -0,0 +1,344 @@
/*
This file was modified my Philipp Crocoll, 2013. Based on:
OtpKeyProv Plugin
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Security.Cryptography;
using System.Diagnostics;
using KeePassLib.Cryptography;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using keepass2android;
namespace OtpKeyProv
{
public sealed class OtpInfo
{
private string m_strType = string.Empty;
public string Type
{
get { return m_strType; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strType = value;
}
}
private string m_strVersion = string.Empty;
public string Version
{
get { return m_strVersion; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strVersion = value;
}
}
private string m_strGen = string.Empty;
public string Generator
{
get { return m_strGen; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strGen = value;
}
}
private byte[] m_pbSecret = null;
[XmlIgnore]
public byte[] Secret
{
get { return m_pbSecret; }
set { m_pbSecret = value; }
}
private string m_strEncSecret = string.Empty;
[DefaultValue("")]
public string EncryptedSecret // Deprecated, < v2.0
{
get { return m_strEncSecret; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strEncSecret = value;
}
}
private List<OtpEncryptedData> m_lSecrets = new List<OtpEncryptedData>();
[XmlArrayItem("EncryptedData")]
public List<OtpEncryptedData> EncryptedSecrets
{
get { return m_lSecrets; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_lSecrets = value;
}
}
private string m_strEncIV = string.Empty;
[DefaultValue("")]
public string EncryptionIV // Deprecated, < v2.0
{
get { return m_strEncIV; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strEncIV = value;
}
}
private string m_strTrfKey = string.Empty;
[DefaultValue("")]
public string TransformationKey // Deprecated, < v2.0
{
get { return m_strTrfKey; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strTrfKey = value;
}
}
private const ulong DefaultTrfRounds = 12000;
private ulong m_uTrfRounds = DefaultTrfRounds;
[DefaultValue(typeof(ulong), "12000")]
public ulong TransformationRounds // Deprecated, < v2.0
{
get { return m_uTrfRounds; }
set { m_uTrfRounds = value; }
}
private ulong m_uCounter = 0;
public ulong Counter
{
get { return m_uCounter; }
set { m_uCounter = value; }
}
private uint m_uOtpLength = 8;
public uint OtpLength
{
get { return m_uOtpLength; }
set { m_uOtpLength = value; }
}
private uint m_uOtpsReq = 4;
public uint OtpsRequired
{
get { return m_uOtpsReq; }
set { m_uOtpsReq = value; }
}
private uint m_uLookAhead = 0;
public uint LookAheadCount
{
get { return m_uLookAhead; }
set { m_uLookAhead = value; }
}
public static OtpInfo Load(IOConnectionInfo ioc)
{
Stream sIn = null;
try
{
sIn = App.Kp2a.GetOtpAuxFileStorage(ioc).OpenFileForRead(ioc);
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
XmlReaderSettings settings = new XmlReaderSettings() { XmlResolver = null, DtdProcessing = DtdProcessing.Ignore };
var reader = XmlReader.Create(sIn, settings);
return (OtpInfo) xs.Deserialize(reader);
}
catch (Exception e)
{
Kp2aLog.LogUnexpectedError(e);
}
finally
{
if(sIn != null) sIn.Close();
}
return null;
}
public static bool Save(IOConnectionInfo ioc, OtpInfo otpInfo)
{
Stream sOut = null;
try
{
using (var trans = App.Kp2a.GetOtpAuxFileStorage(ioc)
.OpenWriteTransaction(ioc, App.Kp2a.GetBooleanPreference(PreferenceKey.UseFileTransactions)))
{
var stream = trans.OpenFile();
WriteToStream(otpInfo, stream);
trans.CommitWrite();
}
return true;
}
catch(Exception) { Debug.Assert(false); }
finally
{
if(sOut != null) sOut.Close();
}
return false;
}
public static void WriteToStream(OtpInfo otpInfo, Stream stream)
{
var xws = XmlWriterSettings();
XmlWriter xw = XmlWriter.Create(stream, xws);
XmlSerializer xs = new XmlSerializer(typeof (OtpInfo));
xs.Serialize(xw, otpInfo);
xw.Close();
}
public static XmlWriterSettings XmlWriterSettings()
{
XmlWriterSettings xws = new XmlWriterSettings
{
CloseOutput = true,
Encoding = StrUtil.Utf8,
Indent = true,
IndentChars = "\t"
};
return xws;
}
public void EncryptSecret()
{
if(m_pbSecret == null) throw new InvalidOperationException();
string[] vOtps = new string[m_uOtpsReq + m_uLookAhead];
ulong uCounter = m_uCounter;
for(int i = 0; i < vOtps.Length; ++i)
{
vOtps[i] = HmacOtp.Generate(m_pbSecret, uCounter,
m_uOtpLength, false, -1);
++uCounter;
}
m_strEncSecret = string.Empty;
m_strEncIV = string.Empty;
m_strTrfKey = string.Empty;
m_uTrfRounds = DefaultTrfRounds;
m_lSecrets.Clear();
for(int i = 0; i <= (int)m_uLookAhead; ++i)
m_lSecrets.Add(OtpUtil.EncryptSecret(m_pbSecret, vOtps, i,
(int)m_uOtpsReq));
}
}
public sealed class OtpEncryptedData
{
private string m_strCipherText = string.Empty;
[DefaultValue("")]
public string CipherText
{
get { return m_strCipherText; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strCipherText = value;
}
}
private string m_strIV = string.Empty;
[DefaultValue("")]
public string IV
{
get { return m_strIV; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strIV = value;
}
}
private string m_strTrfKey = string.Empty;
[DefaultValue("")]
public string TransformationKey
{
get { return m_strTrfKey; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strTrfKey = value;
}
}
private ulong m_uTrfRounds = 10000;
public ulong TransformationRounds
{
get { return m_uTrfRounds; }
set { m_uTrfRounds = value; }
}
private string m_strPlainHash = string.Empty;
[DefaultValue("")]
public string PlainTextHash
{
get { return m_strPlainHash; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strPlainHash = value;
}
}
private string m_strPlainHashTrfKey = string.Empty;
[DefaultValue("")]
public string PlainTextHashTransformationKey
{
get { return m_strPlainHashTrfKey; }
set
{
if(value == null) throw new ArgumentNullException("value");
m_strPlainHashTrfKey = value;
}
}
private ulong m_uHashTrfRounds = 10000;
public ulong PlainTextHashTransformationRounds
{
get { return m_uHashTrfRounds; }
set { m_uHashTrfRounds = value; }
}
}
}

View File

@@ -0,0 +1,174 @@
/*
OtpKeyProv Plugin
Copyright (C) 2011-2012 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Keys;
using KeePassLib.Utility;
namespace OtpKeyProv
{
public enum OtpDataFmt
{
Hex = 0,
Base64 = 1,
Base32 = 4,
Utf8 = 2,
Dec = 3
}
public static class OtpUtil
{
public static byte[] KeyFromOtps(string[] vOtps, int iOtpsOffset,
int iOtpsCount, byte[] pbTrfKey32, ulong uTrfRounds)
{
StringBuilder sb = new StringBuilder();
for(int i = iOtpsOffset; i < (iOtpsOffset + iOtpsCount); ++i)
{
if(sb.Length > 0) sb.Append(':');
sb.Append(vOtps[i].Trim());
}
string strKey = sb.ToString();
byte[] pb = StrUtil.Utf8.GetBytes(strKey);
if(pb.Length == 0) return null;
byte[] pbKey = HashAndTransform(pb, pbTrfKey32, uTrfRounds);
if(pbKey == null) throw new InvalidOperationException();
return pbKey;
}
public static string EncryptData(byte[] pbData, byte[] pbKey32,
byte[] pbIV16)
{
byte[] pbIV8 = new byte[8];
Array.Copy(pbIV16, 0, pbIV8, 0, 8);
byte[] pbEnc = new byte[pbData.Length];
Array.Copy(pbData, pbEnc, pbData.Length);
Salsa20Cipher enc = new Salsa20Cipher(pbKey32, pbIV8);
enc.Encrypt(pbEnc, 0, pbEnc.Length);
return ("s20://" + Convert.ToBase64String(pbEnc,
Base64FormattingOptions.None));
}
public static byte[] DecryptData(string strData, byte[] pbKey32,
byte[] pbIV16)
{
if(!strData.StartsWith("s20://")) return null;
string strEnc = strData.Substring(6);
byte[] pb = Convert.FromBase64String(strEnc);
byte[] pbIV8 = new byte[8];
Array.Copy(pbIV16, 0, pbIV8, 0, 8);
Salsa20Cipher dec = new Salsa20Cipher(pbKey32, pbIV8);
dec.Encrypt(pb, 0, pb.Length);
return pb;
}
private static byte[] HashAndTransform(byte[] pbData, byte[] pbTrfKey32,
ulong uTrfRounds)
{
SHA256Managed sha256 = new SHA256Managed();
byte[] pbHash = sha256.ComputeHash(pbData);
sha256.Clear();
if(!AesKdf.TransformKeyManaged(pbHash, pbTrfKey32, uTrfRounds))
return null;
sha256 = new SHA256Managed();
pbHash = sha256.ComputeHash(pbHash);
sha256.Clear();
return pbHash;
}
public static OtpEncryptedData EncryptSecret(byte[] pbSecret, string[] vOtps,
int iOtpsOffset, int iOtpsCount)
{
OtpEncryptedData d = new OtpEncryptedData();
CryptoRandom r = CryptoRandom.Instance;
byte[] pbIV16 = r.GetRandomBytes(16);
d.IV = Convert.ToBase64String(pbIV16, Base64FormattingOptions.None);
byte[] pbTrfKey32 = r.GetRandomBytes(32);
d.TransformationKey = Convert.ToBase64String(pbTrfKey32, Base64FormattingOptions.None);
byte[] pbKey32 = OtpUtil.KeyFromOtps(vOtps, iOtpsOffset, iOtpsCount,
pbTrfKey32, d.TransformationRounds);
d.CipherText = OtpUtil.EncryptData(pbSecret, pbKey32, pbIV16);
byte[] pbHashTrfKey32 = r.GetRandomBytes(32);
d.PlainTextHashTransformationKey = Convert.ToBase64String(pbHashTrfKey32,
Base64FormattingOptions.None);
byte[] pbHash = HashAndTransform(pbSecret, pbHashTrfKey32,
d.PlainTextHashTransformationRounds);
d.PlainTextHash = Convert.ToBase64String(pbHash, Base64FormattingOptions.None);
return d;
}
public static byte[] DecryptSecret(OtpEncryptedData d, string[] vOtps,
int iOtpsOffset, int iOtpsCount)
{
try { return DecryptSecretPriv(d, vOtps, iOtpsOffset, iOtpsCount); }
catch(Exception) { Debug.Assert(false); }
return null;
}
private static byte[] DecryptSecretPriv(OtpEncryptedData d, string[] vOtps,
int iOtpsOffset, int iOtpsCount)
{
if(d == null) throw new ArgumentNullException("d");
byte[] pbTrfKey32 = Convert.FromBase64String(d.TransformationKey);
byte[] pbKey32 = OtpUtil.KeyFromOtps(vOtps, iOtpsOffset, iOtpsCount,
pbTrfKey32, d.TransformationRounds);
byte[] pbIV = Convert.FromBase64String(d.IV);
byte[] pbSecret = OtpUtil.DecryptData(d.CipherText, pbKey32, pbIV);
byte[] pbHashTrfKey32 = Convert.FromBase64String(d.PlainTextHashTransformationKey);
byte[] pbHash = HashAndTransform(pbSecret, pbHashTrfKey32,
d.PlainTextHashTransformationRounds);
if(!MemUtil.ArraysEqual(pbHash, Convert.FromBase64String(d.PlainTextHash)))
return null;
return pbSecret;
}
}
}