fix dh (by plagiarism), refactor pcapparser

This commit is contained in:
raphaeIl
2025-01-16 14:13:15 -05:00
parent fe508d14d1
commit f269800704
26 changed files with 92455 additions and 42630 deletions

View File

@@ -1,6 +1,8 @@
using System;
using System.Security.Cryptography;
using System.Text;
using Mono.Math;
using Mono.Security.Cryptography;
using NSec.Cryptography;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
@@ -24,7 +26,7 @@ namespace Novaria.Common.Crypto
public static readonly int IVSize = 16;
public static byte[] PS_REQUEST_NONCE = new byte[12]; // hardcoded nonce, probably makes it easier for server idk? could also just randomly generate but me lazy
public static byte[] PS_PUBLIC_KEY { get => DiffieHellman.Instance.ServerPublicKey.ToByteArray(); }
//public static byte[] PS_PUBLIC_KEY { get => DiffieHellman.Instance.ServerPublicKey.GetBytes(); }
public static string TOKEN = "seggs"; // hardcoded for now
public static readonly byte[] DEFAULT_SERVERLIST_KEY = new byte[] { 74, 72, 42, 67, 80, 51, 50, 57, 89, 120, 111, 103, 81, 74, 69, 120 };
@@ -34,8 +36,9 @@ namespace Novaria.Common.Crypto
public static readonly byte[] IKE_KEY = Encoding.ASCII.GetBytes("3LS9&oYdsp^5wi8&ZxC#c7MZg73hbEDw");
private static DiffieHellmanManaged SendKey;
private static BigInteger p = BigInteger.Parse("1552518092300708935130918131258481755631334049434514313202351194902966239949102107258669453876591642442910007680288864229150803718918046342632727613031282983744380820890196288509170691316593175367469551763119843371637221007210577919");
private static GcmBlockCipher cipher;
private static AesEngine engine;
public delegate void AeadEncryptHandler(Span<byte> result, ReadOnlySpan<byte> key, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> data, bool needAssociatedData);
@@ -57,6 +60,25 @@ namespace Novaria.Common.Crypto
PS_REQUEST_NONCE[11] = 42;
InitAeadTool();
InitDH();
}
private static int InitDH()
{
int nRandom = 1;
byte[] bytes = BitConverter.GetBytes(2);
if (BitConverter.IsLittleEndian)
{
Array.Reverse<byte>(bytes);
}
byte[] bytes2 = BitConverter.GetBytes(nRandom);
if (BitConverter.IsLittleEndian)
{
Array.Reverse<byte>(bytes2);
}
SendKey = new DiffieHellmanManaged(p.GetBytes(), bytes, bytes2);
return nRandom;
}
public static void InitAeadTool()
@@ -84,6 +106,24 @@ namespace Novaria.Common.Crypto
AeadTool.cipher = new GcmBlockCipher(AeadTool.engine);
}
public static void SetAeadKey(byte[] pubKey)
{
byte[] array = SendKey.DecryptKeyExchange(pubKey);
int num = array.Length;
if (num > 32)
{
num = 32;
}
key3 = new byte[32];
Buffer.BlockCopy(array, 0, key3, 0, num);
}
public static byte[] GetPubKey()
{
return new BigInteger(SendKey.CreateKeyExchange()).GetBytes();
}
private static void Encrypt_AESGCM_BouncyCastle(Span<byte> result, ReadOnlySpan<byte> key, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> data, bool needAssociatedData)
{
if (!needAssociatedData)

View File

@@ -1,11 +1,11 @@
using Novaria.Common.Util;
using System.Numerics;
using Mono.Math;
using Novaria.Common.Util;
namespace Novaria.Common.Crypto
{
public class DiffieHellman : Singleton<DiffieHellman>
{
private BigInteger p = BigInteger.Parse("1552518092300708935130918131258481755631334049434514313202351194902966239949102107258669453876591642442910007680288864229150803718918046342632727613031282983744380820890196288509170691316593175367469551763119843371637221007210577919");
private System.Numerics.BigInteger old_p = System.Numerics.BigInteger.Parse("1552518092300708935130918131258481755631334049434514313202351194902966239949102107258669453876591642442910007680288864229150803718918046342632727613031282983744380820890196288509170691316593175367469551763119843371637221007210577919");
private BigInteger g = 2;
@@ -15,37 +15,49 @@ namespace Novaria.Common.Crypto
public DiffieHellman()
{
Console.WriteLine(spriv);
//Console.WriteLine(spriv);
//g** Spriv mod p
ServerPublicKey = BigInteger.ModPow(g, spriv, p);
//ServerPublicKey = this.g.ModPow(spriv, p);
}
public byte[] CalculateKey(byte[] clientPubKey) // server calculates key like this
{
BigInteger clientPubKeyInt = new BigInteger(clientPubKey.Reverse().ToArray());
//Cpub**Spriv mod p
Console.WriteLine(clientPubKeyInt.ToString());
var result = BigInteger.ModPow(clientPubKeyInt, spriv, p);
// old stuff
//System.Numerics.BigInteger clientPubKeyInt = new System.Numerics.BigInteger(clientPubKey.Reverse().ToArray());
//var result = System.Numerics.BigInteger.ModPow(clientPubKeyInt, new System.Numerics.BigInteger(new byte[] { 1, 2, 3, 4 }), old_p);
//Console.WriteLine(result);
//if (result < 0)
//{
// return result.ToByteArray(false, true)[..32];
// Console.WriteLine("THIS SHOULD CRASH");
//}
return result.ToByteArray(true, true)[..32];
}
// this is for manual pcap parsing use only, officalServerPubKey is in the first IKE response, client priv will be in frida
public byte[] CalculateKey(byte[] officalServerPubKey, byte[] officialClientPriv)
{
BigInteger officalServerPubKeyInt = new BigInteger(officalServerPubKey.Reverse().ToArray());
BigInteger officialClientPrivInt = new BigInteger(officialClientPriv.Reverse().ToArray());
//BigInteger bigInteger = new BigInteger(clientPubKey.Reverse().ToArray()).ModPow(this.spriv, this.p);
BigInteger result = BigInteger.ModPow(officalServerPubKeyInt, officialClientPrivInt, p);
//return bigInteger.GetBytes()[..32];
return null;
//BigInteger clientPubKeyInt = new BigInteger(clientPubKey.Reverse().ToArray());
return result.ToByteArray(true, true)[..32];
////Cpub**Spriv mod p
//Console.WriteLine(clientPubKeyInt.ToString());
//var result = BigInteger.ModPow(clientPubKeyInt, spriv, p);
//Console.WriteLine("------------test-------------------");
////Utils.PrintByteArray(BigInteger.Abs());
////if (result < 0)
////{
//// return result.ToByteArray(false, true)[..32];
////}
//Utils.PrintByteArray(result.ToByteArray(true, true));
//Console.WriteLine("----------------------------------");
//return result.GetBytes()[..32];
//return result.ToByteArray(true, true)[..32];
}
}
}

View File

@@ -0,0 +1,55 @@
//
// Mono.Math.Prime.Generator.NextPrimeFinder.cs - Prime Generator
//
// Authors:
// Ben Maurer
//
// Copyright (c) 2003 Ben Maurer. All rights reserved
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Math.Prime.Generator {
/// <summary>
/// Finds the next prime after a given number.
/// </summary>
#if INSIDE_CORLIB
internal
#else
public
#endif
class NextPrimeFinder : SequentialSearchPrimeGeneratorBase {
protected override BigInteger GenerateSearchBase (int bits, object Context)
{
if (Context == null)
throw new ArgumentNullException ("Context");
BigInteger ret = new BigInteger ((BigInteger)Context);
ret.SetBit (0);
return ret;
}
}
}

View File

@@ -0,0 +1,75 @@
//
// Mono.Math.Prime.Generator.PrimeGeneratorBase.cs - Abstract Prime Generator
//
// Authors:
// Ben Maurer
//
// Copyright (c) 2003 Ben Maurer. All rights reserved
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Math.Prime.Generator {
#if INSIDE_CORLIB
internal
#else
public
#endif
abstract class PrimeGeneratorBase {
public virtual ConfidenceFactor Confidence {
get {
#if DEBUG
return ConfidenceFactor.ExtraLow;
#else
return ConfidenceFactor.Medium;
#endif
}
}
public virtual Prime.PrimalityTest PrimalityTest {
get {
return new Prime.PrimalityTest (PrimalityTests.RabinMillerTest);
}
}
public virtual int TrialDivisionBounds {
get { return 4000; }
}
/// <summary>
/// Performs primality tests on bi, assumes trial division has been done.
/// </summary>
/// <param name="bi">A BigInteger that has been subjected to and passed trial division</param>
/// <returns>False if bi is composite, true if it may be prime.</returns>
/// <remarks>The speed of this method is dependent on Confidence</remarks>
protected bool PostTrialDivisionTests (BigInteger bi)
{
return PrimalityTest (bi, this.Confidence);
}
public abstract BigInteger GenerateNewPrime (int bits);
}
}

View File

@@ -0,0 +1,120 @@
//
// Mono.Math.Prime.Generator.SequentialSearchPrimeGeneratorBase.cs - Prime Generator
//
// Authors:
// Ben Maurer
//
// Copyright (c) 2003 Ben Maurer. All rights reserved
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
namespace Mono.Math.Prime.Generator {
#if INSIDE_CORLIB
internal
#else
public
#endif
class SequentialSearchPrimeGeneratorBase : PrimeGeneratorBase {
protected virtual BigInteger GenerateSearchBase (int bits, object context)
{
BigInteger ret = BigInteger.GenerateRandom (bits);
ret.SetBit (0);
return ret;
}
public override BigInteger GenerateNewPrime (int bits)
{
return GenerateNewPrime (bits, null);
}
public virtual BigInteger GenerateNewPrime (int bits, object context)
{
//
// STEP 1. Find a place to do a sequential search
//
BigInteger curVal = GenerateSearchBase (bits, context);
const uint primeProd1 = 3u* 5u * 7u * 11u * 13u * 17u * 19u * 23u * 29u;
uint pMod1 = curVal % primeProd1;
int DivisionBound = TrialDivisionBounds;
uint[] SmallPrimes = BigInteger.smallPrimes;
//
// STEP 2. Search for primes
//
while (true) {
//
// STEP 2.1 Sieve out numbers divisible by the first 9 primes
//
if (pMod1 % 3 == 0) goto biNotPrime;
if (pMod1 % 5 == 0) goto biNotPrime;
if (pMod1 % 7 == 0) goto biNotPrime;
if (pMod1 % 11 == 0) goto biNotPrime;
if (pMod1 % 13 == 0) goto biNotPrime;
if (pMod1 % 17 == 0) goto biNotPrime;
if (pMod1 % 19 == 0) goto biNotPrime;
if (pMod1 % 23 == 0) goto biNotPrime;
if (pMod1 % 29 == 0) goto biNotPrime;
//
// STEP 2.2 Sieve out all numbers divisible by the primes <= DivisionBound
//
for (int p = 10; p < SmallPrimes.Length && SmallPrimes [p] <= DivisionBound; p++) {
if (curVal % SmallPrimes [p] == 0)
goto biNotPrime;
}
//
// STEP 2.3 Is the potential prime acceptable?
//
if (!IsPrimeAcceptable (curVal, context))
goto biNotPrime;
//
// STEP 2.4 Filter out all primes that pass this step with a primality test
//
if (PrimalityTest (curVal, Confidence))
return curVal;
//
// STEP 2.4
//
biNotPrime:
pMod1 += 2;
if (pMod1 >= primeProd1)
pMod1 -= primeProd1;
curVal.Incr2 ();
}
}
protected virtual bool IsPrimeAcceptable (BigInteger bi, object context)
{
return true;
}
}
}

View File

@@ -0,0 +1,68 @@
//
// Mono.Math.Prime.ConfidenceFactor.cs - Confidence factor for prime generation
//
// Authors:
// Ben Maurer
//
// Copyright (c) 2003 Ben Maurer. All rights reserved
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Math.Prime {
/// <summary>
/// A factor of confidence.
/// </summary>
#if INSIDE_CORLIB
internal
#else
public
#endif
enum ConfidenceFactor {
/// <summary>
/// Only suitable for development use, probability of failure may be greater than 1/2^20.
/// </summary>
ExtraLow,
/// <summary>
/// Suitable only for transactions which do not require forward secrecy. Probability of failure about 1/2^40
/// </summary>
Low,
/// <summary>
/// Designed for production use. Probability of failure about 1/2^80.
/// </summary>
Medium,
/// <summary>
/// Suitable for sensitive data. Probability of failure about 1/2^160.
/// </summary>
High,
/// <summary>
/// Use only if you have lots of time! Probability of failure about 1/2^320.
/// </summary>
ExtraHigh,
/// <summary>
/// Only use methods which generate provable primes. Not yet implemented.
/// </summary>
Provable
}
}

View File

@@ -0,0 +1,218 @@
//
// Mono.Math.Prime.PrimalityTests.cs - Test for primality
//
// Authors:
// Ben Maurer
//
// Copyright (c) 2003 Ben Maurer. All rights reserved
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Math.Prime {
#if INSIDE_CORLIB
internal
#else
public
#endif
delegate bool PrimalityTest (BigInteger bi, ConfidenceFactor confidence);
#if INSIDE_CORLIB
internal
#else
public
#endif
sealed class PrimalityTests {
private PrimalityTests ()
{
}
#region SPP Test
private static int GetSPPRounds (BigInteger bi, ConfidenceFactor confidence)
{
int bc = bi.BitCount();
int Rounds;
// Data from HAC, 4.49
if (bc <= 100 ) Rounds = 27;
else if (bc <= 150 ) Rounds = 18;
else if (bc <= 200 ) Rounds = 15;
else if (bc <= 250 ) Rounds = 12;
else if (bc <= 300 ) Rounds = 9;
else if (bc <= 350 ) Rounds = 8;
else if (bc <= 400 ) Rounds = 7;
else if (bc <= 500 ) Rounds = 6;
else if (bc <= 600 ) Rounds = 5;
else if (bc <= 800 ) Rounds = 4;
else if (bc <= 1250) Rounds = 3;
else Rounds = 2;
switch (confidence) {
case ConfidenceFactor.ExtraLow:
Rounds >>= 2;
return Rounds != 0 ? Rounds : 1;
case ConfidenceFactor.Low:
Rounds >>= 1;
return Rounds != 0 ? Rounds : 1;
case ConfidenceFactor.Medium:
return Rounds;
case ConfidenceFactor.High:
return Rounds << 1;
case ConfidenceFactor.ExtraHigh:
return Rounds << 2;
case ConfidenceFactor.Provable:
throw new Exception ("The Rabin-Miller test can not be executed in a way such that its results are provable");
default:
throw new ArgumentOutOfRangeException ("confidence");
}
}
public static bool Test (BigInteger n, ConfidenceFactor confidence)
{
// Rabin-Miller fails with smaller primes (at least with our BigInteger code)
if (n.BitCount () < 33)
return SmallPrimeSppTest (n, confidence);
else
return RabinMillerTest (n, confidence);
}
/// <summary>
/// Probabilistic prime test based on Rabin-Miller's test
/// </summary>
/// <param name="n" type="BigInteger.BigInteger">
/// <para>
/// The number to test.
/// </para>
/// </param>
/// <param name="confidence" type="int">
/// <para>
/// The number of chosen bases. The test has at least a
/// 1/4^confidence chance of falsely returning True.
/// </para>
/// </param>
/// <returns>
/// <para>
/// True if "this" is a strong pseudoprime to randomly chosen bases.
/// </para>
/// <para>
/// False if "this" is definitely NOT prime.
/// </para>
/// </returns>
public static bool RabinMillerTest (BigInteger n, ConfidenceFactor confidence)
{
int bits = n.BitCount ();
int t = GetSPPRounds (bits, confidence);
// n - 1 == 2^s * r, r is odd
BigInteger n_minus_1 = n - 1;
int s = n_minus_1.LowestSetBit ();
BigInteger r = n_minus_1 >> s;
BigInteger.ModulusRing mr = new BigInteger.ModulusRing (n);
// Applying optimization from HAC section 4.50 (base == 2)
// not a really random base but an interesting (and speedy) one
BigInteger y = null;
// FIXME - optimization disable for small primes due to bug #81857
if (n.BitCount () > 100)
y = mr.Pow (2, r);
// still here ? start at round 1 (round 0 was a == 2)
for (int round = 0; round < t; round++) {
if ((round > 0) || (y == null)) {
BigInteger a = null;
// check for 2 <= a <= n - 2
// ...but we already did a == 2 previously as an optimization
do {
a = BigInteger.GenerateRandom (bits);
} while ((a <= 2) && (a >= n_minus_1));
y = mr.Pow (a, r);
}
if (y == 1)
continue;
for (int j = 0; ((j < s) && (y != n_minus_1)); j++) {
y = mr.Pow (y, 2);
if (y == 1)
return false;
}
if (y != n_minus_1)
return false;
}
return true;
}
public static bool SmallPrimeSppTest (BigInteger bi, ConfidenceFactor confidence)
{
int Rounds = GetSPPRounds (bi, confidence);
// calculate values of s and t
BigInteger p_sub1 = bi - 1;
int s = p_sub1.LowestSetBit ();
BigInteger t = p_sub1 >> s;
BigInteger.ModulusRing mr = new BigInteger.ModulusRing (bi);
for (int round = 0; round < Rounds; round++) {
BigInteger b = mr.Pow (BigInteger.smallPrimes [round], t);
if (b == 1) continue; // a^t mod p = 1
bool result = false;
for (int j = 0; j < s; j++) {
if (b == p_sub1) { // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1
result = true;
break;
}
b = (b * b) % bi;
}
if (result == false)
return false;
}
return true;
}
#endregion
// TODO: Implement the Lucus test
// TODO: Implement other new primality tests
// TODO: Implement primality proving
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,805 @@
//
// CryptoConvert.cs - Crypto Convertion Routines
//
// Author:
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
namespace Mono.Security.Cryptography {
#if INSIDE_CORLIB
internal
#else
public
#endif
sealed class CryptoConvert {
private CryptoConvert ()
{
}
static private int ToInt32LE (byte [] bytes, int offset)
{
return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
}
static private uint ToUInt32LE (byte [] bytes, int offset)
{
return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
}
static private byte [] GetBytesLE (int val)
{
return new byte [] {
(byte) (val & 0xff),
(byte) ((val >> 8) & 0xff),
(byte) ((val >> 16) & 0xff),
(byte) ((val >> 24) & 0xff)
};
}
static private byte[] Trim (byte[] array)
{
for (int i=0; i < array.Length; i++) {
if (array [i] != 0x00) {
byte[] result = new byte [array.Length - i];
Buffer.BlockCopy (array, i, result, 0, result.Length);
return result;
}
}
return null;
}
#if INSIDE_CORLIB
static internal bool TryImportCapiPrivateKeyBlob (byte[] blob, int offset)
{
try {
var rsap = GetParametersFromCapiPrivateKeyBlob (blob, offset);
// Since we are only checking whether this throws an exception and
// not actually returning the `RSA` object, we can use `RSAManaged`
// here because that's what the `RSACryptoServiceProvider` implementation
// does internally.
var rsa = new RSAManaged ();
rsa.ImportParameters (rsap);
return true;
} catch (CryptographicException) {
return false;
}
}
#endif
// convert the key from PRIVATEKEYBLOB to RSA
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
// e.g. SNK files, PVK files
static public RSA FromCapiPrivateKeyBlob (byte[] blob)
{
return FromCapiPrivateKeyBlob (blob, 0);
}
static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset)
{
RSAParameters rsap = GetParametersFromCapiPrivateKeyBlob (blob, offset);
#if INSIDE_CORLIB && MOBILE
RSA rsa = RSA.Create ();
rsa.ImportParameters (rsap);
#else
RSA rsa = null;
try {
rsa = RSA.Create ();
rsa.ImportParameters (rsap);
}
catch (CryptographicException ce) {
// this may cause problem when this code is run under
// the SYSTEM identity on Windows (e.g. ASP.NET). See
// http://bugzilla.ximian.com/show_bug.cgi?id=77559
try {
CspParameters csp = new CspParameters ();
csp.Flags = CspProviderFlags.UseMachineKeyStore;
rsa = new RSACryptoServiceProvider (csp);
rsa.ImportParameters (rsap);
}
catch {
// rethrow original, not the later, exception if this fails
throw ce;
}
}
#endif
return rsa;
}
static RSAParameters GetParametersFromCapiPrivateKeyBlob (byte[] blob, int offset)
{
if (blob == null)
throw new ArgumentNullException ("blob");
if (offset >= blob.Length)
throw new ArgumentException ("blob is too small.");
RSAParameters rsap = new RSAParameters ();
try {
if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
(blob [offset+1] != 0x02) || // Version (0x02)
(blob [offset+2] != 0x00) || // Reserved (word)
(blob [offset+3] != 0x00) ||
(ToUInt32LE (blob, offset+8) != 0x32415352)) // DWORD magic = RSA2
throw new CryptographicException ("Invalid blob header");
// ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
// int algId = ToInt32LE (blob, offset+4);
// DWORD bitlen
int bitLen = ToInt32LE (blob, offset+12);
// DWORD public exponent
byte[] exp = new byte [4];
Buffer.BlockCopy (blob, offset+16, exp, 0, 4);
Array.Reverse (exp);
rsap.Exponent = Trim (exp);
int pos = offset+20;
// BYTE modulus[rsapubkey.bitlen/8];
int byteLen = (bitLen >> 3);
rsap.Modulus = new byte [byteLen];
Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
Array.Reverse (rsap.Modulus);
pos += byteLen;
// BYTE prime1[rsapubkey.bitlen/16];
int byteHalfLen = (byteLen >> 1);
rsap.P = new byte [byteHalfLen];
Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen);
Array.Reverse (rsap.P);
pos += byteHalfLen;
// BYTE prime2[rsapubkey.bitlen/16];
rsap.Q = new byte [byteHalfLen];
Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen);
Array.Reverse (rsap.Q);
pos += byteHalfLen;
// BYTE exponent1[rsapubkey.bitlen/16];
rsap.DP = new byte [byteHalfLen];
Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen);
Array.Reverse (rsap.DP);
pos += byteHalfLen;
// BYTE exponent2[rsapubkey.bitlen/16];
rsap.DQ = new byte [byteHalfLen];
Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen);
Array.Reverse (rsap.DQ);
pos += byteHalfLen;
// BYTE coefficient[rsapubkey.bitlen/16];
rsap.InverseQ = new byte [byteHalfLen];
Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen);
Array.Reverse (rsap.InverseQ);
pos += byteHalfLen;
// ok, this is hackish but CryptoAPI support it so...
// note: only works because CRT is used by default
// http://bugzilla.ximian.com/show_bug.cgi?id=57941
rsap.D = new byte [byteLen]; // must be allocated
if (pos + byteLen + offset <= blob.Length) {
// BYTE privateExponent[rsapubkey.bitlen/8];
Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen);
Array.Reverse (rsap.D);
}
return rsap;
} catch (Exception e) {
throw new CryptographicException ("Invalid blob.", e);
}
}
static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob)
{
return FromCapiPrivateKeyBlobDSA (blob, 0);
}
static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset)
{
if (blob == null)
throw new ArgumentNullException ("blob");
if (offset >= blob.Length)
throw new ArgumentException ("blob is too small.");
DSAParameters dsap = new DSAParameters ();
try {
if ((blob [offset] != 0x07) || // PRIVATEKEYBLOB (0x07)
(blob [offset + 1] != 0x02) || // Version (0x02)
(blob [offset + 2] != 0x00) || // Reserved (word)
(blob [offset + 3] != 0x00) ||
(ToUInt32LE (blob, offset + 8) != 0x32535344)) // DWORD magic
throw new CryptographicException ("Invalid blob header");
int bitlen = ToInt32LE (blob, offset + 12);
int bytelen = bitlen >> 3;
int pos = offset + 16;
dsap.P = new byte [bytelen];
Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen);
Array.Reverse (dsap.P);
pos += bytelen;
dsap.Q = new byte [20];
Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20);
Array.Reverse (dsap.Q);
pos += 20;
dsap.G = new byte [bytelen];
Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen);
Array.Reverse (dsap.G);
pos += bytelen;
dsap.X = new byte [20];
Buffer.BlockCopy (blob, pos, dsap.X, 0, 20);
Array.Reverse (dsap.X);
pos += 20;
dsap.Counter = ToInt32LE (blob, pos);
pos += 4;
dsap.Seed = new byte [20];
Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20);
Array.Reverse (dsap.Seed);
pos += 20;
}
catch (Exception e) {
throw new CryptographicException ("Invalid blob.", e);
}
#if INSIDE_CORLIB && MOBILE
DSA dsa = (DSA)DSA.Create ();
dsa.ImportParameters (dsap);
#else
DSA dsa = null;
try {
dsa = (DSA)DSA.Create ();
dsa.ImportParameters (dsap);
}
catch (CryptographicException ce) {
// this may cause problem when this code is run under
// the SYSTEM identity on Windows (e.g. ASP.NET). See
// http://bugzilla.ximian.com/show_bug.cgi?id=77559
try {
CspParameters csp = new CspParameters ();
csp.Flags = CspProviderFlags.UseMachineKeyStore;
dsa = new DSACryptoServiceProvider (csp);
dsa.ImportParameters (dsap);
}
catch {
// rethrow original, not the later, exception if this fails
throw ce;
}
}
#endif
return dsa;
}
static public byte[] ToCapiPrivateKeyBlob (RSA rsa)
{
RSAParameters p = rsa.ExportParameters (true);
int keyLength = p.Modulus.Length; // in bytes
byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
// [2], [3] // RESERVED - Always 0
blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
blob [8] = 0x52; // Magic - RSA2 (ASCII in hex)
blob [9] = 0x53;
blob [10] = 0x41;
blob [11] = 0x32;
byte[] bitlen = GetBytesLE (keyLength << 3);
blob [12] = bitlen [0]; // bitlen
blob [13] = bitlen [1];
blob [14] = bitlen [2];
blob [15] = bitlen [3];
// public exponent (DWORD)
int pos = 16;
int n = p.Exponent.Length;
while (n > 0)
blob [pos++] = p.Exponent [--n];
// modulus
pos = 20;
byte[] part = p.Modulus;
int len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
// private key
part = p.P;
len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
part = p.Q;
len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
part = p.DP;
len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
part = p.DQ;
len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
part = p.InverseQ;
len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
part = p.D;
len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
return blob;
}
static public byte[] ToCapiPrivateKeyBlob (DSA dsa)
{
DSAParameters p = dsa.ExportParameters (true);
int keyLength = p.P.Length; // in bytes
// header + P + Q + G + X + count + seed
byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20];
blob [0] = 0x07; // Type - PRIVATEKEYBLOB (0x07)
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
// [2], [3] // RESERVED - Always 0
blob [5] = 0x22; // ALGID
blob [8] = 0x44; // Magic
blob [9] = 0x53;
blob [10] = 0x53;
blob [11] = 0x32;
byte[] bitlen = GetBytesLE (keyLength << 3);
blob [12] = bitlen [0];
blob [13] = bitlen [1];
blob [14] = bitlen [2];
blob [15] = bitlen [3];
int pos = 16;
byte[] part = p.P;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, keyLength);
pos += keyLength;
part = p.Q;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, 20);
pos += 20;
part = p.G;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, keyLength);
pos += keyLength;
part = p.X;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, 20);
pos += 20;
Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4);
pos += 4;
part = p.Seed;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, 20);
return blob;
}
#if INSIDE_CORLIB
static internal bool TryImportCapiPublicKeyBlob (byte[] blob, int offset)
{
try {
var rsap = GetParametersFromCapiPublicKeyBlob (blob, offset);
// Since we are only checking whether this throws an exception and
// not actually returning the `RSA` object, we can use `RSAManaged`
// here because that's what the `RSACryptoServiceProvider` implementation
// does internally.
var rsa = new RSAManaged ();
rsa.ImportParameters (rsap);
return true;
} catch (CryptographicException) {
return false;
}
}
#endif
static public RSA FromCapiPublicKeyBlob (byte[] blob)
{
return FromCapiPublicKeyBlob (blob, 0);
}
static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset)
{
var rsap = GetParametersFromCapiPublicKeyBlob (blob, offset);
try {
#if INSIDE_CORLIB && MOBILE
RSA rsa = RSA.Create ();
rsa.ImportParameters (rsap);
#else
RSA rsa = null;
try {
rsa = RSA.Create ();
rsa.ImportParameters (rsap);
}
catch (CryptographicException) {
// this may cause problem when this code is run under
// the SYSTEM identity on Windows (e.g. ASP.NET). See
// http://bugzilla.ximian.com/show_bug.cgi?id=77559
CspParameters csp = new CspParameters ();
csp.Flags = CspProviderFlags.UseMachineKeyStore;
rsa = new RSACryptoServiceProvider (csp);
rsa.ImportParameters (rsap);
}
#endif
return rsa;
} catch (Exception e) {
throw new CryptographicException ("Invalid blob.", e);
}
}
static RSAParameters GetParametersFromCapiPublicKeyBlob (byte[] blob, int offset)
{
if (blob == null)
throw new ArgumentNullException ("blob");
if (offset >= blob.Length)
throw new ArgumentException ("blob is too small.");
try {
if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
(blob [offset+1] != 0x02) || // Version (0x02)
(blob [offset+2] != 0x00) || // Reserved (word)
(blob [offset+3] != 0x00) ||
(ToUInt32LE (blob, offset+8) != 0x31415352)) // DWORD magic = RSA1
throw new CryptographicException ("Invalid blob header");
// ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
// int algId = ToInt32LE (blob, offset+4);
// DWORD bitlen
int bitLen = ToInt32LE (blob, offset+12);
// DWORD public exponent
RSAParameters rsap = new RSAParameters ();
rsap.Exponent = new byte [3];
rsap.Exponent [0] = blob [offset+18];
rsap.Exponent [1] = blob [offset+17];
rsap.Exponent [2] = blob [offset+16];
int pos = offset+20;
// BYTE modulus[rsapubkey.bitlen/8];
int byteLen = (bitLen >> 3);
rsap.Modulus = new byte [byteLen];
Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
Array.Reverse (rsap.Modulus);
return rsap;
} catch (Exception e) {
throw new CryptographicException ("Invalid blob.", e);
}
}
static public DSA FromCapiPublicKeyBlobDSA (byte[] blob)
{
return FromCapiPublicKeyBlobDSA (blob, 0);
}
static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset)
{
if (blob == null)
throw new ArgumentNullException ("blob");
if (offset >= blob.Length)
throw new ArgumentException ("blob is too small.");
try {
if ((blob [offset] != 0x06) || // PUBLICKEYBLOB (0x06)
(blob [offset + 1] != 0x02) || // Version (0x02)
(blob [offset + 2] != 0x00) || // Reserved (word)
(blob [offset + 3] != 0x00) ||
(ToUInt32LE (blob, offset + 8) != 0x31535344)) // DWORD magic
throw new CryptographicException ("Invalid blob header");
int bitlen = ToInt32LE (blob, offset + 12);
DSAParameters dsap = new DSAParameters ();
int bytelen = bitlen >> 3;
int pos = offset + 16;
dsap.P = new byte [bytelen];
Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen);
Array.Reverse (dsap.P);
pos += bytelen;
dsap.Q = new byte [20];
Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20);
Array.Reverse (dsap.Q);
pos += 20;
dsap.G = new byte [bytelen];
Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen);
Array.Reverse (dsap.G);
pos += bytelen;
dsap.Y = new byte [bytelen];
Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen);
Array.Reverse (dsap.Y);
pos += bytelen;
dsap.Counter = ToInt32LE (blob, pos);
pos += 4;
dsap.Seed = new byte [20];
Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20);
Array.Reverse (dsap.Seed);
pos += 20;
DSA dsa = (DSA)DSA.Create ();
dsa.ImportParameters (dsap);
return dsa;
}
catch (Exception e) {
throw new CryptographicException ("Invalid blob.", e);
}
}
static public byte[] ToCapiPublicKeyBlob (RSA rsa)
{
RSAParameters p = rsa.ExportParameters (false);
int keyLength = p.Modulus.Length; // in bytes
byte[] blob = new byte [20 + keyLength];
blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
// [2], [3] // RESERVED - Always 0
blob [5] = 0x24; // ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
blob [8] = 0x52; // Magic - RSA1 (ASCII in hex)
blob [9] = 0x53;
blob [10] = 0x41;
blob [11] = 0x31;
byte[] bitlen = GetBytesLE (keyLength << 3);
blob [12] = bitlen [0]; // bitlen
blob [13] = bitlen [1];
blob [14] = bitlen [2];
blob [15] = bitlen [3];
// public exponent (DWORD)
int pos = 16;
int n = p.Exponent.Length;
while (n > 0)
blob [pos++] = p.Exponent [--n];
// modulus
pos = 20;
byte[] part = p.Modulus;
int len = part.Length;
Array.Reverse (part, 0, len);
Buffer.BlockCopy (part, 0, blob, pos, len);
pos += len;
return blob;
}
static public byte[] ToCapiPublicKeyBlob (DSA dsa)
{
DSAParameters p = dsa.ExportParameters (false);
int keyLength = p.P.Length; // in bytes
// header + P + Q + G + Y + count + seed
byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20];
blob [0] = 0x06; // Type - PUBLICKEYBLOB (0x06)
blob [1] = 0x02; // Version - Always CUR_BLOB_VERSION (0x02)
// [2], [3] // RESERVED - Always 0
blob [5] = 0x22; // ALGID
blob [8] = 0x44; // Magic
blob [9] = 0x53;
blob [10] = 0x53;
blob [11] = 0x31;
byte[] bitlen = GetBytesLE (keyLength << 3);
blob [12] = bitlen [0];
blob [13] = bitlen [1];
blob [14] = bitlen [2];
blob [15] = bitlen [3];
int pos = 16;
byte[] part;
part = p.P;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, keyLength);
pos += keyLength;
part = p.Q;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, 20);
pos += 20;
part = p.G;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, keyLength);
pos += keyLength;
part = p.Y;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, keyLength);
pos += keyLength;
Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4);
pos += 4;
part = p.Seed;
Array.Reverse (part);
Buffer.BlockCopy (part, 0, blob, pos, 20);
return blob;
}
// PRIVATEKEYBLOB
// PUBLICKEYBLOB
static public RSA FromCapiKeyBlob (byte[] blob)
{
return FromCapiKeyBlob (blob, 0);
}
static public RSA FromCapiKeyBlob (byte[] blob, int offset)
{
if (blob == null)
throw new ArgumentNullException ("blob");
if (offset >= blob.Length)
throw new ArgumentException ("blob is too small.");
switch (blob [offset]) {
case 0x00:
// this could be a public key inside an header
// like "sn -e" would produce
if (blob [offset + 12] == 0x06) {
return FromCapiPublicKeyBlob (blob, offset + 12);
}
break;
case 0x06:
return FromCapiPublicKeyBlob (blob, offset);
case 0x07:
return FromCapiPrivateKeyBlob (blob, offset);
}
throw new CryptographicException ("Unknown blob format.");
}
static public DSA FromCapiKeyBlobDSA (byte[] blob)
{
return FromCapiKeyBlobDSA (blob, 0);
}
static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset)
{
if (blob == null)
throw new ArgumentNullException ("blob");
if (offset >= blob.Length)
throw new ArgumentException ("blob is too small.");
switch (blob [offset]) {
case 0x06:
return FromCapiPublicKeyBlobDSA (blob, offset);
case 0x07:
return FromCapiPrivateKeyBlobDSA (blob, offset);
}
throw new CryptographicException ("Unknown blob format.");
}
static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey)
{
if (keypair == null)
throw new ArgumentNullException ("keypair");
// check between RSA and DSA (and potentially others like DH)
if (keypair is RSA)
return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
else if (keypair is DSA)
return ToCapiKeyBlob ((DSA)keypair, includePrivateKey);
else
return null; // TODO
}
static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey)
{
if (rsa == null)
throw new ArgumentNullException ("rsa");
if (includePrivateKey)
return ToCapiPrivateKeyBlob (rsa);
else
return ToCapiPublicKeyBlob (rsa);
}
static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey)
{
if (dsa == null)
throw new ArgumentNullException ("dsa");
if (includePrivateKey)
return ToCapiPrivateKeyBlob (dsa);
else
return ToCapiPublicKeyBlob (dsa);
}
static public string ToHex (byte[] input)
{
if (input == null)
return null;
StringBuilder sb = new StringBuilder (input.Length * 2);
foreach (byte b in input) {
sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture));
}
return sb.ToString ();
}
static private byte FromHexChar (char c)
{
if ((c >= 'a') && (c <= 'f'))
return (byte) (c - 'a' + 10);
if ((c >= 'A') && (c <= 'F'))
return (byte) (c - 'A' + 10);
if ((c >= '0') && (c <= '9'))
return (byte) (c - '0');
throw new ArgumentException ("invalid hex char");
}
static public byte[] FromHex (string hex)
{
if (hex == null)
return null;
if ((hex.Length & 0x1) == 0x1)
throw new ArgumentException ("Length must be a multiple of 2");
byte[] result = new byte [hex.Length >> 1];
int n = 0;
int i = 0;
while (n < result.Length) {
result [n] = (byte) (FromHexChar (hex [i++]) << 4);
result [n++] += FromHexChar (hex [i++]);
}
return result;
}
}
}

