三、大整数运算
在复杂密码算法的复杂实现中,基于大整数的运算是难以实现的任务。大多数限制是由硬件设备(例如处理器、RAM 存储器)或编程语言造成的。
在 C# 中,整数值表示为 32 位。在这 32 位中,只有 31 位用于表示正整数运算。在密码学中,建议我们处理 20 亿的数字,2 ^ 109。
大多数编译器,比如 GCC,都提供了long long type。这提供了拥有大约 9 万亿分之一,9 ^ 1018的整数的能力。
以下示例显示了 RSA (Rivest-Shamir-Adleman)公钥加密密码系统,它需要大约 300 位数字。当处理真实事件及其概率时,在大多数情况下,计算将涉及非常大的数字。对于 C# 来说,获得主要结果可能是准确的,但是与其他困难的计算相比,我们将拥有非常大的数量。
考虑下面这个众所周知的例子:计算只有一张彩票的彩票中头奖的几率。组合数是 50 取 6 一次,“50 选 6”是
)。计算过程得到的数字是 15.890.700,所以在这种情况下,获胜的几率是 1/15,890,700。如果我们用 C# 编程语言来实现,15890700 这个数字可以非常容易地表示出来。这可能会很棘手,然后我们可能会在实施 50 的过程中陷入幼稚!(使用 Windows 中的计算器计算),即 3.041409320171e+64 或
30,414,093,201,713,378,043,612,608,166,064,768,844,377,641,568,960,512,000,000,000,000
用 C # 来表示这个数字几乎是不可能的,即使我们使用的是 64 位平台。
在接下来的几节中,我们将讨论一些可用于大整数算术运算的算法。请注意,由于我们使用的是加密算法和认证协议,这可能很难实现,因为我们处理的是大整数。下面我们将展示如何处理大数的一步一步的方法。
图 3-1 和清单 3-1 显示了大整数功能的完整实现。

