< Summary

Information
Class: Renci.SshNet.Security.Chaos.NaCl.CryptoBytes
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Security\Chaos.NaCl\CryptoBytes.cs
Line coverage
20%
Covered lines: 23
Uncovered lines: 91
Coverable lines: 114
Total lines: 190
Line coverage: 20.1%
Branch coverage
17%
Covered branches: 10
Total branches: 58
Branch coverage: 17.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Security\Chaos.NaCl\CryptoBytes.cs

#LineLine coverage
 1using System;
 2using System.Runtime.CompilerServices;
 3
 4namespace Renci.SshNet.Security.Chaos.NaCl
 5{
 6    internal static class CryptoBytes
 7    {
 8        internal static bool ConstantTimeEquals(byte[] x, byte[] y)
 09        {
 010            if (x == null)
 011                throw new ArgumentNullException("x");
 012            if (y == null)
 013                throw new ArgumentNullException("y");
 014            if (x.Length != y.Length)
 015                throw new ArgumentException("x.Length must equal y.Length");
 016            return InternalConstantTimeEquals(x, 0, y, 0, x.Length) != 0;
 017        }
 18
 19        internal static bool ConstantTimeEquals(ArraySegment<byte> x, ArraySegment<byte> y)
 020        {
 021            if (x.Array == null)
 022                throw new ArgumentNullException("x.Array");
 023            if (y.Array == null)
 024                throw new ArgumentNullException("y.Array");
 025            if (x.Count != y.Count)
 026                throw new ArgumentException("x.Count must equal y.Count");
 27
 028            return InternalConstantTimeEquals(x.Array, x.Offset, y.Array, y.Offset, x.Count) != 0;
 029        }
 30
 31        internal static bool ConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length)
 332        {
 333            if (x == null)
 034                throw new ArgumentNullException("x");
 335            if (xOffset < 0)
 036                throw new ArgumentOutOfRangeException("xOffset", "xOffset < 0");
 337            if (y == null)
 038                throw new ArgumentNullException("y");
 339            if (yOffset < 0)
 040                throw new ArgumentOutOfRangeException("yOffset", "yOffset < 0");
 341            if (length < 0)
 042                throw new ArgumentOutOfRangeException("length", "length < 0");
 343            if (x.Length - xOffset < length)
 044                throw new ArgumentException("xOffset + length > x.Length");
 345            if (y.Length - yOffset < length)
 046                throw new ArgumentException("yOffset + length > y.Length");
 47
 348            return InternalConstantTimeEquals(x, xOffset, y, yOffset, length) != 0;
 349        }
 50
 51        private static uint InternalConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length)
 352        {
 353            int differentbits = 0;
 19854            for (int i = 0; i < length; i++)
 9655                differentbits |= x[xOffset + i] ^ y[yOffset + i];
 356            return (1 & (unchecked((uint)differentbits - 1) >> 8));
 357        }
 58
 59        internal static void Wipe(byte[] data)
 235860        {
 235861            if (data == null)
 062                throw new ArgumentNullException("data");
 235863            InternalWipe(data, 0, data.Length);
 235864        }
 65
 66        internal static void Wipe(byte[] data, int offset, int count)
 067        {
 068            if (data == null)
 069                throw new ArgumentNullException("data");
 070            if (offset < 0)
 071                throw new ArgumentOutOfRangeException("offset");
 072            if (count < 0)
 073                throw new ArgumentOutOfRangeException("count", "Requires count >= 0");
 074            if ((uint)offset + (uint)count > (uint)data.Length)
 075                throw new ArgumentException("Requires offset + count <= data.Length");
 076            InternalWipe(data, offset, count);
 077        }
 78
 79        internal static void Wipe(ArraySegment<byte> data)
 080        {
 081            if (data.Array == null)
 082                throw new ArgumentNullException("data.Array");
 083            InternalWipe(data.Array, data.Offset, data.Count);
 084        }
 85
 86        // Secure wiping is hard
 87        // * the GC can move around and copy memory
 88        //   Perhaps this can be avoided by using unmanaged memory or by fixing the position of the array in memory
 89        // * Swap files and error dumps can contain secret information
 90        //   It seems possible to lock memory in RAM, no idea about error dumps
 91        // * Compiler could optimize out the wiping if it knows that data won't be read back
 92        //   I hope this is enough, suppressing inlining
 93        //   but perhaps `RtlSecureZeroMemory` is needed
 94        [MethodImpl(MethodImplOptions.NoInlining)]
 95        internal static void InternalWipe(byte[] data, int offset, int count)
 237396        {
 237397            Array.Clear(data, offset, count);
 237398        }
 99
 100        // shallow wipe of structs
 101        [MethodImpl(MethodImplOptions.NoInlining)]
 102        internal static void InternalWipe<T>(ref T data)
 103            where T : struct
 0104        {
 0105            data = default(T);
 0106        }
 107
 108        // constant time hex conversion
 109        // see http://stackoverflow.com/a/14333437/445517
 110        //
 111        // An explanation of the weird bit fiddling:
 112        //
 113        // 1. `bytes[i] >> 4` extracts the high nibble of a byte
 114        //   `bytes[i] & 0xF` extracts the low nibble of a byte
 115        // 2. `b - 10`
 116        //    is `< 0` for values `b < 10`, which will become a decimal digit
 117        //    is `>= 0` for values `b > 10`, which will become a letter from `A` to `F`.
 118        // 3. Using `i >> 31` on a signed 32 bit integer extracts the sign, thanks to sign extension.
 119        //    It will be `-1` for `i < 0` and `0` for `i >= 0`.
 120        // 4. Combining 2) and 3), shows that `(b-10)>>31` will be `0` for letters and `-1` for digits.
 121        // 5. Looking at the case for letters, the last summand becomes `0`, and `b` is in the range 10 to 15. We want t
 122        // 6. Looking at the case for digits, we want to adapt the last summand so it maps `b` from the range 0 to 9 to 
 123        // Now we could just multiply with 7. But since -1 is represented by all bits being 1, we can instead use `& -7`
 124        //
 125        // Some further considerations:
 126        //
 127        // * I didn't use a second loop variable to index into `c`, since measurement shows that calculating it from `i`
 128        // * Using exactly `i < bytes.Length` as upper bound of the loop allows the JITter to eliminate bounds checks on
 129        // * Making `b` an int avoids unnecessary conversions from and to byte.
 130        internal static string ToHexStringUpper(byte[] data)
 0131        {
 0132            if (data == null)
 0133                return null;
 0134            char[] c = new char[data.Length * 2];
 135            int b;
 0136            for (int i = 0; i < data.Length; i++)
 0137            {
 0138                b = data[i] >> 4;
 0139                c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7));
 0140                b = data[i] & 0xF;
 0141                c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7));
 0142            }
 0143            return new string(c);
 0144        }
 145
 146        // Explanation is similar to ToHexStringUpper
 147        // constant 55 -> 87 and -7 -> -39 to compensate for the offset 32 between lowercase and uppercase letters
 148        internal static string ToHexStringLower(byte[] data)
 0149        {
 0150            if (data == null)
 0151                return null;
 0152            char[] c = new char[data.Length * 2];
 153            int b;
 0154            for (int i = 0; i < data.Length; i++)
 0155            {
 0156                b = data[i] >> 4;
 0157                c[i * 2] = (char)(87 + b + (((b - 10) >> 31) & -39));
 0158                b = data[i] & 0xF;
 0159                c[i * 2 + 1] = (char)(87 + b + (((b - 10) >> 31) & -39));
 0160            }
 0161            return new string(c);
 0162        }
 163
 164        internal static byte[] FromHexString(string hexString)
 0165        {
 0166            if (hexString == null)
 0167                return null;
 0168            if (hexString.Length % 2 != 0)
 0169                throw new FormatException("The hex string is invalid because it has an odd length");
 0170            var result = new byte[hexString.Length / 2];
 0171            for (int i = 0; i < result.Length; i++)
 0172                result[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
 0173            return result;
 0174        }
 175
 176        internal static string ToBase64String(byte[] data)
 0177        {
 0178            if (data == null)
 0179                return null;
 0180            return Convert.ToBase64String(data);
 0181        }
 182
 183        internal static byte[] FromBase64String(string s)
 0184        {
 0185            if (s == null)
 0186                return null;
 0187            return Convert.FromBase64String(s);
 0188        }
 189    }
 190}