View File

@@ -0,0 +1,147 @@
//
// Mono.Security.Cryptography.CryptoTools
// Shared class for common cryptographic functionalities
//
// Authors:
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
// Copyright (C) 2004, 2008 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Security.Cryptography;
namespace Mono.Security.Cryptography {
#if INSIDE_CORLIB
internal
#else
public
#endif
sealed class KeyBuilder {
static private RandomNumberGenerator rng;
private KeyBuilder ()
{
}
static RandomNumberGenerator Rng {
get {
if (rng == null)
rng = RandomNumberGenerator.Create ();
return rng;
}
}
static public byte[] Key (int size)
{
byte[] key = new byte [size];
Rng.GetBytes (key);
return key;
}
static public byte[] IV (int size)
{
byte[] iv = new byte [size];
Rng.GetBytes (iv);
return iv;
}
}
// Process an array as a sequence of blocks
#if INSIDE_CORLIB
internal
#else
public
#endif
class BlockProcessor {
private ICryptoTransform transform;
private byte[] block;
private int blockSize; // in bytes (not in bits)
private int blockCount;
public BlockProcessor (ICryptoTransform transform)
: this (transform, transform.InputBlockSize) {}
// some Transforms (like HashAlgorithm descendant) return 1 for
// block size (which isn't their real internal block size)
public BlockProcessor (ICryptoTransform transform, int blockSize)
{
if (transform == null)
throw new ArgumentNullException ("transform");
if (blockSize <= 0)
throw new ArgumentOutOfRangeException ("blockSize");
this.transform = transform;
this.blockSize = blockSize;
block = new byte [blockSize];
}
~BlockProcessor ()
{
// zeroize our block (so we don't retain any information)
Array.Clear (block, 0, blockSize);
}
public void Initialize ()
{
Array.Clear (block, 0, blockSize);
blockCount = 0;
}
public void Core (byte[] rgb)
{
Core (rgb, 0, rgb.Length);
}
public void Core (byte[] rgb, int ib, int cb)
{
// 1. fill the rest of the "block"
int n = System.Math.Min (blockSize - blockCount, cb);
Buffer.BlockCopy (rgb, ib, block, blockCount, n);
blockCount += n;
// 2. if block is full then transform it
if (blockCount == blockSize) {
transform.TransformBlock (block, 0, blockSize, block, 0);
// 3. transform any other full block in specified buffer
int b = (int) ((cb - n) / blockSize);
for (int i=0; i < b; i++) {
transform.TransformBlock (rgb, n + ib, blockSize, block, 0);
n += blockSize;
}
// 4. if data is still present fill the "block" with the remainder
blockCount = cb - n;
if (blockCount > 0)
Buffer.BlockCopy (rgb, n + ib, block, 0, blockCount);
}
}
public byte[] Final ()
{
return transform.TransformFinalBlock (block, 0, blockCount);
}
}
}

