S2k
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using System;
using System.IO;
namespace Org.BouncyCastle.Bcpg
{
public class S2k : BcpgObject
{
public sealed class Argon2Params
{
private readonly byte[] m_salt;
private readonly int m_passes;
private readonly int m_parallelism;
private readonly int m_memorySizeExponent;
public int MemorySizeExponent => m_memorySizeExponent;
public int Parallelism => m_parallelism;
public int Passes => m_passes;
public static SecureRandom DefaultSecureRandom()
{
return CryptoServicesRegistrar.GetSecureRandom();
}
public static Argon2Params MemoryConstrainedParameters(SecureRandom secureRandom)
{
return new Argon2Params(3, 4, 16, secureRandom);
}
public static Argon2Params RecommendedParameters(SecureRandom secureRandom)
{
return new Argon2Params(1, 4, 21, secureRandom);
}
public Argon2Params(int passes, int parallelism, int memorySizeExponent, SecureRandom secureRandom)
: this(GenerateSalt(secureRandom), passes, parallelism, memorySizeExponent)
{
}
public Argon2Params(byte[] salt, int passes, int parallelism, int memorySizeExponent)
{
if (salt.Length != 16)
throw new ArgumentException("Argon2 uses 16 bytes of salt", "salt");
if (passes < 1 || passes > 255)
throw new ArgumentOutOfRangeException("passes", passes, "MUST be an integer value from 1 to 255.");
if (parallelism < 1 || parallelism > 255)
throw new ArgumentOutOfRangeException("parallelism", parallelism, "MUST be an integer value from 1 to 255.");
int num = 35 - Integers.NumberOfLeadingZeros(parallelism - 1);
int num2 = 30;
if (memorySizeExponent < num || memorySizeExponent > num2)
throw new ArgumentOutOfRangeException("memorySizeExponent", memorySizeExponent, "MUST be an integer value from 3 + bitlen(parallelism - 1) to 30.");
m_salt = Arrays.Clone(salt);
m_passes = passes;
m_parallelism = parallelism;
m_memorySizeExponent = memorySizeExponent;
}
public byte[] GetSalt()
{
return Arrays.Clone(m_salt);
}
private static byte[] GenerateSalt(SecureRandom secureRandom)
{
return SecureRandom.GetNextBytes(secureRandom, 16);
}
}
private const int ExpBias = 6;
public const int Simple = 0;
public const int Salted = 1;
public const int SaltedAndIterated = 3;
public const int Argon2 = 4;
public const int GnuDummyS2K = 101;
public const int GnuProtectionModeNoPrivateKey = 1;
public const int GnuProtectionModeDivertToCard = 2;
public const int GnuProtectionModeInternal = 3;
internal int m_type;
internal HashAlgorithmTag m_algorithm;
internal byte[] m_iv;
internal int m_itCount = -1;
internal int m_protectionMode = -1;
internal Argon2Params m_argon2Config;
public virtual int Type => m_type;
public virtual HashAlgorithmTag HashAlgorithm => m_algorithm;
public virtual long IterationCount {
get {
if (m_itCount < 256)
return DeriveIterationCount(m_itCount);
return m_itCount;
}
}
public virtual int ProtectionMode => m_protectionMode;
public virtual Argon2Params Argon2Config => m_argon2Config;
public static S2k GenerateSalted(SecureRandom secureRandom, HashAlgorithmTag hashAlgorithm)
{
byte[] iv = GenerateIV(secureRandom);
return new S2k(hashAlgorithm, iv);
}
public static S2k GenerateSaltedAndIterated(SecureRandom secureRandom, HashAlgorithmTag hashAlgorithm, int itCount)
{
byte[] iv = GenerateIV(secureRandom);
return new S2k(hashAlgorithm, iv, itCount);
}
internal S2k(Stream inStr)
{
m_type = StreamUtilities.RequireByte(inStr);
switch (m_type) {
case 0:
m_algorithm = (HashAlgorithmTag)StreamUtilities.RequireByte(inStr);
break;
case 1:
m_algorithm = (HashAlgorithmTag)StreamUtilities.RequireByte(inStr);
m_iv = StreamUtilities.RequireBytes(inStr, 8);
break;
case 3:
m_algorithm = (HashAlgorithmTag)StreamUtilities.RequireByte(inStr);
m_iv = StreamUtilities.RequireBytes(inStr, 8);
m_itCount = StreamUtilities.RequireByte(inStr);
break;
case 4: {
byte[] salt = StreamUtilities.RequireBytes(inStr, 16);
byte passes = StreamUtilities.RequireByte(inStr);
byte parallelism = StreamUtilities.RequireByte(inStr);
byte memorySizeExponent = StreamUtilities.RequireByte(inStr);
m_argon2Config = new Argon2Params(salt, passes, parallelism, memorySizeExponent);
break;
}
case 101: {
m_algorithm = (HashAlgorithmTag)StreamUtilities.RequireByte(inStr);
uint num = StreamUtilities.RequireUInt32BE(inStr);
m_protectionMode = (byte)num;
break;
}
default:
throw new UnsupportedPacketVersionException("Invalid S2K type: " + m_type.ToString());
}
}
public S2k(HashAlgorithmTag algorithm)
{
m_type = 0;
m_algorithm = algorithm;
}
public S2k(HashAlgorithmTag algorithm, byte[] iv)
{
m_type = 1;
m_algorithm = algorithm;
m_iv = iv;
}
public S2k(HashAlgorithmTag algorithm, byte[] iv, int itCount)
{
m_type = 3;
m_algorithm = algorithm;
m_iv = iv;
m_itCount = itCount;
}
public S2k(Argon2Params argon2Config)
{
m_type = 4;
if (argon2Config == null)
throw new ArgumentNullException("argon2Config");
m_argon2Config = argon2Config;
}
public virtual byte[] GetIV()
{
return Arrays.Clone(m_iv);
}
public override void Encode(BcpgOutputStream bcpgOut)
{
bcpgOut.WriteByte((byte)m_type);
switch (m_type) {
case 0:
bcpgOut.WriteByte((byte)m_algorithm);
break;
case 1:
bcpgOut.WriteByte((byte)m_algorithm);
bcpgOut.Write(m_iv);
break;
case 3:
bcpgOut.WriteByte((byte)m_algorithm);
bcpgOut.Write(m_iv);
WriteCheckedByte(bcpgOut, m_itCount, "Iteration count");
break;
case 4:
bcpgOut.Write(m_argon2Config.GetSalt());
WriteCheckedByte(bcpgOut, m_argon2Config.Passes, "Passes");
WriteCheckedByte(bcpgOut, m_argon2Config.Parallelism, "Parallelism");
WriteCheckedByte(bcpgOut, m_argon2Config.MemorySizeExponent, "Memory size exponent");
break;
case 101: {
byte[] obj = new byte[5] {
0,
71,
78,
85,
0
};
obj[0] = (byte)m_algorithm;
obj[4] = (byte)m_protectionMode;
bcpgOut.Write(obj);
break;
}
default:
throw new InvalidOperationException("Unknown S2K type " + m_type.ToString());
}
}
private static long DeriveIterationCount(int itCount)
{
return 16 + (itCount & 15) << (itCount >> 4) + 6;
}
private static byte[] GenerateIV(SecureRandom secureRandom)
{
return SecureRandom.GetNextBytes(secureRandom, 8);
}
private static void WriteCheckedByte(BcpgOutputStream bcpgOut, int val, string valName)
{
if ((val & 4294967040) != 0)
throw new InvalidOperationException(valName + " not encodable");
bcpgOut.WriteByte((byte)val);
}
}
}