图 3-1
计算 GCD 的 BigInteger 示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;
namespace BigIntExample
{
class Program
{
static void Main(string[] args)
{
//** compute bigger factorials
Console.Write("Enter a factorial to be computed (>100) : ");
int factorial = Convert.ToInt32(Console.ReadLine());
ComputeBigFactorial(factorial);
Console.WriteLine(Environment.NewLine);
//** compute sum of the first
//** even number to get Fibonacci Series
Console.WriteLine("Enter a number for the " +
"first even set (>= 100 000) : ");
int evenNumberFib = Convert.ToInt32(Console.ReadLine());
SumOfFirstOneLacEvenFibonacciSeries(evenNumberFib);
Console.WriteLine(Environment.NewLine);
//** computing greatest common divisor
Console.WriteLine("GCD = " +
BigInteger.GreatestCommonDivisor(12, 24));
//** comparing purpose
BigInteger value1 = 10;
BigInteger value2 = 100;
switch (BigInteger.Compare(value1, value2))
{
case -1:
Console.WriteLine("{0} < {1}", value1, value2);
break;
case 0:
Console.WriteLine("{0} = {1}", value1, value2);
break;
case 1:
Console.WriteLine("{0} > {1}", value1, value2);
break;
}
//** parsing
Console.WriteLine(BigInteger.Parse("10000000"));
//** obtaining negation
Console.WriteLine(BigInteger.Negate(-100));
//** returning the sign
Console.WriteLine(BigInteger.Negate(-1).Sign);
//** returning conversion (int to BigInterger)
int i = 100;
BigInteger bI = (BigInteger)i;
Console.WriteLine(bI);
//** returning conversion (BigInteger to int)
BigInteger BI = 200;
int j = (int)BI;
Console.WriteLine(j);
Console.Read();
}
//** computing the factorials
private static void ComputeBigFactorial(int factorial)
{
BigInteger number = factorial;
BigInteger fact = 1;
for (; number-- > 0; ) fact *= number+1;
Console.WriteLine(fact);
}
//** computing the first even fibonacci series
private static void SumOfFirstOneLacEvenFibonacciSeries(
int evenNumberFib)
{
int limit = evenNumberFib;
BigInteger value1 = 1;
BigInteger value2 = 2;
BigInteger theSum = 0;
BigInteger even_sum = value1 + value2;
for (int i = 2; i < limit; i++)
{
theSum = value1 + value2;
if (theSum % 2 == 0) even_sum += theSum;
value1 = value2;
value2 = theSum;
}
Console.WriteLine(even_sum);
}
}
}
Listing 3-1BigInteger Implementation
首先,我们需要在一个大整数中使用不同的计算来转换一个标准整数。为了做到这一点,我们编写了一个名为transformIntToBigInt(A, 123)的函数。该功能的目的是将 A 初始化为A[0]=3、A[1]=2、A[2]=1,并将剩余位置归零为A[3,...N-1]。清单 3-2 展示了如何通过使用一个简单的 C# 实现来完成上面的语句。在这个例子中,基址代表位符号。
public void TransformIntToBigInt(int BigNo[], int number)
{
Int k;
//** start indexing with 0 position
k = 0;
// if we still have something left
// within the number, continue
while (number) {
// insert the digit that is least significant
// into BigNo[k] number
BigNo[k++] = number % bitSign;
// we don't need the least significant bit
number /= BASE;
}
// complete the remain of the array with zeroes
while (k < N)
BigNo[k++] = 0;
}
Listing 3-2Transforming a Standard Integer Using Different Computations in a Big Integer
来自清单 3-2 的算法有 O ( N )空间和时间。
让我们继续我们的旅程,看看给一个大 int 加 1 的可能性。这个过程非常有用,在密码学中经常使用。清单 3-3 中的函数比总加法简单得多。
public void increment (int BigNo [])
{
Int i;
// start indexing with least significant digit
i = 0;
while (i < N)
{
// increment the digit
BigNo[i]++;
// if it overflows
if (BigNo[i] == BASE)
{
// make it zero and move the index to next
BigNo[i] = 0;
i++;
}
else
// else, we are done!
break;
}
}
Listing 3-3Adding One to a Big Int
清单 3-2 中描述的算法对于可能的最坏情况有 O ( n )(想象一下类似于 99999999999999999999…)和ω(1)表示最佳情况。最好的情况发生在最低有效位没有溢出的时候。
继续,让我们仔细看看两个大整数相加的方法。在这种情况下,我们希望在两个不同的数组中添加两个大整数,BigNo1[0,…,N-1]和 BigNo2[0,…,N-1]。结果将保存在另一个数组 BigNo3[0,…,N-1]中。算法很基础,没什么花哨的。参见清单 3-4 。
public void addition(int BigNo1[], int BigNo2[], int BigNo3[])
{
Int j, overflowCarry, sum;
//** there is no necessary to carry at this moment
carry = 0;
//** move from the least to the most significant digit
for (j=0; j<N; j++)
{
// the digit placed on j'th position of BigNo3[]
// is the sum of j'th digits of
// BigNo1[] and BigNo2[] and including the
// overvflow carry
sum = BigNo1[j] + BigNo2[j] + overflowCarry;
// if the sum will go out of the base then
// we will find ourself in an overflow situation
if (sum >= BASE)
{
carry = 1;
//** making adjustment in such way that
//** the sum will fit within a digit
sum -= BASE;
}
else
//otherwise no carryOverflow
carry = 0;
// use the same sum variable to add the
BigNo3[j] = sum;
}
// once we reached to the end
// we can expect an overflow
if (carry)
printf ("There is an overflow in the addition!\n");
}
Listing 3-4Addition Algorithm
接下来,我们将重点讨论乘法。我们将使用一个基本的方法,将两个大数字相乘, X 和 Y ,将每个 X 数字与每个 Y 数字相乘,因此输出成为一个组成元素。每个新数字的绩效结果向左移动。我们实现的函数,multiplyingOneDigit,将把一个整数和一个数字相乘。结果将被放入一个新的大整数中。我们还将展示另一个函数,left_shifting,,它将数字向左移动一定数量的空格。用 b i 相乘,其中 b 为基数, i 表示空格数。清单 3-5 展示了乘法算法。
public void multiply (int BigInt1[], int BigInt2[],
int BigInt3[])
{
Int x, y, P[integer_length];
// BigInt3 will store the sum of
// partial products. The value is set to 0.
buildInteger (BigInt3, 0);
// for each digit in BigInt1
for (x=0; x<length_of_integer; x++)
{
// multiply BigInt2 with digit [x]
multiplyOneDigit(BigInt2, P, BigInt1[x]);
// left shifting the partial product with i bytes
leftShifting(P, x);
// add the output result to the current sum
addResult(BigInt3, P, BigInt3);
}
}
Listing 3-5Multiplication Algorithim
在清单 3-6 中,我们将检查一个乘以一位数的函数。
public void multiplyUsingOneDigit (int BigOne1[], int
BigOne2[], int number)
{
Int k, carryOverflow;
// we don't need extraflow for the moment
carryOverflow = 0;
// for each digit, starting with least significant...
for (k=0; k<N; k++)
{
// multiply the digit with the number,
// save the result in BigOne2
BigOne2[k] = number * BigOne1[k];
// set extra overflow that is taking
// place starting with the last digit
BigOne2[k] += carryOverflow;
// product is too big to fit in a digit
if (BigOne2[k] >= BASE)
{
//** handle the overflow
carryOverflow = B[k] / BASE;
BigOne2[k] %= BASE;
}
else
// no overflow
carryOverflow = 0;
}
if (carryOverflow)
printf ("During the multiplication
we experienced an overflow!\n");
}
Listing 3-6Multiplying Using a Single Digit
清单 3-7 显示了一个将向左移动特定数量空格的函数操作。
public void leftShifting (int BigInt1[], int number) {
int i;
//start from left to right,
// everything with left n spaces
for (i=N-1; i>= number; i--)
BigInt1[i] = BigInt1[i- number];
// complete the last n digits with zeros
while (i >= 0) BigInt1[i--] = 0;
}
Listing 3-7Shifting to Left a Specific Number of Spaces
在 C# 中使用大整数
.NET Framework 4.0 提供了System.Numerics.BigInteger类,该类基于Microsoft.SolverFoundation.Common.BigInteger。
在设计和开发加密算法时,使用BigInteger非常有用。BigInteger表示不可变类型,用于没有上限或下限的大数。特征BigInteger类的成员是Byte、Int(所有三种表示:16、32 和 64)、SByte和UInt (16、32 和 64)类型。
如何使用 BigInteger 类
创建BigInteger对象有几种方法:
-
使用
new关键字并将任何浮点值作为参数提供给类的构造函数。参见清单 3-8 。 -
通过声明一个赋值的
BigInteger变量。参见清单 3-9 。
BigInteger publicCryptographyKeyFromDouble
= new BigInteger(131312.3454354353);
Console.WriteLine(publicCryptographyKeyFromDouble);
BigInteger publicCryptographyKeyFromInt64
= new BigInteger(7587643562354635);
Console.WriteLine(publicCryptographyKeyFromInt64);
Listing 3-8Using the new Keyword with BigInteger
- 通过给一个
BigInteger对象分配一个十进制或浮点值。在赋值发生之前,首先需要将值转换为BigInteger。见清单 3-10 。
long publicCryptographyKey = 87432541632312;
BigInteger cryptoPubKey = publicCryptographyKey;
Console.WriteLine(cryptoPubKey);
Listing 3-9Using a Variable with a Value Assigned
BigInteger publicCryptographyKeyFromDoubleValue
= (BigInteger) 423412.3423;
Console.WriteLine(publicCryptographyKeyFromDoubleValue);
BigInteger publicCryptographyFromDecimal
= (BigInteger) 23452.34m;
Console.WriteLine(publicCryptographyFromDecimal);
Listing 3-10Casting to BigIntegers
的大整数库。网
当我们在密码学中处理大整数时,64 位对于数字来说是不够的。这一节将介绍一个名为 RSA 的复杂密码系统的例子,我们将展示一些关于 .NET BigInteger类和其他。网络图书馆。
如上节所示,System.Numerics.BigIntegers是从开始引入的 .NET Framework 4.0,清单 3-11 中的简单示例展示了如何以不同于上面的其他示例的方式使用它。
var publicCryptoKey = BigInteger.Parse("5845829436572843562734865972843659784564");
var privateCryptoKey = BigInteger.Parse("245245452345252342435543527362524323455");
Listing 3-11Using System.Numerics.BigIntegers
任何运算(加、乘、除、差等。)使用publicCryptoKey和privateCryptoKey是可能的。
良好操作规范
GMP [2 是一个命名空间,也称为 Emil,以 Emil Stefanov 命名。它结合了 F# 中 BigInt 的强大功能。这个库的缺点是它只能在 32 位系统上实现。C# 中的例子看起来与使用BigInteger的例子非常相似。参见清单 3-12 。
var publicCryptoKey = BigInt ("5845829436572843562734865972843659784564");
var privateCryptoKey = BigInt ("245245452345252342435543527362524323455");
Listing 3-12Using GMP
MPIR
MPIR [3 可以看做是 GMP [2 的一个分叉。MPIR 的目的是给 Windows 上的编译过程一个任何专业开发人员都想在他们的工具包中拥有的有味道和无痛的过程。
为了在美国使用 MPIR .NET 中,一些包装器是必要的,比如DLLImports或GetProcAddress赋值。一个非常好的可以使用的包装器是 X-MPIR,由来自阿尔吉利布 [4 ]的 Sergey Bochkanov 提出并开发。
清单 3-13 中的代码展示了如何使用 MPIR 开发复杂的密码系统。
var publicKey = mpir.mpz_init_set_str("4657334563546345", 10);
var sessionKey = mpir.mpz_init_set_str("784567354634564", 10);
var exampleOfOperation = mpir.mpz_init();
//** random operations
mpir.mpz_mul(exampleOfOperation, publicKey, sessionKey);
//** Implement the cryptosystem operations as you like **//
//** it is recommended to invoke clear
//** method in order to avoid any memory leakage
mpir.mpz_clear(publicKey);
mpir.mpz_clear(sessionKey);
mpir.mpz_clear(exampleOfOperation);
Listing 3-13Using MPIR
结论
在这一章中,我们介绍了表示大整数的最重要的方面,以及它们是如何设计和实现的。
-
我们分析了处理大数的基本方法的实现的技术方面。
-
我们展示了来自 .NET Framework 4.0 并展示了如何使用
BigInteger对象。 -
我们包括了其他处理大整数的库,比如 GMP 和 MPIR。
文献学
-
BigInteger阶级。网上有:https://docs.microsoft.com/en-us/dotnet/api/system.numerics.biginteger?redirectedfrom=MSDN&view=netcore-3.1。 -
没有限制的 GMP 算法。网上有:
https://gm.plib.org/ 。 -
Mpir.NET。网上有:
http://wezeku.github.io/Mpir.NET/。 -
阿尔格里卜。网上有:
www.alglib.net/。
四、浮点运算
处理大整数可以被视为一种抽象艺术,如果密码系统没有正确实现,整个密码算法或方案可能会导致真正的灾难。本章重点介绍浮点运算及其对密码学的重要性。
为什么是浮点运算?
浮点运算代表了一种特殊形式的运算,我们需要关注它,因为它在密码学领域中的应用,如基于混沌的密码学、同态加密、量子密码术等等。这种类型的算法可以在基于混沌的加密或同态加密中观察到,这将在第 14 和 16 章中介绍。
在使用小实数和非常大的实数的系统中,也可以找到使用浮点数的计算。在他们的计算过程中,过程必须非常快。
浮点变量是一类特殊的变量,可以保存实数,如 4534.0、5.232 或 0.005443。浮动部分意味着小数点会“浮动”
C# 提供不同的浮点数据类型,如float 、 double 、、SByte *。*正如您从 C++或其他具有整数情况的编程语言中所知,该语言没有为这些类型定义任何大小。在现代体系结构中,大多数浮点表示都遵循二进制表示格式的 IEEE 754 标准。按照这个标准,float类型有 4 个字节,double有 8 个字节,long double有 8 个字节(与double相同),也是 80 位(通过填充我们有 12 个字节或 16 个字节)。
当您处理浮点值时,请确保至少有一个小数。这有助于编译器区分浮点数和整数。以下声明示例显示了如何声明和初始化变量:
int a = 4; //** 4 is an integer
double b = 3.0 //** 3.0 represents a floating point (with no
//** suffix – double type by default)
显示浮点数
让我们考虑清单 4-1 中的例子。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CommonFloatNumbers
{
class Program
{
static void Main(string[] args)
{
float cryptoKey1 = 5.0f;
float cryptoKey2 = 6.7f;
Console.WriteLine(cryptoKey1);
Console.WriteLine(cryptoKey2);
Console.ReadKey();
}
}
}
Listing 4-1Displaying Common Float Numbers
输出如图 4-1 所示。

图 4-1
普通浮点数的输出
通过查看程序的输出,您可以发现在第一种情况下,输出是 5 个字节,但是源代码显示的是 5.0。这是因为小数部分等于 0。在第二种情况下,打印的数字与源代码中的数字相同。在第三种情况下,数字使用科学记数法显示,这是加密算法的重要资产。
浮点的范围
让我们来看看 IEEE 754 表示,并考虑以下尺寸及其范围和精度。见表 4-1 。
表 4-1
IEEE 754 标准表示法
|大小
|
范围
|
精确
|
| — | — | — |
| 4 字节 | 1.18×10—38到 3.4 × 10 38 | 6-9 个最重要的数字。通常在 7 位数左右。 |
| 8 字节 | 2.23×10—308到 1.80 × 10 308 | 15-18 个最重要的数字。通常在 16 位数左右。 |
| 80 位(通常使用 12 或 16 字节) | 3.36×10—4932到 1.18 × 10 4932 | 18-21 个最重要的数字 |
| 16 字节 | 3.36×10—4932到 1.18 × 10 4932 | 33-36 位最重要的数字 |
在当今的处理器上,80 位浮点是用 12 或 16 字节实现的。处理器能够管理这种大小是很常见的。
浮点精度
让我们考虑下面这个用分数表示的例子:
)。十进制表示是 0.3333333…无穷大 3 s 。
使用一台机器,需要无限的内存来存储无限长的数。这种内存限制会将浮点数的存储限制为特定数量的有效数字。浮点数及其精度将决定在不丢失任何知识的情况下可以表达多少有效数字。如果我们确实在密码学中产生了一个浮点数,清单 4-2 中的例子展示了如何将值截断为六位数。输出见图 4-2 。

图 4-2
浮点精度的输出
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FloatingValues
{
class Program
{
static void Main(string[] args)
{
float cryptoKey1 = 7.56756767f;
float cryptoKey2 = 765.657667f;
float cryptoKey3 = 345543.564f;
float cryptoKey4 = 9976544.43f;
float cryptoKey5 = 0.00043534345f;
Console.WriteLine("cryptoKey1 = " + cryptoKey1);
Console.WriteLine("cryptoKey2 = " + cryptoKey2);
Console.WriteLine("cryptoKey3 = " + cryptoKey3);
Console.WriteLine("cryptoKey4 = " + cryptoKey4);
Console.WriteLine("cryptoKey5 = " + cryptoKey5);
}
}
}
Listing 4-2Representation of Floating Point Precision
请注意,上面的每个案例只有六个重要数字。
通常,根据所使用的编译器,指数将在最小位数内填充。指数的位数取决于所使用的编译器,例如 Visual Studio 使用 3,而其他编译器使用 2(使用符合 C99 的指令和标准实现)。表示浮点数的位数及其精度取决于大小和存储的特定值。float值的定义精度为 6 和 9 位数字,大多数值至少有 7 位有效数字。double值以 15 位和 18 位精度表示。Long double值至少由 15 或 33 位有效数字表示,这取决于字节的占用情况。
浮点运算的下一级
在第十四章中,我们将介绍一种复杂类型的加密。同态加密代表一种特殊类型的加密,被用作保护隐私的专业技术。这项技术将存储和计算外包出去。这种加密方式允许数据被加密并外包给商业(或公共)环境,目的是处理它们。所有这些过程都是在数据加密时发生的。同态加密源自带错误的环学习(见第十五章),与私有集合交集 [1 ]相关。
继续我们的复杂密码系统之旅,浮点表示法代表了加密/解密机制的热点,它通过寻找适当的方法来近似一个实数,从而支持范围和精度之间的折衷。
如上所述,浮点术语表示数字的小数点可以浮动。总之,它可以设置在数字的重要数字内的任何位置。更具体地说,当我们着眼于复杂的密码体制时,比如同态加密,一个浮点数一个可以表示为四个整数,所以在
)
n 为基数, f 为指数, j 为精度, d 为重要或有效位。重要性或有效位 d 必须满足以下关系:
)
从 with.NET 框架 4.0 开始,有一个庞大的浮点操作例程集 [8 ]。从 C# 8.0 开始,这些基本函数用于基于与浮点数相关的简单数学计算来工作和完成任务,这是普通编程和加密(低级和简单的概念)所必需的。对于高级加密算法,该函数的能力和对大浮点数的要求非常有限,并且它们不能为密码学家提供合适的设备。
下面我们将研究 IEEE 处理浮点的标准化和实现函数。与上面已经给出的应用相比,这个应用稍微复杂一些。也就是说,应用的结构包含三个类:IEEEStandardization.cs(参见清单 4-3 )、FloatingPoint.cs(参见清单 4-4 )和Program.cs(参见清单 4-5 )。下面的应用是根据 IEEE 标准开发的,展示了如何以较少的努力实现主要操作。
在清单 4-3 中,我们可以看到处理浮点值时返回的值的主要类型。这些值表示为枚举类型,包含从规范化到反规范化的值、非数字字符和符号值。在清单 4-4 中,我们实现了获取双二进制表示部分的方法、IEEE-754 标准操作的实现以及单精度位转换的方法。在清单 4-5 中,我们将所有的方法合并到一个主程序中,并让它工作,展示了我们如何使用不同的方法来计算浮点值、乘法结果和减法。见图 4-3 。

图 4-3
基于 IEEE 标准化的复杂浮点运算
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FloatingPointArithmetic
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("FLOATING POINT ARITHMETIC " +
"by Marius Iulian MIHAILESCU " +
"and Stefania Loredana NITA \n");
Console.WriteLine("\t\tDifferent ways of computing 1/105.");
Console.WriteLine("\t\tMultiply the output with 105 and subtract 1");
Console.WriteLine("\t\tWe will get an error.");
double d = 1 / 105.0;
float s = 1 / 105.0F;
Console.WriteLine("\t\t\tUsing double: {0} * " +
"105 - 1 = {1} < 0!", d, d * 105.0 - 1.0);
Console.WriteLine("\t\t\tUsing single: {0} * " +
"105 - 1 = {1} > 0!", s, s * 105.0 - 1.0);
Console.WriteLine();
Console.WriteLine("\t\tComputing a chaos-based " +
"value for cryptography purpose.");
float chaotic_value = 4.99F * 17;
Console.WriteLine("\t\t\tThe chaotic value is " +
"{0}.", chaotic_value);
Console.WriteLine();
Console.WriteLine("\t\tAnother example of chaotic " +
"value for which we need the integer part.");
int another_chaotic_value = (int)(100 * (1 - 0.1F));
Console.WriteLine("\t\t\tAnother chaotic value is {0}.",
another_chaotic_value);
Console.WriteLine();
Console.WriteLine("\t\tFor cryptography is " +
"important to have an implementation " +
"for IEEE-754");
double[] double_values = new double[] { 0, -1 /
Double.PositiveInfinity, 1, -1,
//Math.PI,
//Math.Exp(20),
//Math.Exp(-20),
//Double.PositiveInfinity,
//Double.NegativeInfinity,
//Double.NaN,
//Double.Epsilon,
// -Double.Epsilon,
//10 / Double.MaxValue
};
for (int i = 0; i < double_values.Length; i++)
{
Console.WriteLine("\t\t\tIEEE-754 Value Type({0}) = {1}",
double_values[i],
FloatingPoint.Class(double_values[i]));
Console.WriteLine("\t\t\t{0,19:E8}{1,19:E8}{2,19}{3,19}",
FloatingPoint.ComputerNextValue(double_values[i],
Double.PositiveInfinity) - double_values[i],
FloatingPoint.ComputerNextValue(double_values[i],
Double.NegativeInfinity) - double_values[i],
FloatingPoint.ComputingLogB(double_values[i]),
FloatingPoint.ReuturnSignificantMantissa(double_values[i]));
}
Console.ReadLine();
}
}
}
Listing 4-5Main Program Implementation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FloatingPointArithmetic
{
public sealed class FloatingPoint
{
#region variable and instances
// the constructor of the class
private FloatingPoint() { }
// the value for conversion is 2⁶⁰
// 2⁶⁰ = 1,152,921,504,606,846,976 (decimal base)
// 2⁶⁰ = 10 00 00 00 00 00 00 00 (hex bytes)
// 8 * 2⁶⁰ = 2 * 1,152,921,504,606,846,976 =
// = 2,305,843,009,213,693,952
// we will use "unchecked" for supressing overflow-checking
// for integral-type arithmetic operations and conversions
private static readonly double UnfavorableNegativeValue
= BitConverter.Int64BitsToDouble(unchecked(8 * 0x1000000000000000));
// constants
private const Double minimum_double = 4.94065645841247e-324;
// 0x7FFFFFFFFFFFFFFF = 9,223,372,036,854,775,807 (decimal)
private const long mask_sign_value = -1 - 0x7FFFFFFFFFFFFFFF;
private const long clear_mask_sign = 0x7FFFFFFFFFFFFFFF;
private const long signficand_mask = 0xFFFFFFFFFFFFF;
private const long clearing_significand_mask = mask_sign_value | significand_exponent_mask;
private const long significand_exponent_mask = 0x7FF0000000000000;
private const long clearing_exponent_mask = mask_sign_value | signficand_mask;
private const int deviation = 1023;
private const int significant_bits = 52;
#endregion
#region Methods for getting parts of a double's binary representation.
// the method return the significand of double value
public static long ReuturnSignificantMantissa(double value)
{
return BitConverter.DoubleToInt64Bits(value)
& signficand_mask;
}
// the method will return the signicand
// for a floating-point number
public static double ReturnSignficantForFloatingPoint(double value)
{
if (Double.IsNaN(value)) return value;
if (Double.IsInfinity(value)) return value;
// computing the exponent using the deviation
int exponentValue = ComputeDeviationExponent(value);
long significand = ReuturnSignificantMantissa(value);
// number 0 and denormalization
// values has to be treated separetely
if (exponentValue == 0)
{
// if the significand is equal
// with we will return 0
if (significand == 0)
return 0.0;
// otherwise we will shit the significand to the left
// until significand will be 53 bits long
while (significand < signficand_mask)
significand <<= 1;
// truncate the leading bit
significand &= signficand_mask;
}
return BitConverter.Int64BitsToDouble
((deviation << 52) + significand);
}
// The function will return a non-deviation
// exponent for a floating-point value.
// The non-deviation is computed through
// substracting the deviation from deviated exponent.
public static int NonDeviationExponent(double value)
{
return (int)((BitConverter.DoubleToInt64Bits(value)
& significand_exponent_mask) >> 52) - deviation;
}
// The function will return the
// deviation exponnent for a
// floating-point value.
// The returned value is obtained
// and computed directly from
// and within binary representation of "value"
public static int ComputeDeviationExponent(double value)
{
return (int)((BitConverter.DoubleToInt64Bits(value)
& significand_exponent_mask) >> 52);
}
// The function returns the bit sign
// of a value. The bit sign is obtained
// directly from the binary representation
// of the value
public static int SignBit(double value)
{
return ((BitConverter.DoubleToInt64Bits(value)
& mask_sign_value) != 0) ? 1 : 0;
}
#endregion
#region Below contains the implementation of the IEEE-754
// The class represents the implementation
// of IEEE-754 floating-point
// References:
// https://www.geeksforgeeks.org/ieee-standard-754-floating-point-numbers/
public static IEEEStandardization Class
(double value)
{
long bits_value_representation =
BitConverter.DoubleToInt64Bits(value);
bool positive_value = (bits_value_representation >= 0);
bits_value_representation =
bits_value_representation & clear_mask_sign;
// case when we have a overflow
// for Not-A-Number
if (bits_value_representation
>= 0x7ff0000000000000)
{
// this is case of infinity
if ((bits_value_representation
& signficand_mask) == 0)
{
if (positive_value)
return IEEEStandardization.
Value_Positive_Infinity;
else
return IEEEStandardization.
Value_Negative_Infinity;
}
else
{
if ((bits_value_representation
& signficand_mask)
< 0x8000000000000)
return IEEEStandardization.
Quiet_Not_a_Number;
else
return IEEEStandardization.
Signaling_Not_a_Number;
}
}
// this is happening when we have
// 0 or a denormalization value
else if (bits_value_representation
< 0x0010000000000000)
{
if (bits_value_representation == 0)
{
if (positive_value)
return IEEEStandardization.
Value_Positive_Zero;
else
return IEEEStandardization.
Value_Negative_Zero;
}
else
{
if (positive_value)
return IEEEStandardization.
Denormalization_Positive_Denormalized;
else
return IEEEStandardization.
Denormalization_Negative_Denormalized;
}
}
else
{
if (positive_value)
return IEEEStandardization.
Normalization_Positive_Normalized;
else
return IEEEStandardization.
Normalization_Negative_Normalized;
}
}
// The function copy the
// sign of the number.
// theSizeOfTheValue parameter
// the number for
// which we copy the sign
// theValueOfTheSign parameter
// the value of the number
// for which we do the copy
public static double CopyProcedureForSign
(double theSizeOfTheValue,
double theValueOfTheSign)
{
// we do a bit manipulation
// do the copying process for
//* the first bit for theSizeOfTheValue
// and theValueOfTheSign
return BitConverter.Int64BitsToDouble(
(BitConverter.DoubleToInt64Bits
(theSizeOfTheValue) &
clear_mask_sign)
| (BitConverter.DoubleToInt64Bits
(theValueOfTheSign) &
mask_sign_value));
}
// a boolean function to
// check if the value is
// finite or not
public bool CheckIfIsFinite(double value)
{
// Verify the part represented by the exponent.
// if all the bits are 1, then we
// are dealing with a infinity (not-a-number).
long bits = BitConverter.
DoubleToInt64Bits(value);
return ((bits & significand_exponent_mask)
== significand_exponent_mask);
}
// The function will return the
// non-biased exponent for a value.
public static double ComputingLogB(double value)
{
// Let's deal with the
// important situations.
if (double.IsNaN(value))
return value;
if (double.IsInfinity(value))
return double.PositiveInfinity;
if (value == 0)
return double.NegativeInfinity;
int exponentDeviationValue =
ComputeDeviationExponent(value);
// if we dealing with a denormalization value
// we need to take the right action
if (exponentDeviationValue == 0)
{
exponentDeviationValue = -1074;
// compute the signficand with no sign
long bits = BitConverter.
DoubleToInt64Bits(value) & clear_mask_sign;
// we move on if we finish dealing with situations
// when bits = 0
do
{
bits >>= 1;
exponentDeviationValue++;
}
while (bits > 0);
return exponentDeviationValue;
}
// exponentDeviationValue was significand,
// proceed with subtraction the deviation
// to obtain and compute the non-deviation exponent
return exponentDeviationValue - deviation;
}
// Compute the floating-point
// number for the next number.
// 'from' parameter
// - represent the starting point
// 'to' parameter
// - represent a value that shows
// the direction in which we will
// move in order to identity
// the next value
public static double
ComputerNextValue(double from, double to)
{
// If 'to' is equal with from
// there is no direction to move in,
// so we will compute and get 'from' value
if (from == to)
return from;
// if not-a-number occur will
// be returned by themselves
if (double.IsNaN(from))
return from;
if (double.IsNaN(to))
return to;
// an infinity will be an infinity all time
if (double.IsInfinity(from))
return from;
// deal with 0 situation
if (from == 0)
return (to > 0) ?
minimum_double : -minimum_double;
// For the rest of the
// situation we are dealing.
// With incrementation or
// decrementation the bits value.
// Values for transitions to infinity,
// denormalized values, and to zero are
// managed in this way.
long bits_value = BitConverter.DoubleToInt64Bits(from);
// A xor here avoids nesting conditionals. We have to increment if
// fromValue lies between 0 and toValue.
// XOR operation will help us to
// not taken into consideration
// conditionals.
if ((from > 0) ^ (from > to))
bits_value++;
else
bits_value--;
return BitConverter.
Int64BitsToDouble(bits_value);
}
// the function compute and return
// a value that is powered with 2
public static double Scalb(double number,
int exponent)
{
// Treat special cases first.
if (number == 0 ||
double.IsInfinity(number) ||
double.IsNaN(number))
return number;
if (exponent == 0)
return number;
int computedExponentValue = ComputeDeviationExponent(number);
long significand = ReuturnSignificantMantissa(number);
long getting_sign = ((number > 0) ? 0 : mask_sign_value);
// check if 'number' is denormalized
if (computedExponentValue == 0)
{
if (exponent < 0)
{
// an exponent that is negative
// we will shift the significand
// -exponent bits to the right
significand >>= -exponent;
return BitConverter.
Int64BitsToDouble(getting_sign | significand);
}
else
{
// a number that is positive is
// necessary to be shifted on left
// and this will be done until a
// normalized number is obtained
while (significand <= signficand_mask
&& exponent > 0)
{
significand <<= 1;
exponent--;
}
if (significand > signficand_mask)
exponent++;
// test if we have a overflow
if (exponent > 2 * deviation)
return (number > 0) ?
double.PositiveInfinity
: double.NegativeInfinity;
// the number represents the
// significand exponent for the result
return BitConverter.Int64BitsToDouble(getting_sign
| ((long)exponent << 52) |
(significand & signficand_mask));
}
}
// Once we are reaching here,
// we are aware that 'exoponent'
// is normalized.
// Proceeding with scaling. 'exponent'
// will be the significand exponent for the result
computedExponentValue =
computedExponentValue + exponent;
// verify if we have 0 or denormalization
if (computedExponentValue < 0)
{
significand = ((1L << 52) +
significand) >> (1 -
computedExponentValue);
return BitConverter.
Int64BitsToDouble(getting_sign | significand);
}
// Veirfy if we have an overflow
if (computedExponentValue >
2 * deviation)
return (number > 0) ?
double.PositiveInfinity :
double.NegativeInfinity;
// If we're here, the result is normalized.
long bits = getting_sign |
((long)computedExponentValue << 52) | significand;
return BitConverter.Int64BitsToDouble(bits);
}
// the function computes a value
// wich will point out if the two
// values are unordered
public static bool Unordered(double value1, double value2)
{
return double.IsNaN(value1) || double.IsNaN(value2);
}
#endregion
#region Methods for conversion bit with single-precision
public static unsafe int ConversionSingleToInt32Bits(float val)
{
return *((int*)&val);
}
public static unsafe float ConversionInt32BitsToSingle(int val)
{
return *((float*)&val);
}
#endregion
}
}
Listing 4-4Floating Point Implementation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FloatingPointArithmetic
{
public enum IEEEStandardization
{
// the value is a signaling NaN - not a number
Signaling_Not_a_Number,
// the value is represented by a quiet
// NaN - not a number and non-signaling
Quiet_Not_a_Number,
// the value represents a positive infinity
Value_Positive_Infinity,
// the value represents a negative infinity
Value_Negative_Infinity,
// The value represents a normal and positive number
Normalization_Positive_Normalized,
// The value represents a normal and negative number
Normalization_Negative_Normalized,
// A denormalized positive number
Denormalization_Positive_Denormalized,
// The value is a denormalized negative number
Denormalization_Negative_Denormalized,
// The value represents a positive zero
Value_Positive_Zero,
// the value represents a negative zero
Value_Negative_Zero
}
}
Listing 4-3IEEE Standardization Implementation
结论
在这一章中,我们讨论了浮点数的一般表示,以及如何在复杂的密码系统中实现它们。我们分析了最重要的术语和基本概念,专业人员在计划建立一个开发复杂密码系统的环境时应该了解这些概念。
我们还证明了浮点运算在复杂密码系统中的重要性,如同态加密、基于混沌的密码、基于格的密码或带错误的环学习。
文献学
-
H.来自同态加密的快速私有集合交集。2017 年 ACM SIGSAC 计算机与通信安全会议 CCS '17 论文集,2017。
-
长度 Ducas 和 D. Micciancio,“FHEW:在不到一秒的时间内引导同态加密”,载于密码学进展-Eurocrypt 2015,第 617-640 页,施普林格,柏林,德国,2015 年。
-
南 Halevi 和 V. Shoup,“HElib 中的算法”,载于Crypto14,第 8616 卷,施普林格,海德堡,德国,2014 年。
-
J.坎波斯、p .夏尔马、e .詹图宁、d .巴格利和 l .福马加利,“保护高级维护开发所需数据的网络安全框架的挑战”,载于*《CIRP 进程报*,第 47 卷,第 227 页,2016 年。
-
c .燃烧物品和 j .Ziegler,“Fast Recursive Division”,inresearchreport max-Planck 计算机科学研究所 report MPI-I-98-1-022,max-Planck-Instituto für informatique,sahabrüber,Germany,1998 年。
-
名词(noun 的缩写)Dowlin,R. Gilad-Bachrach,K. Laine,K. Lauter,M. Naehrig 和 J. Wernsing,“使用同态加密进行生物信息学的手册”,载于 IEEE 会议录*,第 105 卷,第 3 期,2017 年。*
-
J.H. Cheon、A. Kim、M. Kim 和 Y. Song,“近似数算术的同态加密”,载于《密码学与信息安全的理论和应用国际会议(ASIA-CRYPT’17)会议录(??),第 409-437 页,中国香港,2017 年 12 月。
-
浮点支持。网上有:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/floating-point-support?view=vs-2019。
五、C# 8.0 的新特性
本章将为密码学和密码分析领域的专业人士讲述 C# 8.0 最重要的特性。有关哪个版本的 C# 用于不同版本的 .NET 框架和 .NET Core,我们推荐 [3 ]的资源。
C# 8.0 对 C# 语言进行了许多改进和增强,其中一些可以成功地用于提高加密和密码分析算法以及安全方案的实现过程的性能。上支持 C# 8.0。网芯 3。x 和。网络标准 2.1。关于 C# 语言版本控制的更多细节可以在这里找到 [1 。这些增强功能包括
-
只读成员
-
默认接口方法
-
模式匹配的改进:开关表达式、属性模式、元组模式、位置模式
-
使用声明
-
静态局部函数
-
一次性参考支柱
-
可为空的引用类型
-
异步流
-
异步一次性
-
指数和范围
-
零合并赋值
-
非托管构造类型
-
嵌套表达式中的 Stackalloc
-
插值逐字字符串的增强
如前所述,本章将描述有助于专业人员提高加密解决方案质量和性能的特性。要探索环境中 C# 8.0 提供的特性的更多细节,建议使用dotnet try工具。要深入探索这些功能,可以成功使用以下解决方案和步骤:
-
访问
https://github.com/dotnet/try/blob/master/README.md#setup,下载安装dotnet try工具。 -
克隆
dotnet/try-samples存储库。可在https://github.com/dotnet/try-samples访问。 -
配置并设置当前目录到
the try-samples存储库的csharp8子目录的路径。 -
调用并运行
dotnet try.
以下部分将涵盖显著提高加密应用的质量和性能的增强功能。
只读成员
新的 readonly 修饰符可以应用于结构的成员。当我们不希望成员的状态被修改时,使用 readonly。我们将通过对成员应用 readonly 而不是 struct 声明来展示更细粒度的表示。
让我们考虑清单 5-1 中的例子,其中我们实现了一个处理 RSA 密码系统的结构。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Listing_5_1___ReadOnlyMembers
{
class Program
{
public int cryptoKey1;
//** initializing and dealing with
public readonly int cryptoKey2 = 25;
public readonly int cryptoKey3;
public Program()
{
// Initialize a readonly instance field
cryptoKey3 = 24;
}
public Program(int value1, int value2, int value3)
{
cryptoKey1 = value1;
cryptoKey2 = value2;
cryptoKey3 = value3;
}
public static void Main()
{
Program cryptoProgram1 = new Program(13, 27, 39);
Console.WriteLine($"Crypto Program 1: crypto_key 1={cryptoProgram1.cryptoKey1}, crypto_key 2={cryptoProgram1.cryptoKey2}, crypto_key 3={cryptoProgram1.cryptoKey3}");
Program cryptoProgram2 = new Program();
cryptoProgram2.cryptoKey1 = 55;
Console.WriteLine($"Crypto Program 2: crypto_key 1={cryptoProgram2.cryptoKey1}, crypto_key 2={cryptoProgram2.cryptoKey2}, crypto_key 3={cryptoProgram2.cryptoKey3}");
Console.ReadKey();
}
}
}
Listing 5-1Applying Readonly for a Cryptographic Purpose
与大多数结构一样,ToString()方法不会更新或修改状态。我们可以通过将readonly放在override关键字的前面来指定发生这种情况(见图 5-1 )。