View File

@@ -0,0 +1,66 @@
//
// DHKeyGeneration.cs: Defines the different key generation methods.
//
// Author:
// Pieter Philippaerts (Pieter@mentalis.org)
//
// (C) 2003 The Mentalis.org Team (http://www.mentalis.org/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Security.Cryptography {
/// <summary>
/// Defines the different Diffie-Hellman key generation methods.
/// </summary>
public enum DHKeyGeneration {
/// <summary>
/// [TODO] you first randomly select a prime Q of size 160 bits, then choose P randomly among numbers like
/// Q*R+1 with R random. Then you go along with finding a generator G which has order exactly Q. The private
/// key X is then a number modulo Q.
/// [FIPS 186-2-Change1 -- http://csrc.nist.gov/publications/fips/]
/// </summary>
// see RFC2631 [http://www.faqs.org/rfcs/rfc2631.html]
//DSA,
/// <summary>
/// Returns dynamically generated values for P and G. Unlike the Sophie Germain or DSA key generation methods,
/// this method does not ensure that the selected prime offers an adequate security level.
/// </summary>
Random,
/// <summary>
/// Returns dynamically generated values for P and G. P is a Sophie Germain prime, which has some interesting
/// security features when used with Diffie Hellman.
/// </summary>
//SophieGermain,
/// <summary>
/// Returns values for P and G that are hard coded in this library. Contrary to what your intuition may tell you,
/// using these hard coded values is perfectly safe.
/// The values of the P and G parameters are taken from 'The OAKLEY Key Determination Protocol' [RFC2412].
/// This is the prefered key generation method, because it is very fast and very safe.
/// Because this method uses fixed values for the P and G parameters, not all bit sizes are supported.
/// The current implementation supports bit sizes of 768, 1024 and 1536.
/// </summary>
Static
}
}

