keepass2android/src/keepass2android/addons/OtpKeyProv/OtpUtil.cs

175 lines
5.0 KiB
C#

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