From fb42e2d725c465021555651a0abf529709650135 Mon Sep 17 00:00:00 2001 From: faragher <47290019+faragher@users.noreply.github.com> Date: Sat, 7 Oct 2023 01:06:59 -0500 Subject: [PATCH] Updating to current offline state --- Crypto/Cryptography.cs | 368 +++++++++++++++++++++++++++++++++++++++++ Crypto/fernet.cs | 7 - Destination.cs | 77 +++++++++ Identity.cs | 75 ++++++++- Program.cs | 75 ++++++++- RNodeInterface.csproj | 16 ++ Reticulum.cs | 31 ++++ 7 files changed, 637 insertions(+), 12 deletions(-) create mode 100644 Crypto/Cryptography.cs create mode 100644 Destination.cs create mode 100644 RNodeInterface.csproj diff --git a/Crypto/Cryptography.cs b/Crypto/Cryptography.cs new file mode 100644 index 0000000..afa0e91 --- /dev/null +++ b/Crypto/Cryptography.cs @@ -0,0 +1,368 @@ +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Engines; +using System.Text; + +namespace RNS +{ + public static class Cryptography + { + public class Fernet + { + // + // !!!WARNING!!! + // This is a modified implementation of Fernet. Do not attempt + // to use for standard Fernet implementations or replace with + // a Fernet library. + // + // Typical Fernet has the following structure, base64url encoded: + // 1 byte version code + // 8 byte timestamp + // 16 byte AES-128 IV + // 16 byte * n data blocks + // 32 byte HMAC + // + // Custom implementation omits version and timestamp, saving 9 bytes. + // 16 byte AES-128 IV + // 16 byte * n data blocks + // 32 byte HMAC + // + // This results in an overhead of 48 bytes (IV+HMAC) instead of + // 57 bytes (Version+Timestamp+IV+HMAC) + // + public const int FERNET_OVERHEAD = 48; //Bytes + } + + public static byte[] SHA256(byte[] data) + { + return System.Security.Cryptography.SHA256.HashData(data); + } + + public static byte[] SHA512(byte[] data) + { + return System.Security.Cryptography.SHA512.HashData(data); + } + + //public class HMAC + //{ + + //} + + public static byte[] HMAC256(byte[] key, byte[] message) + { + Org.BouncyCastle.Crypto.Macs.HMac hmac = new Org.BouncyCastle.Crypto.Macs.HMac(new Org.BouncyCastle.Crypto.Digests.Sha256Digest()); + hmac.Init(new KeyParameter(key)); + byte[] result = new byte[hmac.GetMacSize()]; + hmac.BlockUpdate(message, 0, message.Length); + hmac.DoFinal(result, 0); + return result; + } + + public static byte[] HMAC512(byte[] key, byte[] message) + { + Org.BouncyCastle.Crypto.Macs.HMac hmac = new Org.BouncyCastle.Crypto.Macs.HMac(new Org.BouncyCastle.Crypto.Digests.Sha512Digest()); + hmac.Init(new KeyParameter(key)); + byte[] result = new byte[hmac.GetMacSize()]; + hmac.BlockUpdate(message, 0, message.Length); + hmac.DoFinal(result, 0); + return result; + } + + public static class AES_128_CBC + { + public static byte[] encrypt(byte[] plaintext, byte[] key, byte[] iv) + { + ICipherParameters Param = new KeyParameter(key); + ParametersWithIV ParamIV = new ParametersWithIV(Param, iv); + Org.BouncyCastle.Crypto.IBufferedCipher cipher = Org.BouncyCastle.Security.CipherUtilities.GetCipher("AES/CBC/PKCS7Padding"); + cipher.Init(true, ParamIV); + return cipher.DoFinal(plaintext, 0, plaintext.Length); + } + + public static byte[] decrypt(byte[] ciphertext, byte[] key, byte[] iv) + { + ICipherParameters Param = new KeyParameter(key); + ParametersWithIV ParamIV = new ParametersWithIV(Param, iv); + Org.BouncyCastle.Crypto.IBufferedCipher cipher = Org.BouncyCastle.Security.CipherUtilities.GetCipher("AES/CBC/PKCS7Padding"); + cipher.Init(false, ParamIV); + return cipher.DoFinal(ciphertext, 0, ciphertext.Length); + } + } + + public class Ed25519PrivateKey + { + byte[] seed; + Ed25519PrivateKeyParameters sk; + Ed25519PublicKeyParameters vk; + + public Ed25519PrivateKey(byte[] _seed) + { + seed = _seed; + sk = new Ed25519PrivateKeyParameters(seed,0); + vk = sk.GeneratePublicKey(); + if (sk == null) { Console.WriteLine("The Signing key is still null"); } + + + + } + + public Ed25519PrivateKey() + { + seed = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32); + from_private_bytes(seed); + } + + public void BIT() + { + Ed25519PrivateKeyParameters Prv = new Ed25519PrivateKeyParameters(seed,0); + Ed25519PublicKeyParameters Pub = Prv.GeneratePublicKey(); + byte[] message = new byte[] { (byte)'a',(byte)'b',(byte)'c' }; + ISigner sig = SignerUtilities.GetSigner("Ed25519"); + sig.Init(true, Prv); + sig.BlockUpdate(message, 0, message.Length); + byte[] signature = sig.GenerateSignature(); + sig.Init(false, Pub); + sig.BlockUpdate(message, 0, message.Length); + Console.WriteLine(sig.VerifySignature(signature)); + signature = sign(message); + sig.Init(false, Pub); + sig.BlockUpdate(message, 0, message.Length); + Console.WriteLine(sig.VerifySignature(signature)); + + } + + public void generate() + { + seed = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32); + from_private_bytes(seed); + } + + public void from_private_bytes(byte[] _seed) + { + seed = _seed; + + sk = new Ed25519PrivateKeyParameters(seed, 0); + vk = sk.GeneratePublicKey(); + } + + public Ed25519PublicKeyParameters public_key() + { + vk = sk.GeneratePublicKey(); + + return vk; + } + + public byte[] sign(byte[] message) + { + //byte[] buffer; + ISigner sig = SignerUtilities.GetSigner("Ed25519"); + sig.Init(true, sk); + if (sk == null) { Console.WriteLine("The Signing key is still null and is signing!"); } + sig.BlockUpdate(message, 0, message.Length); + return sig.GenerateSignature(); + } + + } + + public class Ed25519PublicKey + { + byte[] seed; + public Ed25519PublicKeyParameters vk; + + public Ed25519PublicKey(byte[] _seed) + { + seed= _seed; + vk = new Ed25519PublicKeyParameters(_seed,0); + } + + public Ed25519PublicKey(Ed25519PublicKeyParameters _vk) + { + vk= _vk; + } + + public bool verify(byte[] signature, byte[] message) + { + if (vk == null) { Console.WriteLine("The Verifying key is still null"); } + ISigner sig = SignerUtilities.GetSigner("Ed25519"); + sig.Init(false, vk); + sig.BlockUpdate(message, 0, message.Length); + return sig.VerifySignature(signature); + + } + + public void SET(Ed25519PublicKeyParameters Pub) + { + vk= Pub; + } + + public void BIT() + { + Ed25519PrivateKeyParameters Prv = new Ed25519PrivateKeyParameters(System.Security.Cryptography.RandomNumberGenerator.GetBytes(32), 0); + Ed25519PublicKeyParameters Pub = Prv.GeneratePublicKey(); + vk = Pub; + byte[] message = new byte[] { (byte)'a', (byte)'b', (byte)'c' }; + ISigner sig = SignerUtilities.GetSigner("Ed25519"); + sig.Init(true, Prv); + sig.BlockUpdate(message, 0, message.Length); + byte[] signature = sig.GenerateSignature(); + sig.Init(false, Pub); + sig.BlockUpdate(message, 0, message.Length); + Console.WriteLine(sig.VerifySignature(signature)); + Console.WriteLine(verify(signature, message)); + if (verify(signature, message)) + { + Console.WriteLine("Ed25519 signature valid"); + + } + else + { + + Console.WriteLine("Ed25519 signature invalid"); + + } + } + } + + + + + // Testing and scratchpad. To be removed. + public static void TestMe() + { + + IAsymmetricCipherKeyPairGenerator g = new Ed25519KeyPairGenerator(); + ISigner sig = SignerUtilities.GetSigner("Ed25519"); + KeyGenerationParameters P = new Ed25519KeyGenerationParameters(new SecureRandom()); + g.Init(P); + //IAsymmetricCipherKeyPairGenerator g = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + AsymmetricCipherKeyPair kp = g.GenerateKeyPair(); + AsymmetricKeyParameter sKey = kp.Private; + AsymmetricKeyParameter vKey = kp.Public; + + sig.Init(true, sKey); + byte[] data = new byte[] { (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h'}; + sig.BlockUpdate(data, 0, data.Length); + byte[] sigBytes = sig.GenerateSignature(); + sig.Init(false, vKey); + sig.BlockUpdate(data, 0, data.Length); + if (!sig.VerifySignature(sigBytes)) + { + Console.WriteLine("Verification Failed"); + } + else + { + Console.WriteLine("Verification Passed"); + } + Org.BouncyCastle.Crypto.IBufferedCipher Cyp = Org.BouncyCastle.Security.CipherUtilities.GetCipher("AES/CBC/PKCS5Padding"); + + + ICipherParameters Param = new KeyParameter(new byte[] {0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f }); + ParametersWithIV ParamIV = new ParametersWithIV(Param, new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + Cyp.Init(true,ParamIV); + byte[] vomit = Cyp.DoFinal(data, 0, data.Length); + foreach (byte b in vomit) + { + Console.Write(b.ToString("X")); + } + Console.WriteLine(); + Cyp.Init(false, ParamIV); + vomit = Cyp.DoFinal(vomit, 0, vomit.Length); + foreach(byte b in vomit) + { + Console.Write(b.ToString("X")); + } + Console.WriteLine(); + + + + + + } + + + + + //public class BCEngine + //{ + // private readonly Encoding _encoding; + // private readonly IBlockCipher _blockCipher; + // private PaddedBufferedBlockCipher _cipher; + // private IBlockCipherPadding _padding; + + // public BCEngine(IBlockCipher blockCipher, Encoding encoding) + // { + // _blockCipher = blockCipher; + // _encoding = encoding; + // } + + // public void SetPadding(IBlockCipherPadding padding) + // { + // if (padding != null) + // _padding = padding; + // } + + // public string Encrypt(string plain, string key) + // { + // byte[] result = BouncyCastleCrypto(true, _encoding.GetBytes(plain), key); + // return Convert.ToBase64String(result); + // } + + // public string Decrypt(string cipher, string key) + // { + // byte[] result = BouncyCastleCrypto(false, Convert.FromBase64String(cipher), key); + // return _encoding.GetString(result); + // } + + + + + //private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, string key) + //{ + // try + // { + // _cipher = _padding == null ? new PaddedBufferedBlockCipher(_blockCipher) : new PaddedBufferedBlockCipher(_blockCipher, _padding); + // byte[] keyByte = _encoding.GetBytes(key); + // _cipher.Init(forEncrypt, new KeyParameter(keyByte)); + // return _cipher.DoFinal(input); + // } + // catch (Org.BouncyCastle.Crypto.CryptoException ex) + // { + // throw new CryptoException("Exception", ex); + // } + //} + + //public string AESEncryption(string plain, string key, bool fips) + //{ + // BCEngine bcEngine = new BCEngine(new AesEngine(), _encoding); + // bcEngine.SetPadding(_padding); + // return bcEngine.Encrypt(plain, key); + //} + + //public string AESDecryption(string cipher, string key, bool fips) + //{ + // BCEngine bcEngine = new BCEngine(new AesEngine(), _encoding); + // bcEngine.SetPadding(_padding); + // return bcEngine.Decrypt(cipher, key); + //} + //} + + public enum DigestType + { + SHA256, + SHA512 + } + } + + public class CryptoEngine + { + private readonly IBlockCipher _blockCipher; + private PaddedBufferedBlockCipher _cipher; + private IBlockCipherPadding _padding; + } + +} diff --git a/Crypto/fernet.cs b/Crypto/fernet.cs index e530547..e69de29 100644 --- a/Crypto/fernet.cs +++ b/Crypto/fernet.cs @@ -1,7 +0,0 @@ -namespace RNS.Cryptography -{ - public class Fernet - { - public const int FERNET_OVERHEAD = 48; //Bytes - } -} \ No newline at end of file diff --git a/Destination.cs b/Destination.cs new file mode 100644 index 0000000..3babd59 --- /dev/null +++ b/Destination.cs @@ -0,0 +1,77 @@ +namespace RNS +{ + + public class Destination + { + const byte SINGLE = 0x00; + const byte GROUP = 0x01; + const byte PLAIN = 0x02; + const byte LINK = 0x03; + static byte[] types = new byte[] { SINGLE, GROUP, PLAIN, LINK }; + + const byte PROVE_NONE = 0x21; + const byte PROVE_APP = 0x22; + const byte PROVE_ALL = 0x23; + static byte[] proof_strategies = new byte[] { PROVE_NONE, PROVE_APP, PROVE_ALL }; + + const byte ALLOW_NONE = 0x00; + const byte ALLOW_ALL = 0x01; + const byte ALLOW_LIST = 0x02; + static byte[] request_policies = new byte[] { ALLOW_NONE, ALLOW_ALL, ALLOW_LIST }; + + const byte IN = 0x11; + const byte OUT = 0x12; + static byte[] directions = new byte[] { IN, OUT }; + + const byte PR_TAG_WINDOW = 30; + + static string Expand_Name(Identity _ID, string _app_name, List _aspects) + { + if (_app_name.Contains(".")) + { + throw new ArgumentException("Dots cannot be used in app names"); + } + + string name = _app_name; + foreach(string S in _aspects) + { + if (S.Contains(".")) throw new ArgumentException("Dots cannot be used in aspects"); + name+="."+S; + } + + if (_ID != null) + { + name+="."+_ID.PrettyName; + } + + return name; + } + + public static byte[] Hash(Identity _ID, string _app_name, List aspects) + { + string FullName = Expand_Name(_ID, _app_name, aspects); + byte[] Hash = Identity.FullHash(Util.UTF8_to_Bytes(FullName)); + byte[] TruncatedHash = Util.TruncateHash(Hash, Identity.NAME_HASH_LENGTH/8); + byte[] addr_hash_material = TruncatedHash; + + foreach (byte b in Hash) + { + Console.Write(b.ToString("X2")); + } + + if (_ID != null) + { + addr_hash_material = new byte[TruncatedHash.Length + _ID.Hash.Length]; + Array.Copy(TruncatedHash,0,addr_hash_material,0,TruncatedHash.Length); + Array.Copy(_ID.Hash,0,addr_hash_material,TruncatedHash.Length,_ID.Hash.Length); + } + + Console.WriteLine(" Name Hash Length = " + (Identity.NAME_HASH_LENGTH / 8)); + + return Util.TruncateHash(addr_hash_material, Identity.NAME_HASH_LENGTH/8); + + } + + + } +} \ No newline at end of file diff --git a/Identity.cs b/Identity.cs index 9baec62..8cb7870 100644 --- a/Identity.cs +++ b/Identity.cs @@ -35,12 +35,79 @@ namespace RNS public const int FERNET_OVERHEAD = RNS.Cryptography.Fernet.FERNET_OVERHEAD; public const int AES128_BLOCKSIZE = 16; // In bytes - int HASHLENGTH = 256; // In bits + const int HASHLENGTH = 256; // In bits int SIGLENGTH = KEYSIZE; // In bits - int NAME_HASH_LENGTH = 80; - int TRUNCATED_HASHLENGTH = RNS.Reticulum.TRUNCATED_HASHLENGTH; + public const int NAME_HASH_LENGTH = 80; + public static int TRUNCATED_HASHLENGTH = RNS.Reticulum.TRUNCATED_HASHLENGTH; + public byte[] Hash = new byte[HASHLENGTH / 8]; - //known_destinations = {} //toDo FixMe + List known_destinations = new List(); + + Identity(bool create_keys = false) + { + string prv = ""; + byte[] prv_bytes = null; + string sig_prv = ""; + byte[] sig_prv_bytes = null; + + string pub = ""; + byte[] pub_bytes = null; + string sig_pub = ""; + byte[] sig_pub_bytes = null; + + //Hash = null; + //HexHash = null; + + if (create_keys) + { + //Create_Keys(); + } + } + + public string PrettyName + { + get { return RNS.Util.PrettyHexRep(Hash); } + } + + public static byte[] FullHash(byte[] data) + { + // Functional, but placing under RNS.Crypto for generalization + // return System.Security.Cryptography.SHA256.HashData(data); + + return RNS.Cryptography.SHA256(data); + } + + public static byte[] Truncated_Hash(byte[] data) + { + byte[] hash = new byte[TRUNCATED_HASHLENGTH/8]; + byte[] fullhash = FullHash(data); + Array.Copy(fullhash, fullhash.Length-(TRUNCATED_HASHLENGTH/8), hash, 0, TRUNCATED_HASHLENGTH/8); + return hash; + + } + + public static byte[] Get_Random_Hash() + { + + System.Security.Cryptography.RandomNumberGenerator random = System.Security.Cryptography.RandomNumberGenerator.Create(); + byte[] hashseed = new byte[TRUNCATED_HASHLENGTH/8]; + random.GetBytes(hashseed); + return Truncated_Hash(hashseed); + + } + + public static void Persist_Data() + { + //if (!RNS.Transport.owner.isConnectedToSharedInstance) + //{ + // Save_Known_Destinations(); + //} + } + + public static void Exit_Handler() + { + Persist_Data(); + } } } \ No newline at end of file diff --git a/Program.cs b/Program.cs index f900798..26681ba 100644 --- a/Program.cs +++ b/Program.cs @@ -61,7 +61,7 @@ byte[] sideband_fb_data = new byte[] { }; //Modify the COM port and broadcast specifications as required -RNode.RNodeInterface R = new RNode.RNodeInterface(new RNS.Transport(), "RNS Test Node", "COM6",915000000,125000,2,8,5,false); +RNS.Interfaces.RNodeInterface R = new RNS.Interfaces.RNodeInterface(new RNS.Transport(), "RNS Test Node", "COM10",915000000,125000,2,8,5,false); void TestCallback(object sender, RNS.Interface.CallbackArgs e) { @@ -73,6 +73,79 @@ void TestCallback(object sender, RNS.Interface.CallbackArgs e) Console.WriteLine(); } +//More test stuff +byte[] FakeHash = new byte[] { 0x3c, 0x12, 0xdb, 0xa8, 0x95, 0xc1, 0x8f, 0xe8, 0x99, 0x7e, 0xbb, 0x55, 0x6d, 0xe9, 0xd9, 0x51 }; +//Console.WriteLine(RNS.Util.PrettyHexRep(FakeHash)); +//FakeHash = RNS.Destination.Hash(null, "NotMyProblem", new List() { "Fish"}); +FakeHash = RNS.Identity.FullHash(new byte[] { 0xde, 0xad, 0xbe, 0xef }); +Console.WriteLine("Deadbeef full hash: " + RNS.Util.PrettyHexRep(FakeHash)); +Console.WriteLine("Expected: <5F78C33274E43FA9DE5659265C1D917E25C03722DCB0B8D27DB8D5FEAA813953>"); +FakeHash = RNS.Identity.Truncated_Hash(new byte[] { 0xde, 0xad, 0xbe, 0xef }); +Console.WriteLine("Deadbeef truncated hash: " + RNS.Util.PrettyHexRep(FakeHash)); +Console.WriteLine("Expected: <25C03722DCB0B8D27DB8D5FEAA813953>"); +Console.WriteLine("Random Hash: "+RNS.Util.PrettyHexRep(RNS.Identity.Get_Random_Hash())); +Console.WriteLine("Random Hash: " + RNS.Util.PrettyHexRep(RNS.Identity.Get_Random_Hash())); +Console.WriteLine("Random Hash: " + RNS.Util.PrettyHexRep(RNS.Identity.Get_Random_Hash())); +//RNS.Cryptography.TestMe(); +byte[] TestBytes = System.Text.Encoding.ASCII.GetBytes("This is my test string. If it displays properly, this crypto check has passed."); +byte[] TestKey = new byte[] { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef }; +byte[] TestIV = new byte[] { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef }; +Console.WriteLine("IV Length: " + TestIV.Length.ToString()); +byte[] TestReturn = RNS.Cryptography.AES_128_CBC.encrypt(TestBytes,TestKey,TestIV); +TestReturn = RNS.Cryptography.AES_128_CBC.decrypt(TestReturn, TestKey, TestIV); +Console.WriteLine("AES Test:"); +Console.WriteLine(System.Text.Encoding.ASCII.GetString(TestReturn)); +Console.WriteLine("Signature Test:"); +RNS.Cryptography.Ed25519PrivateKey SK = new RNS.Cryptography.Ed25519PrivateKey(); +RNS.Cryptography.Ed25519PublicKey VK = new RNS.Cryptography.Ed25519PublicKey(SK.public_key()); +TestReturn = SK.sign(TestBytes); + +if (VK.verify(TestReturn, TestBytes)) +{ + Console.WriteLine("Ed25519 signature valid"); + +} +else +{ + + Console.WriteLine("Ed25519 signature invalid"); + +} + +//rfc4231 test vectors +byte[] MACKey = new byte[] { 0x4a, 0x65, 0x66, 0x65 }; +//byte[] MACData = new byte[] { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 }; + +byte[] MACData = new byte[] { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x3f }; + +string ExpectedMAC256 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"; +string ExpectedMAC512 = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"; + +Console.WriteLine(System.Text.ASCIIEncoding.ASCII.GetString(MACData)); + +byte[] MAC = RNS.Cryptography.HMAC256(MACKey, MACData); +Console.WriteLine("Testing HMAC - SHA256"); +Console.Write(" "); +foreach(byte b in MAC) +{ + Console.Write(b.ToString("X2")); +} +Console.WriteLine(""); +Console.WriteLine("Expected: " + ExpectedMAC256); + +MAC = RNS.Cryptography.HMAC512(MACKey, MACData); +Console.WriteLine("Testing HMAC - SHA512"); +Console.Write(" "); +foreach (byte b in MAC) +{ + Console.Write(b.ToString("X2")); +} +Console.WriteLine(""); +Console.WriteLine("Expected: " + ExpectedMAC512); + +//end test + + R.Callbacks.CallbackEventHandler += TestCallback; Console.WriteLine("Return to send packet. Q then return to quit."); diff --git a/RNodeInterface.csproj b/RNodeInterface.csproj new file mode 100644 index 0000000..2dffdd4 --- /dev/null +++ b/RNodeInterface.csproj @@ -0,0 +1,16 @@ + + + + Exe + net6.0 + enable + enable + True + + + + + + + + diff --git a/Reticulum.cs b/Reticulum.cs index 10653e9..aeaf876 100644 --- a/Reticulum.cs +++ b/Reticulum.cs @@ -199,4 +199,35 @@ namespace RNS } + + public static class Util + { + public static string PrettyHexRep(byte[] data) + { + string buffer = "<"; + foreach(byte b in data) + { + buffer += b.ToString("X2"); + } + buffer += ">"; + return buffer; + } + + public static byte[] UTF8_to_Bytes(string Input) + { + return System.Text.Encoding.UTF8.GetBytes(Input); + } + + public static byte[] TruncateHash(byte[] data, int length, bool fromEnd = true) + { + byte[] hash = new byte[length]; + int index = 0; + if (fromEnd) + { + index = data.Length - length; + } + Array.Copy(data, index, hash, 0, length); + return hash; + } + } } \ No newline at end of file