View File

@@ -0,0 +1,53 @@
//
// DHParameters.cs: Defines a structure that holds the parameters of the Diffie-Hellman algorithm
//
// Author:
// Pieter Philippaerts (Pieter@mentalis.org)
//
// (C) 2003 The Mentalis.org Team (http://www.mentalis.org/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
namespace Mono.Security.Cryptography {
/// <summary>
/// Represents the parameters of the Diffie-Hellman algorithm.
/// </summary>
[Serializable]
public struct DHParameters {
/// <summary>
/// Represents the public <b>P</b> parameter of the Diffie-Hellman algorithm.
/// </summary>
public byte[] P;
/// <summary>
/// Represents the public <b>G</b> parameter of the Diffie-Hellman algorithm.
/// </summary>
public byte[] G;
/// <summary>
/// Represents the private <b>X</b> parameter of the Diffie-Hellman algorithm.
/// </summary>
[NonSerialized]
public byte[] X;
}
}

View File

@@ -0,0 +1,124 @@
//
// DiffieHellman.cs: Defines a base class from which all Diffie-Hellman implementations inherit
//
// Author:
// Pieter Philippaerts (Pieter@mentalis.org)
//
// (C) 2003 The Mentalis.org Team (http://www.mentalis.org/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using Mono.Math;
namespace Mono.Security.Cryptography {
/// <summary>
/// Defines a base class from which all Diffie-Hellman implementations inherit.
/// </summary>
public abstract class DiffieHellman : AsymmetricAlgorithm {
/// <summary>
/// Creates an instance of the default implementation of the <see cref="DiffieHellman"/> algorithm.
/// </summary>
/// <returns>A new instance of the default implementation of DiffieHellman.</returns>
public static new DiffieHellman Create () {
return Create ("Mono.Security.Cryptography.DiffieHellman");
}
/// <summary>
/// Creates an instance of the specified implementation of <see cref="DiffieHellman"/>.
/// </summary>
/// <param name="algName">The name of the implementation of DiffieHellman to use.</param>
/// <returns>A new instance of the specified implementation of DiffieHellman.</returns>
public static new DiffieHellman Create (string algName) {
return (DiffieHellman) CryptoConfig.CreateFromName (algName);
}
/// <summary>
/// When overridden in a derived class, creates the key exchange data.
/// </summary>
/// <returns>The key exchange data to be sent to the intended recipient.</returns>
public abstract byte[] CreateKeyExchange();
/// <summary>
/// When overridden in a derived class, extracts secret information from the key exchange data.
/// </summary>
/// <param name="keyex">The key exchange data within which the secret information is hidden.</param>
/// <returns>The secret information derived from the key exchange data.</returns>
public abstract byte[] DecryptKeyExchange (byte[] keyex);
/// <summary>
/// When overridden in a derived class, exports the <see cref="DHParameters"/>.
/// </summary>
/// <param name="includePrivate"><b>true</b> to include private parameters; otherwise, <b>false</b>.</param>
/// <returns>The parameters for Diffie-Hellman.</returns>
public abstract DHParameters ExportParameters (bool includePrivate);
/// <summary>
/// When overridden in a derived class, imports the specified <see cref="DHParameters"/>.
/// </summary>
/// <param name="parameters">The parameters for Diffie-Hellman.</param>
public abstract void ImportParameters (DHParameters parameters);
private byte[] GetNamedParam(SecurityElement se, string param) {
SecurityElement sep = se.SearchForChildByTag(param);
if (sep == null)
return null;
return Convert.FromBase64String(sep.Text);
}
/// <summary>
/// Creates and returns an XML string representation of the current <see cref="DiffieHellman"/> object.
/// </summary>
/// <param name="includePrivateParameters"><b>true</b> to include private parameters; otherwise, <b>false</b>.</param>
/// <returns>An XML string encoding of the current DiffieHellman object.</returns>
public override string ToXmlString (bool includePrivateParameters) {
StringBuilder sb = new StringBuilder ();
DHParameters dhParams = ExportParameters(includePrivateParameters);
try {
sb.Append ("<DHKeyValue>");
sb.Append ("<P>");
sb.Append (Convert.ToBase64String (dhParams.P));
sb.Append ("</P>");
sb.Append ("<G>");
sb.Append (Convert.ToBase64String (dhParams.G));
sb.Append ("</G>");
if (includePrivateParameters) {
sb.Append ("<X>");
sb.Append (Convert.ToBase64String (dhParams.X));
sb.Append ("</X>");
}
sb.Append ("</DHKeyValue>");
} finally {
Array.Clear(dhParams.P, 0, dhParams.P.Length);
Array.Clear(dhParams.G, 0, dhParams.G.Length);
if (dhParams.X != null)
Array.Clear(dhParams.X, 0, dhParams.X.Length);
}
return sb.ToString ();
}
}
}