图 5-1
使用 readonly 关键字的输出示例
模式匹配
在加密模式匹配中,技术可以用在不同的地方,例如解析密码要求、字符串和加密密钥预期。
从 C# 8.0 开始,可以在代码的不同位置使用和实现更多的模式表达式。C# 8.0 的另一个重要增强是递归模式,一个可以在另一个模式表达式结果的输出上使用的模式表达式。让我们考虑清单 5-2 中的例子,我们使用一个enum结构来列出加密算法。
public enum CryptoType
{
RSA,
AES,
TripleDES,
}
Listing 5-2Using an enum struct
如果正在开发的应用包含CryptographicAlgorithm类型的定义,则它是由加密组件(例如加密、解密、计算私钥、计算公钥等)构建的。),所以我们可以使用清单 5-3 中的例子,用一条switch指令将一个CryptographicAlgorithm值转换成CryptoType值。
public static CryptographicAlgorithm
GetCryptoAlgorithm(CryptographicAlgorithm crypto)
{
return crypto.cryptosystemType switch
{
CryptoType.RSA => new RSA(),
CryptoType.AES => new AES(),
CryptoType.TripleDES => new TripleDES(),
_ => throw new ArgumentException(message: "There is no
such cryptographic algorithm ",
paramName: nameof(crypto.cryptosystemType))
};
}
Listing 5-3Using a switch Expression
清单 5-4 给出了一个完整的例子。该列表的结果如图 5-2 所示。

图 5-2
清单 5-4 中代码的结果
using System;
namespace PatternsMatching
{
class Program
{
static void Main(string[] args)
{
CryptographicAlgorithm cryptoAlg =
new CryptographicAlgorithm();
Console.WriteLine("Pick a cryptosystem [1=RSA,
2=AES, 3=TripleDES]");
string type = Console.ReadLine();
CryptoType ct = type switch
{
"1" => CryptoType.RSA,
"2" => CryptoType.AES,
"3" => CryptoType.TripleDES,
_ => throw new ArgumentException(message:
"There is no such option ",
paramName: nameof(type))
};
try
{
cryptoAlg.cryptosystemType = ct;
GetCryptoAlgorithm(cryptoAlg);
Console.ReadKey();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static CryptographicAlgorithm
GetCryptoAlgorithm(
CryptographicAlgorithm crypto)
{
return crypto.cryptosystemType switch
{
CryptoType.RSA => new RSA(),
CryptoType.AES => new AES(),
CryptoType.TripleDES => new TripleDES(),
_ => throw new ArgumentException(message:
"There is no such cryptographic algorithm ",
paramName: nameof(crypto.cryptosystemType))
};
}
}
public enum CryptoType
{
RSA,
AES,
TripleDES,
}
public class CryptographicAlgorithm
{
internal CryptoType cryptosystemType;
public CryptographicAlgorithm()
{
}
}
class RSA : CryptographicAlgorithm
{
public RSA()
{
Console.WriteLine("RSA chosen!");
}
}
class AES : CryptographicAlgorithm
{
public AES()
{
Console.WriteLine("AES chosen!");
}
}
class TripleDES : CryptographicAlgorithm
{
public TripleDES()
{
Console.WriteLine("TripleDES chosen!");
}
}
}
Listing 5-4Chosing an Encryption System Based on switch Expressions
模式
属性模式
使用属性模式的新特性让专业人员能够匹配属于对象的属性。让我们考虑一个电子学习平台作为例子(见清单 5-5 )。我们需要基于CryptographicAlgorithm对象的cryptosystemType属性,使用特定的加密算法对消息进行加密。请注意,这些部分中的示例仅用于演示。
public static int Encrypt(CryptographicAlgorithm crypto, Parameters parameters, int message)
{
return crypto switch
{
{ cryptosystemType: CryptoType.RSA } => (new RSA()).
Encrypt(parameters.n, parameters.e, message),
{ cryptosystemType: CryptoType.AES } => (new AES()).
Encrypt(parameters.key, message),
{ cryptosystemType: CryptoType.TripleDES } => (new
TripleDES().Encrypt(parameters.k1, parameters.k2,
parameters.k3, message)),
_ => throw new ArgumentException(message: "There is no
such cryptographic algorithm ",
paramName:
nameof(crypto.cryptosystemType))
};
}
Listing 5-5Using Property Patterns
清单 5-6 中给出了完整的代码,输出如图 5-3 所示。

图 5-3
清单 5-6 中代码的结果
using System;
namespace PropertyPatterns
{
class Program
{
static void Main(string[] args)
{
CryptographicAlgorithm cryptoAlg = new
CryptographicAlgorithm();
Console.WriteLine("Pick a cryptosystem [1=RSA,
2=AES, 3=TripleDES]");
string type = Console.ReadLine();
CryptoType ct = type switch
{
"1" => CryptoType.RSA,
"2" => CryptoType.AES,
"3" => CryptoType.TripleDES,
_ => throw new ArgumentException(message:
"There is no such option ",
paramName: nameof(type))
};
//** the parameters should be initialized
Parameters parameters = new Parameters();
//** the message that needs to be encrypted
int message = 0;
try
{
cryptoAlg.cryptosystemType = ct;
Encrypt(cryptoAlg, parameters, message);
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static int Encrypt(
CryptographicAlgorithm crypto,
Parameters parameters,
int message)
{
return crypto switch
{
{ cryptosystemType: CryptoType.RSA } => (new
RSA()).Encrypt(parameters.n,
parameters.e,
message),
{ cryptosystemType: CryptoType.AES } => (new
AES()).Encrypt(parameters.key,
message),
{ cryptosystemType: CryptoType.TripleDES } =>
(new TripleDES().Encrypt(parameters.k1,
parameters.k2,
parameters.k3,
message)),
_ => throw new ArgumentException(message:
"There is no such cryptographic
algorithm ",
paramName:
nameof(crypto.cryptosystemType))
};
}
}
public enum CryptoType
{ RSA, AES, TripleDES, }
public class Parameters
{
public Parameters() { }
internal int n, e, k1, k2, k3;
internal int[,] key;
}
public class CryptographicAlgorithm
{
internal CryptoType cryptosystemType;
public CryptographicAlgorithm() { }
public int Encrypt()
{
return 0;
}
}
class RSA : CryptographicAlgorithm
{
public RSA()
{
Console.WriteLine("RSA chosen!");
}
public int Encrypt(int n, int e, int message)
{
Console.WriteLine("Here goes the implementation of
the RSA encryption algorithm.");
return 0;
}
}
class AES : CryptographicAlgorithm
{
public AES()
{
Console.WriteLine("AES chosen!");
}
public int Encrypt(int[,] key, int message)
{
Console.WriteLine("Here goes the implementation of
the AES encryption algorithm.");
return 0;
}
}
class TripleDES : CryptographicAlgorithm
{
public TripleDES()
{
Console.WriteLine("TripleDES chosen!");
}
public int Encrypt(int k1, int k2, int k3,
int message)
{
Console.WriteLine("Here goes the implementation of
the TripleDES encryption algorithm.");
return 0;
}
}
}
Listing 5-6Demonstration of Using Property Patterns
多重模式
一些加密算法(例如 RSA 或可搜索的加密方案)依赖于各种输入。使用元组模式给了我们在用元组表示的多个值之间切换的可能性。清单 5-7 中的代码说明了如何使用开关表达式在不同的加密算法之间进行切换。
在本例中,您可以根据密钥的数量选择加密系统:一个密钥用于加密和解密,或者一对密钥(用于加密的公钥和用于解密的密钥)。列表 5-7 的结果如图 5-4 所示。

图 5-4
列表结果 5-7
using System;
namespace TuplePatterns
{
class Program
{
static void Main(string[] args)
{
CryptographicAlgorithm cryptoAlg =
new CryptographicAlgorithm();
Console.WriteLine("Enter the number of keys:
1=(secret key);
2=(public key, secret key); ");
string noOfKeys = Console.ReadLine();
try
{
Console.WriteLine(noOfKeys switch
{
"1" => InitializingAlgKeys(cryptoAlg,
false, true),
"2" => InitializingAlgKeys(cryptoAlg,
true, true),
_ => throw new ArgumentException(message:
"There is no such option ")
});
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static string InitializingAlgKeys
(CryptographicAlgorithm crypto,
bool publicKey,
bool secretKey)
{
return (publicKey, secretKey) switch
{
(true, true) => (new RSA()).InitializeKeys(),
(false, true) => (new AES()).InitializeKeys(),
_ => throw new ArgumentException(message:
"There is no such option. ")
};
}
}
public class CryptographicAlgorithm
{
public CryptographicAlgorithm() { }
public string InitializeKeys()
{
return "";
}
}
class RSA : CryptographicAlgorithm
{
public string InitializeKeys(int publicKey = -1,
int secretKey = -1)
{
return "RSA uses a public key and a secret key.
Initializing the keys for RSA...";
}
}
class AES : CryptographicAlgorithm
{
public string InitializeKeys(int secretKey = -1)
{
return "AES uses a secret key.
Initializing the key for AES...";
}
}
}
Listing 5-7Using Tupple Patterns
位置模式
一些应用和实现使用了Deconstruct方法。此方法的目的是将属性解构为离散变量。要使用位置模式,Deconstruct方法应该是可访问的,这样我们将能够查看特征对象的属性,并使用这些属性来生成模式。清单 5-8 显示了CryptoAlgorithms类,它包括一个Deconstruct方法,以便为prvKey(私钥)和pubKey(公钥)创建一个离散变量。清单 5-8 中的代码展示了位置模式在密码学领域的应用。结果如图 5-5 所示。

图 5-5
列表结果 5-8
using System;
namespace PositionalPattern
{
class Program
{
static void Main(string[] args)
{
KeysAdministration k1 = new
KeysAdministration("This is the private key",
"This is the public key");
KeysAdministration k2 = new
KeysAdministration("This is the private key", null);
KeysAdministration k3 = new
KeysAdministration(null, "This is the public key");
CryptographicAlgorithm cryptoAlg;
cryptoAlg = GetAlgorithmType(k1);
Console.WriteLine("public and private keys: " +
cryptoAlg.ToString());
cryptoAlg = GetAlgorithmType(k2);
Console.WriteLine("just the private key: " +
cryptoAlg.ToString());
cryptoAlg = GetAlgorithmType(k3);
Console.WriteLine("no matching keys: " +
cryptoAlg.ToString());
Console.ReadKey();
}
static CryptographicAlgorithm
GetAlgorithmType(KeysAdministration keys)
=> keys switch
{
var (privKey, pubKey) when
!string.IsNullOrEmpty(privKey) &&
!string.IsNullOrEmpty(pubKey)
=> CryptographicAlgorithm.RSA,
var (privKey, pubKey) when
!string.IsNullOrEmpty(privKey) &&
string.IsNullOrEmpty(pubKey)
=> CryptographicAlgorithm.AES,
_ => CryptographicAlgorithm.Unknown
};
}
public enum CryptographicAlgorithm
{
Unknown,
RSA,
AES,
}
public class KeysAdministration
{
public string prvKey { get; }
public string pubKey { get; }
public KeysAdministration(string PrivateKey,
string PublicKey)
=> (prvKey, pubKey) = (PrivateKey, PublicKey);
public void Deconstruct(out string PrivateKey,
out string PublicKey)
=> (PrivateKey, PublicKey) = (prvKey, pubKey);
}
}
Listing 5-8Positional Patterns
使用声明
使用声明表示一种在变量(或其类型)前面有using关键字的变量声明。例如,让我们考虑清单 5-9 中的例子,它将消息的加密写入文件。输出如图 5-6 所示。

图 5-6
列表结果 5-9
using System;
using System.Collections.Generic;
using System.Linq;
namespace Declarations
{
class Program
{
static void Main(string[] args)
{
List<string> encryptedMessageLines = new
List<string>();
encryptedMessageLines.Add("Option 1 key:
sdkjegiorjgvldmgkA64");
encryptedMessageLines.Add("This is the message to
be encrypted: This is an example of using
declarations in C# 8.0.");
encryptedMessageLines.Add("Option 2 key:
l$klj4grg565j");
Console.Write("The number of lines skipped: ");
Console.WriteLine(WriteEncryptedMessages(
encryptedMessageLines.AsEnumerable<string>())
.ToString());
Console.ReadKey();
}
static int WriteEncryptedMessages(IEnumerable<string>
encryptedMessageLines)
{
using var fileWithEncryption = new
System.IO.StreamWriter("Message.txt");
//** A note to be done on how we will declare
//** lines_to_be_skipped after the using
//** statement.
int lines_to_be_skipped = 0;
foreach (string lineMessage in
encryptedMessageLines)
{
if (!lineMessage.Contains("key"))
{
fileWithEncryption.WriteLine(lineMessage);
}
else
{
lines_to_be_skipped++;
}
}
//** Notice how the skipped lines
//** are the main subject here
return lines_to_be_skipped;
//** the file will be disposed once
//** the compiler reached here
}
}
}
Listing 5-9Using Declarations
在图 5-6 中,我们获得了两行,因为列表encryptedMessageLines有两行包含这行代码中提到的字符串key:
if (!lineMessage.Contains("key")) from the WriteEncryptedMessages method
为了检查从encryptedMessageLines开始的两行是否确实被跳过,看一下图 5-7 ,它显示了文本被写入的Message.txt文件的内容。如果没有指定完整路径,那么这个文件可以在项目的bin > Debug > netcoreapp3.1文件夹中找到(如果项目使用的是。网芯 3.1;否则路径是相似的)。

图 5-7
清单 5-9 中使用的文件内容
指数和范围
索引和范围为访问序列中的单个元素或范围提供了简短而精确的语法。
-
C# 8.0 提供了对两种新类型和操作符的支持,比如
System.Index被定义为序列中的索引。 -
操作符
^末尾的索引指定一个索引在序列的末尾是相对的。 -
System.Range定义一个已声明序列的子范围。 -
用于范围的运算符
…,指定操作数范围内的开始和结束。
在清单 5-10 中,我们考虑一个从开始到结束都有索引的数组。
var cryptograms = new string[]
{
// beginning index ending index
"ghdghdg", // 0 ⁹
"gfhdgfhdgfh", // 1 ⁸
"hsfgd", // 2 ⁷
"dg545ghfd44", // 3 ⁶
"fg435ffdgsdfg", // 4 ⁵
"gdsfg4refgs", // 5 ⁴
"54tgt4gv", // 6 ³
"ge43fsda", // 7 ²
"fdgsdef" // 8 ¹
};
Listing 5-10Working with Indexes
如您所见,您可以返回¹索引,如下例所示,列出了 5-11 。
//** this will return the cryptogram value "fdgsdef"
Console.WriteLine($"The last cryptogram (encrypted message) has
the following value {cryptograms[¹]}");
Listing 5-11Returning ¹ Index
在清单 5-12 中,我们用密码"gfhdgfhdgfh," "hsfgd,"和"dg545ghfd44."创建了一个子范围,这包括cryptograms[1]到cryptograms[3]。注意,element cryptograms[4]不在这个范围内。
var encryptedTexts = cryptograms[1..4];
Listing 5-12Creating Subranges
在清单 5-13 中,我们使用密码"ge43fsda"和"fdgsdef."创建了一个子范围,这将包括cryptograms[²]和cryptograms[¹]。注意,这个指数是cryptograms[⁰]。
var encryptedTexts = cryptograms[²..⁰];
Listing 5-13Another Subrange
在清单 5-14 中,我们为开始、结束或者在某些情况下为两者创建了一个在结束时开放的范围。
//** contains "ghdghdg" through "fdgsdef".
var encryptedTexts = cryptograms[..];
//** contains "ghdghdg" through "dg545ghfd44"
var firstEncryptedText = cryptograms[..4];
//** contains "ghdghdg", "ge43fsda" and "fdgsdef"
var lastEncryptedText = cryptograms[6..];
Listing 5-14Open Range
将所有这些放在一起,我们得到了清单 5-15 中的代码,结果如图 5-8 所示。

图 5-8
指数和范围输出
using System;
namespace IndicesRanges
{
class Program
{
static void Main(string[] args)
{
var cryptograms = new string[]
{
// beginning index ending index
"ghdghdg", // 0 ⁹
"gfhdgfhdgfh", // 1 ⁸
"hsfgd", // 2 ⁷
"dg545ghfd44", // 3 ⁶
"fg435ffdgsdfg", // 4 ⁵
"gdsfg4refgs", // 5 ⁴
"54tgt4gv", // 6 ³
"ge43fsda", // 7 ²
"fdgsdef" // 8 ¹
};
//** this will return the cryptogram value "fdgsdef"
Console.WriteLine($"The last cryptogram (encrypted message) has the following value { cryptograms[¹]}");
Console.Write("\n\n" + "Example 1 ~encryptedTexts~: ");
var encryptedTexts = cryptograms[1..4];
for (int i=0;i<encryptedTexts.Length;i++)
{
Console.Write(encryptedTexts[i] + " ");
}
Console.Write("\n\n" + "Example 2 ~encryptedTexts~: ");
encryptedTexts = cryptograms[²..⁰];
for (int i = 0; i < encryptedTexts.Length; i++)
{
Console.Write(encryptedTexts[i] + " ");
}
//** contains "ghdghdg" through "fdgsdef".
Console.Write("\n\n" + "Example 3 ~encryptedTexts~: ");
encryptedTexts = cryptograms[..];
for (int i = 0; i < encryptedTexts.Length; i++)
{
Console.Write(encryptedTexts[i] + " ");
}
//** contains "ghdghdg" through "dg545ghfd44"
Console.Write("\n\n" + "Example 4 ~firstEncryptedText~: ");
var firstEncryptedText = cryptograms[..4];
for (int i = 0; i < firstEncryptedText.Length; i++)
{
Console.Write(firstEncryptedText[i] + " ");
}
//** contains "ghdghdg", "ge43fsda" and "fdgsdef"
Console.Write("\n\n" + "Example 5 ~lastEncryptedText~: ");
var lastEncryptedText = cryptograms[6..];
for (int i = 0; i < lastEncryptedText.Length; i++)
{
Console.Write(lastEncryptedText[i] + " ");
}
Console.ReadKey();
}
}
}
Listing 5-15Example of Functional Indices and Ranges
零合并赋值
C# 8.0 提供了一个新特性,可以显著提高表示加密算法的代码的质量和性能。运算符??=(参见清单 5-16 )可用于将右操作数的值赋给左操作数,前提是左操作数的返回输出被评估为null。
下面,我们将把所有的东西放在一起,并展示如何将零合并赋值用于加密应用。见清单 5-16 和图 5-9 。

图 5-9
列表结果 5-16
using System;
using System.Collections.Generic;
namespace NullCoalescingAssignment
{
class Program
{
static void Main(string[] args)
{
List<string> cryptograms = null;
string val = null;
cryptograms ??= new List<string>();
cryptograms.Add(val ??= "fdsfasdf");
cryptograms.Add(val ??= "dsfasdfads4234");
//** output: "fdsfasdf" and "fdsfasdf"
Console.WriteLine(string.Join(" ", cryptograms));
//** output: "fdsfasdf"
Console.WriteLine(val);
Console.ReadKey();
}
}
}
Listing 5-16Operator ??=
非托管构造类型
就加密算法和安全解决方案的实现而言,非托管构造类型可能是一个非常有趣的话题。在 C# 7.3 中,构造类型不能是非托管类型(参见清单 5-17 )。当我们使用变量和不同类型来实现加密算法时,这非常有用,特别是当大小在它们的表示中起主要作用时。见清单 5-17 和图 5-10 。

图 5-10
列表结果 5-17
using System;
namespace TheUnmanagedTypes
{
class Program
{
static void Main(string[] args)
{
ShowTypeVariableSize<CryptographicAlgorithms<int>>();
ShowTypeVariableSize<CryptographicAlgorithms<double>>();
Console.ReadKey();
}
public struct CryptographicAlgorithms<T>
{
public T prvEncKey;
public T pubEncKey;
}
private unsafe static void ShowTypeVariableSize<T>()
where T : unmanaged
{
Console.WriteLine($"{typeof(T)} represents an
unmanaged type and the size is
{ sizeof(T) } bytes");
}
}
//** The resulted outputed is:
//** CryptographicAlgorithms`1[System.Int32] is unmanaged.
//** The size is 8 bytes
//** CryptographicAlgorithms`1[System.Double] is
//** unmanaged.
//** The size is 16 bytes
}
Listing 5-17Unmanaged Type Example (According to C# 7.3)
如果我们处理泛型结构,我们可以处理非托管和非托管构造类型。清单 5-17 中的例子定义了一个通用结构CryptographicAlgorithms<T>并展示了一个非托管构造类型的例子。如果我们想要一个非托管类型的例子,那就是CryptographicAlgorithms<object>.这不是非托管的,因为它有以对象类型为特征的字段,这是非托管的完美例子。如果我们希望所有构造的类型都是非托管类型,那么我们需要使用非托管约束(参见清单 5-18 )。
public struct CryptographicAlgorithms<T> where T : unmanaged
{
public T prvEncKey;
public T pubEncKey;
}
Listing 5-18Using an Unmanaged Constraint (According to C# 7.3)
基于清单 5-18 中的例子,CryptographicAlgorithms<T>类型是从 C# 8.0 和更高版本开始的非托管类型。正如你所看到的,对于一些非托管类型,我们可以声明一个指针指向一个声明为这种类型的变量,或者在堆栈上适当分配一个内存块(参见清单 5-19 )。为此,我们需要关注System.Span [2 。输出见图 5-11 。

图 5-11
清单 5-19 的结果
using System;
namespace MemoryAllocation
{
class Program
{
static void Main(string[] args)
{
Random r = new Random();
int k1 = r.Next(0, 255), k2 = r.Next(0, 255),
k3 = r.Next(0, 255), k4 = r.Next(0, 255),
k5 = r.Next(0, 255);
Span<int> keys = stackalloc[] { k1, k2, k3, k4, k5 };
Console.Write("The keys are: ");
for (int i = 0; i < keys.Length; i++)
Console.Write(keys[i] + " ");
var ind = keys.IndexOfAny(stackalloc[] { k3, k5});
Console.WriteLine("\n \n" + ind); // output: 1
Console.ReadKey();
}
}
}
Listing 5-19Allocation of a Memory Block on the Stack
结论
在本章中,我们介绍了 C# 8.0 最重要的新特性,这些特性可以在加密算法和安全方案的实现过程中使用。我们介绍的新特性集中在加密解决方案的两个主要方面,它们是
-
代码质量:从传统的源代码转向现代的编写源代码的角度
-
通过使用高级编程概念提高源代码的性能:经典与现代代码,使用优雅和规范的源代码重写大多数密码原语(如 RSA、AES、DES、Blowfish、Twofish 等)。)关注 lambda 表达式和 LINQ 语言
-
实施过程中使用的变量大小,并确保分配了适当的大小:分配内存大小并使用适当的大小和类型非常重要,尤其是当应用受到攻击时(软件混淆、缓冲区溢出等)。).这种攻击非常棘手,一旦被利用,会对软件应用造成严重破坏。
文献学
-
C# 语言版本控制。网上有:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version。 -
跨度结构。网上有:
https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netcore-3.1。 -
C# 语言版本控制。网上有:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version。
972

被折叠的 条评论
为什么被折叠?