View File

@@ -0,0 +1,273 @@
//
// DiffieHellmanManaged.cs: Implements the Diffie-Hellman key agreement algorithm
//
// Author:
// Pieter Philippaerts (Pieter@mentalis.org)
//
// (C) 2003 The Mentalis.org Team (http://www.mentalis.org/)
//
// References:
// - PKCS#3 [http://www.rsasecurity.com/rsalabs/pkcs/pkcs-3/]
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Security.Cryptography;
using Mono.Math;
namespace Mono.Security.Cryptography {
/// <summary>
/// Implements the Diffie-Hellman algorithm.
/// </summary>
public sealed class DiffieHellmanManaged : DiffieHellman {
/// <summary>
/// Initializes a new <see cref="DiffieHellmanManaged"/> instance.
/// </summary>
/// <remarks>The default length of the shared secret is 1024 bits.</remarks>
public DiffieHellmanManaged() : this(1024, 160, DHKeyGeneration.Static) {}
/// <summary>
/// Initializes a new <see cref="DiffieHellmanManaged"/> instance.
/// </summary>
/// <param name="bitLength">The length, in bits, of the public P parameter.</param>
/// <param name="l">The length, in bits, of the secret value X. This parameter can be set to 0 to use the default size.</param>
/// <param name="method">One of the <see cref="DHKeyGeneration"/> values.</param>
/// <remarks>The larger the bit length, the more secure the algorithm is. The default is 1024 bits. The minimum bit length is 128 bits.<br/>The size of the private value will be one fourth of the bit length specified.</remarks>
/// <exception cref="ArgumentException">The specified bit length is invalid.</exception>
public DiffieHellmanManaged(int bitLength, int l, DHKeyGeneration method) {
if (bitLength < 256 || l < 0)
throw new ArgumentException();
BigInteger p, g;
GenerateKey (bitLength, method, out p, out g);
Initialize(p, g, null, l, false);
}
/// <summary>
/// Initializes a new <see cref="DiffieHellmanManaged"/> instance.
/// </summary>
/// <param name="p">The P parameter of the Diffie-Hellman algorithm. This is a public parameter.</param>
/// <param name="g">The G parameter of the Diffie-Hellman algorithm. This is a public parameter.</param>
/// <param name="x">The X parameter of the Diffie-Hellman algorithm. This is a private parameter. If this parameters is a null reference (<b>Nothing</b> in Visual Basic), a secret value of the default size will be generated.</param>
/// <exception cref="ArgumentNullException"><paramref name="p"/> or <paramref name="g"/> is a null reference (<b>Nothing</b> in Visual Basic).</exception>
/// <exception cref="CryptographicException"><paramref name="p"/> or <paramref name="g"/> is invalid.</exception>
public DiffieHellmanManaged(byte[] p, byte[] g, byte[] x) {
if (p == null || g == null)
throw new ArgumentNullException();
if (x == null)
Initialize(new BigInteger(p), new BigInteger(g), null, 0, true);
else
Initialize(new BigInteger(p), new BigInteger(g), new BigInteger(x), 0, true);
}
/// <summary>
/// Initializes a new <see cref="DiffieHellmanManaged"/> instance.
/// </summary>
/// <param name="p">The P parameter of the Diffie-Hellman algorithm.</param>
/// <param name="g">The G parameter of the Diffie-Hellman algorithm.</param>
/// <param name="l">The length, in bits, of the private value. If 0 is specified, the default value will be used.</param>
/// <exception cref="ArgumentNullException"><paramref name="p"/> or <paramref name="g"/> is a null reference (<b>Nothing</b> in Visual Basic).</exception>
/// <exception cref="ArgumentException"><paramref name="l"/> is invalid.</exception>
/// <exception cref="CryptographicException"><paramref name="p"/> or <paramref name="g"/> is invalid.</exception>
public DiffieHellmanManaged(byte[] p, byte[] g, int l) {
if (p == null || g == null)
throw new ArgumentNullException();
if (l < 0)
throw new ArgumentException();
Initialize(new BigInteger(p), new BigInteger(g), null, l, true);
}
// initializes the private variables (throws CryptographicException)
private void Initialize(BigInteger p, BigInteger g, BigInteger x, int secretLen, bool checkInput) {
if (checkInput) {
if (!p.IsProbablePrime() || g <= 0 || g >= p || (x != null && (x <= 0 || x > p - 2)))
throw new CryptographicException();
}
// default is to generate a number as large as the prime this
// is usually overkill, but it's the most secure thing we can
// do if the user doesn't specify a desired secret length ...
if (secretLen == 0)
secretLen = p.BitCount();
m_P = p;
m_G = g;
if (x == null) {
BigInteger pm1 = m_P - 1;
for(m_X = BigInteger.GenerateRandom(secretLen); m_X >= pm1 || m_X == 0; m_X = BigInteger.GenerateRandom(secretLen)) {}
} else {
m_X = x;
}
}
/// <summary>
/// Creates the key exchange data.
/// </summary>
/// <returns>The key exchange data to be sent to the intended recipient.</returns>
public override byte[] CreateKeyExchange() {
BigInteger y = m_G.ModPow(m_X, m_P);
byte[] ret = y.GetBytes();
y.Clear();
return ret;
}
/// <summary>
/// Extracts secret information from the key exchange data.
/// </summary>
/// <param name="keyEx">The key exchange data within which the shared key is hidden.</param>
/// <returns>The shared key derived from the key exchange data.</returns>
public override byte[] DecryptKeyExchange(byte[] keyEx) {
BigInteger pvr = new BigInteger(keyEx);
BigInteger z = pvr.ModPow(m_X, m_P);
byte[] ret = z.GetBytes();
z.Clear();
return ret;
}
/// <summary>
/// Gets the name of the key exchange algorithm.
/// </summary>
/// <value>The name of the key exchange algorithm.</value>
public override string KeyExchangeAlgorithm {
get {
return "1.2.840.113549.1.3"; // PKCS#3 OID
}
}
/// <summary>
/// Gets the name of the signature algorithm.
/// </summary>
/// <value>The name of the signature algorithm.</value>
public override string SignatureAlgorithm {
get {
return null;
}
}
// clear keys
protected override void Dispose(bool disposing) {
if (!m_Disposed) {
if (m_P != null) m_P.Clear();
if (m_G != null) m_G.Clear();
if (m_X != null) m_X.Clear();
}
m_Disposed = true;
}
/// <summary>
/// Exports the <see cref="DHParameters"/>.
/// </summary>
/// <param name="includePrivateParameters"><b>true</b> to include private parameters; otherwise, <b>false</b>.</param>
/// <returns>The parameters for <see cref="DiffieHellman"/>.</returns>
public override DHParameters ExportParameters(bool includePrivateParameters) {
DHParameters ret = new DHParameters();
ret.P = m_P.GetBytes();
ret.G = m_G.GetBytes();
if (includePrivateParameters) {
ret.X = m_X.GetBytes();
}
return ret;
}
/// <summary>
/// Imports the specified <see cref="DHParameters"/>.
/// </summary>
/// <param name="parameters">The parameters for <see cref="DiffieHellman"/>.</param>
/// <exception cref="CryptographicException"><paramref name="P"/> or <paramref name="G"/> is a null reference (<b>Nothing</b> in Visual Basic) -or- <paramref name="P"/> is not a prime number.</exception>
public override void ImportParameters(DHParameters parameters) {
if (parameters.P == null)
throw new CryptographicException("Missing P value.");
if (parameters.G == null)
throw new CryptographicException("Missing G value.");
BigInteger p = new BigInteger(parameters.P), g = new BigInteger(parameters.G), x = null;
if (parameters.X != null) {
x = new BigInteger(parameters.X);
}
Initialize(p, g, x, 0, true);
}
~DiffieHellmanManaged() {
Dispose(false);
}
//TODO: implement DH key generation methods
private void GenerateKey(int bitlen, DHKeyGeneration keygen, out BigInteger p, out BigInteger g) {
if (keygen == DHKeyGeneration.Static) {
if (bitlen == 768)
p = new BigInteger(m_OAKLEY768);
else if (bitlen == 1024)
p = new BigInteger(m_OAKLEY1024);
else if (bitlen == 1536)
p = new BigInteger(m_OAKLEY1536);
else
throw new ArgumentException("Invalid bit size.");
g = new BigInteger(22); // all OAKLEY keys use 22 as generator
//} else if (keygen == DHKeyGeneration.SophieGermain) {
// throw new NotSupportedException(); //TODO
//} else if (keygen == DHKeyGeneration.DSA) {
// 1. Let j = (p - 1)/q.
// 2. Set h = any integer, where 1 < h < p - 1
// 3. Set g = h^j mod p
// 4. If g = 1 go to step 2
// BigInteger j = (p - 1) / q;
} else { // random
p = BigInteger.GeneratePseudoPrime(bitlen);
g = new BigInteger(3); // always use 3 as a generator
}
}
private BigInteger m_P;
private BigInteger m_G;
private BigInteger m_X;
private bool m_Disposed;
private static byte[] m_OAKLEY768 = new byte[] {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
private static byte[] m_OAKLEY1024 = new byte[] {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
private static byte[] m_OAKLEY1536 = new byte[] {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
}
}

View File

@@ -5,7 +5,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
@@ -15,5 +15,9 @@
<PackageReference Include="Serilog" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Mono\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Novaria.Common.Util
{
using System;
using System.Numerics;
public static class BigIntegerExtensions
{
public static byte[] GetBytes(this BigInteger value)
{
if (value == 0)
{
return new byte[1];
}
int bitCount = value.GetBitCount();
int byteCount = bitCount >> 3;
if ((bitCount & 7) != 0)
{
byteCount++;
}
byte[] result = new byte[byteCount];
int remainingBytes = byteCount & 3;
if (remainingBytes == 0)
{
remainingBytes = 4;
}
int byteIndex = 0;
// Convert BigInteger to unsigned equivalent (to match the behavior of the original code)
BigInteger unsignedValue = BigInteger.Abs(value);
// Iterate through each 32-bit chunk of the BigInteger
while (unsignedValue != 0)
{
uint currentWord = (uint)(unsignedValue & 0xFFFFFFFF);
unsignedValue >>= 32;
for (int i = remainingBytes - 1; i >= 0; i--)
{
result[byteIndex + i] = (byte)(currentWord & 0xFF);
currentWord >>= 8;
}
byteIndex += remainingBytes;
remainingBytes = 4;
}
return result;
}
// Helper method to calculate the number of significant bits in a BigInteger
private static int GetBitCount(this BigInteger value)
{
BigInteger unsignedValue = BigInteger.Abs(value);
int bits = 0;
while (unsignedValue != 0)
{
unsignedValue >>= 1;
bits++;
}
return bits;
}
}
}