【Proverif语法(七)】

解决中间人攻击
(* Symmetric key encryption *)

type key.
fun senc(bitstring, key): bitstring.
reduc forall m: bitstring, k: key; sdec(senc(m,k),k) = m.


(* Asymmetric key encryption *)

type skey.
type pkey.

fun pk(skey): pkey.
fun aenc(bitstring, pkey): bitstring.

reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.


(* Digital signatures *)

type sskey.
type spkey.

fun spk(sskey): spkey.
fun sign(bitstring, sskey): bitstring.

reduc forall m: bitstring, ssk: sskey; getmess(sign(m,ssk)) = m.
reduc forall m: bitstring, ssk: sskey; checksign(sign(m,ssk),spk(ssk)) = m.


free c:channel.

free s:bitstring [private].
query attacker(s).

event acceptsClient(key).
event acceptsServer(key,pkey).
event termClient(key,pkey).
event termServer(key).

query x:key,y:pkey; event(termClient(x,y))==>event(acceptsServer(x,y)).
query x:key; inj-event(termServer(x))==>inj-event(acceptsClient(x)).

let clientA(pkA:pkey,skA:skey,pkB:spkey) = 
	out(c,pkA);
	in(c,x:bitstring); 
	let y = adec(x,skA) in
	let (=pkA,=pkB,k:key) = checksign(y,pkB) in
	(*=pkA,对自己的公钥进行确认,证明确实是发给自己的*)
	event acceptsClient(k);
	out(c,senc(s,k));
	event termClient(k,pkA).

let serverB(pkB:spkey,skB:sskey,pkA:pkey) = 
	in(c,pkX:pkey);
	new k:key; 
	event acceptsServer(k,pkX);
	out(c,aenc(sign((pkX,pkB,k),skB),pkX));
	(*签名里添加了pkX,便于收到此消息的客户端确认确实是发给自己的*)
	in(c,x:bitstring); 
	let z = sdec(x,k) in
	if pkX = pkA then event termServer(k).

process 
	new skA:skey; 
	new skB:sskey;
	let pkA = pk(skA) in out(c,pkA);
	let pkB = spk(skB) in out(c,pkB);
	( (!clientA(pkA,skA,pkB)) | (!serverB(pkB,skB,pkA)) )

运行:


--------------------------------------------------------------
Verification summary:

Query not attacker(s[]) is true.

Query event(termClient(x_2,y_1)) ==> event(acceptsServer(x_2,y_1)) is true.

Query inj-event(termServer(x_2)) ==> inj-event(acceptsClient(x_2)) is true.

--------------------------------------------------------------


语言特征
1.常量

在这里插入图片描述
其中,c是常量的名称,t是它的类型。可以通过声明相同类型t的几个常量。
在这里插入图片描述

2.数据构造函数和类型转换

在这里插入图片描述
构造函数可以通过附加[data]来声明为数据项:
在这里插入图片描述

声明为数据的构造函数类似于元组:攻击者可以构造和分解数据构造函数。换句话说,声明数据构造函数f隐式地声明n个析构函数映射f(x1,…,xn)xi,其中i∈{1,……,n}

可以通过模式匹配逆数据构造函数。T1, . . . , Tn的类型是f的参数类型,因此当Ti是变量时,可以省略它的类型。例如,与声明在一起:
在这里插入图片描述在这里插入图片描述
构造函数声明的数据不能声明为私有的。
数据构造函数的一个应用程序是类型转换。由于类型不匹配,类型系统偶尔很难将函数应用于参数。这可以通过类型转换来克服。定义如下:
在这里插入图片描述
其中,类型转换器tc接受t类型的输入,并返回t'类型的结果。请注意,由于构造函数是数据构造函数,因此攻击者可以从术语tc(M)中恢复术语M。直观地说,关键字typeConverter意味着该函数是标识函数(identity function),因此除了改变类型之外没有任何效果。默认情况下,类型用于输入协议,但在协议验证期间,ProVerif会忽略类型。因此,typeConverter的功能将被删除。(这种行为允许ProVerif检测类型缺陷攻击,其中攻击者混合不同类型的数据。
t't的反向类型转换应通过模式匹配来执行:
在这里插入图片描述
其中,M为t'类型,而xt类型。允许使用此构造函数,因为类型转换器是数据构造函数。当定义从类型tt'的类型转换器tc(t):t'时,所有类型t的元素都可以转换为类型t',但t'类型只能转换为类型t的元素是形式tc(M)的元素。因此,例如,从表示128位键的类型键定义类型转换器到类型比特字符串,但不是在另一个方向上,因为所有128bit key都是bitstring,但只有一些bitstring是128bit key。

3.Natural numbers

自然数得到本机支持,并具有内置类型nat。在内部,ProVerif遵循Peano公理建模自然数,即它考虑一个nat类型的常数0和一个继任者的数据构造函数。因此,所有的自然数都是术语,可以与其他用户定义的函数一起使用。如果一个项是常数0或自然数,则说是一个自然数。图中扩展了术语的语法,以考虑操作自然数的内置内缀函数。

【Proverif语法(七)】_第1张图片
最后,ProVerif有一个内置的布尔函数是nat检查一个项是否为一个自然数,即,is_nat(M)返回true,当且仅当M等于模等分理论到一个自然数。

两个任意项M之间的加法是不允许的。序关系>、<、>=、<=内部是用布尔型的解构器来实现的,如果关系的两边都是自然数那么就按日常理解的关系来做比较返回true或者false,只要有一边不是自然数,那么这个比较就会失败。另外,M - i中如果M < i那么也会失败,因为ProVerif中不允许出现负数。
由于自然数是用Peano Axiom生成的,所以攻击者可以生成所有的自然数。ProVerif不允许自己定义新的自然数。用户自定义的constructor也不能把nat作为返回类型,但是可以作为constructor的参数,所以是可以作为destructor(析构函数)的参数和返回类型的。

type key.

free c:channel.

free s:bitstring [private].


fun ienc(nat,key):bitstring.
(* constructor:用key对nat加密 *)
fun idec(bitstring,key):nat
(* destructor:用key对加密结果解密,如果本来明文是x,现在得到的是x-1 *)
reduc forall x:nat,y:key;idec(ienc(x+1,y),y)=x.

query attacker(s);

process
	new k:key;(
	out(c,ienc(2,k))
	|in(c,x:nat);in(c,y:bitstring);if x+3>idec(y,k) then out(c,s)
	)

x + 3 > 1是恒成立的,所以攻击者只要随便向通道c里传入一个数就能让这个判定成立,从而让s发送出去完成窃取:
【Proverif语法(七)】_第2张图片

4.Enriched terms

【Proverif语法(七)】_第3张图片

free c:channel.

free A:bitstring.
free B:bitstring.

process
	in(c,(x:bitstring,y:bitstring));
	if x=A||x=B then
	let z=(if y=A then new n:bitstring;(x,n) else (x,y)) in
	out(c,z)

先从channel c读取两个bitstring xy,如果xA或者B就把z从通道c发送出去,如果y=A就让z=(x,n),否则就让z=(x,y)

in(c,(x:bitsring,y:bitstring));
if((x=A)||(x=B)) then
new n:bitstring;
let z:bitstring=(if(y=A) then (x,n) else (x,y))in
out(c,z)
5.table和key distribution
table d(t1, ..., tn).

在进程里面对table进行填充和访问,但是没法删除,table不能被攻击者访问(就像数据库里的数据没法直接被攻击者获取一样)。
在这里插入图片描述

  • 向table d里插入数据(M1,...,Mn),然后执行过程P
  • 从table d里获取匹配模式(T1,...,Tn)的数据,如果能获取到相匹配的数据就执行P,否则就执行Q
  • 从table d里获取匹配模式(T1,...,Tn)的数据,获取到的数据还要能满足条件M,如果成功就执行P,否则执行Q

在这里插入图片描述
从table d里获取匹配模式(T1,...,Tn)的数据,条件满足的时候这个term就取用in后面的term,否则就取用else后面的term。

6.phase相

表达的是进程执行的不同“阶段”,通过给进程指令加入标号来区分不同的阶段。
默认的不带标号的指令都是在0阶段,所以不能显式地指定0阶段。
阶段的语义是,从0阶段开始执行,当从第 i 阶段到第 i+1阶段的时候,会去执行第 i + 1 阶段的指令,并且所有≥i+2阶段的指令都会被丢弃。
阶段可以用来证明前向安全(forward secrecy)协议,它是指即使长期使用的主密钥泄漏,也不会导致过去的会话密钥泄漏。

(* Symmetric key encryption *)

type key.
fun senc(bitstring, key): bitstring.
reduc forall m: bitstring, k: key; sdec(senc(m,k),k) = m.


(* Asymmetric key encryption *)

type skey.
type pkey.

fun pk(skey): pkey.
fun aenc(bitstring, pkey): bitstring.

reduc forall m: bitstring, sk: skey; adec(aenc(m,pk(sk)),sk) = m.


(* Digital signatures *)

type sskey.
type spkey.

fun spk(sskey): spkey.
fun sign(bitstring, sskey): bitstring.

reduc forall m: bitstring, ssk: sskey; getmess(sign(m,ssk)) = m.
reduc forall m: bitstring, ssk: sskey; checksign(sign(m,ssk),spk(ssk)) = m.


free c:channel.

free s:bitstring [private].
query attacker(s).

let clientA(pkA:pkey,skA:skey,pkB:spkey) = 
	out(c,pkA);
	in(c,x:bitstring); 
	let y = adec(x,skA) in
	let (=pkA,=pkB,k:key) = checksign(y,pkB) in
	out(c,senc(s,k)).

let serverB(pkB:spkey,skB:sskey,pkA:pkey) = 
	in(c,pkX:pkey);
	new k:key; 
	out(c,aenc(sign((pkX,pkB,k),skB),pkX));
	in(c,x:bitstring); 
	let z = sdec(x,k).

process 
	new skA:skey; 
	new skB:sskey;
	let pkA = pk(skA) in out(c,pkA);
	let pkB = spk(skB) in out(c,pkB);
	( (!clientA(pkA,skA,pkB)) | (!serverB(pkB,skB,pkA)) |
	phase 1; out(c, skB) )
	(*phase1时泄露skB*)

*:在phase1的时候把skB泄露,phase0时的s依然是安全的。


--------------------------------------------------------------
Verification summary:

Query not attacker_p1(s[]) is true.

--------------------------------------------------------------

如果泄露的是skA

phase 1; out(c, skA) )

phase0时的s就不再安全。


--------------------------------------------------------------
Verification summary:

Query not attacker_p1(s[]) is false.

--------------------------------------------------------------
7.同步Synchronization

关键字sync用来实现process的全局同步,它的功能有点像上面学习的phase。同步有一个等级和一个标记:

sync t [tag]

其中t是同步的等级(level),tag是同步的标签。具有相同等级和相同标签的同步被视为相同的同步,所以相同的同步只能被用于ifletlet ... suchthatget的不同分支里,因为同一时刻只能有一个分支在执行,所以同一时刻至多只有一个“相同的同步”能到达。

全局同步是按level升序执行的,在执行level为t的同步的时候,进程会执行到level为t的同步的所有tag都到达的程序点。

比如,假设t就是最小同步等级,然后在这个等级的tagtag1,…,tagn,那么进程就会执行到到达tag1,…,tagn这些程序点(各一个),然后就会一起执行掉这些同步命令,然后再继续往下执行。

phase不同的是,同步是不会丢弃进程片段的。

同步的tag确定方式:

  1. 用户可以用sync t [tag]来指定,如果用户忽略了tag,只写sync t,那么ProVerif会分配一个新tag
  2. 如果同步被用在process macro里,那么在这个macro展开的地方可以指定tag的前缀(prefix),形如[sync: tag prefix p],然后在宏展开的时候就会把这个前缀补上,比如:
let P(x:bitstring)=
	sync 1 [t];
	out(c,x).
process 
	P(a) [sync:tag prefix T1] | [sync:tag prefix T2]
yields the process
sync 1 [T1_T];out(c,a)|sync 1 [T2,T];out(c,b)

T1T2这两个前缀加到T前面去了,并且补上下划线变成T1_TT2_T两个tag。这个功能是有必要的,当一个process macro被复用的时候,特别是多个做并发的时候,按理说这些同步点的tag就应该是不一样的(不然就成了“相同的同步”),所以即使[sync: tag prefix p]被省略了,实际上ProVerif也会自动加入一个新的前缀让它们区分开。如果要强制不加前缀,那么就使用[sync: no tag prefix]

对于测试分支,不同分支里的同步应该有相同的tag名,不然的话就容易导致同步的阻塞(block),因为两个分支只能走一个但是缺要求同步的时候走到所有的不同的tag标记点。所以应该这样写:

if...then(...sync1 [T];...)else(...sync1[T];...)
or 
if...then(...P(...)[sync:tag prefix T])
	 else(...P(...)[sync:tag prefix T])

分别对应process macro内和外的写法。

8.扩展的destructor

将解构器的能力扩展,现在它可以被定义为:

在这里插入图片描述相当于有一系列的重写规则(rewrite rule),对于实际拿到的一个析构操作,到析构器里从上到下看,如果某一条重写规则是可应用的(applied,其实就是命中了这条规则,就是所有的项都能正确的match上),那么就用这条规则。如果所有的重写规则都遍历完一遍没有能应用上的,那么这个destructor就失败(fail)了。

在这里插入图片描述
在这个定义下,eq(M, N)都是string并且是同一个东西的时候被规约成true,都是string不同的两项的时候被规约成false。如果这两条重写规则都不命中的话,解构操作就会失败,也就是说上面的解构器不能处理这样一个问题:有参数失败的情况。要解除这个限制,ProVerif引入了表示失败的特殊值fail,比如下面的例子:

【Proverif语法(七)】_第4张图片
当出true时候就把第一项x吐出来,否则当出false,或者出失败fail的时候就把第二项y吐出来。一个“可能失败的t类型变量x”(不妨理解成C#里的可空类型修饰符T?,类型T?相比T多了一个取值null)可以表示成x : t or fail,所以上面的解构器可以这样写:

【Proverif语法(七)】_第5张图片

在这个case里,如果要搞成能处理x和y的失败问题,可以这样:

在这里插入图片描述
另外,由于引入了fail这个关键字,可以用定制的Axiom来对fail做捕获和给出兜底值,比如下面的例子里兜底值是c0
【Proverif语法(七)】_第6张图片

9.等式equation

一些密码学操作(比如Diffie-Hellman密钥协商)不能被编码为destructor,因为里面涉及代数学操作。ProVerif针对这种情况提供了一种替代模型——equation,形如:
在这里插入图片描述其中M和N是用前面的类型为t1…tn的变量x1…xn通过constructor构建而成的项,当然这两个项里面也可以完全没有用到这些变量(那这两项就是常量了),这种时候前面对这n个变量的类型声明会被忽略。可以一次性定义多条等式:
在这里插入图片描述
其中option的取值可以为[convergent]、[linear]或者为空,分别对应ProVerif里equation所的一些局限性。

例如,对Diffie-Hellman密钥协定方法建模时,由于要对求幂的操作进行建模,具体是要建模

在这里插入图片描述
【Proverif语法(七)】_第7张图片
在这里插入图片描述
对称加密也可以用equation来描述(添加[data]声明为数据项):

【Proverif语法(七)】_第8张图片

10.Function macros 功能宏:letfun

有时,由不仅仅是一个构造函数或析构函数应用程序组成的术语会重复很多次,ProVerif提供了一个宏机制,以定义一个表示该项的函数符号,并避免重复,函数宏由以下声明进行定义:

在这里插入图片描述f是从M的类型推导,[or fail]控制传入的参数失败时处理方式,如果不加,类型失败时直接返回failM是富集项。

如果or fail 缺失并且参数失败,则函数宏也会失败。例如,与定义在一起使用
【Proverif语法(七)】_第9张图片如果or fail存在,且参数失败,则失败值将传递给函数宏,例如可以捕获它,并返回一些非失败结果。例如,对h的定义与上面相同,以下定义为f

在这里插入图片描述
概率非对称加密的一个例子:

【Proverif语法(七)】_第10张图片
观察到,由于引入了额外的名称,导致了概率密码学的使用增加了模型的复杂性。这可能会减缓分析过程。

11. Process macros with fail

与上面的函数宏类似,进程宏也可以使用类型为t或or fail的参数进行声明:

在这里插入图片描述
每个参数类型之后的可选的or fail,允许用户在进程的某些参数失败时控制进程的行为:

【Proverif语法(七)】_第11张图片【Proverif语法(七)】_第12张图片

12.密码学原语的合适的形式化

ProVerif依赖于符号化的Dolev-Yao密码学模型,所以结果不能应用于计算模型,如果要用计算模型可以考虑CryptoVerif等其它工具。

哈希函数
哈希函数表示为一个没有关联析构函数的一元析构函数或方程h。该构造函数将接受并返回一个位字符串作为输入。因此,我们的定义如下:

在这里插入图片描述
没有任何相关的析构函数或等式理论捕获了加密哈希函数的前图像阻、第二前图像阻和碰撞阻特性。事实上,确保了更强的性质:这个哈希函数模型接近随机Oracle模型。

对称加密

对称加密最基本的形式化是基于解密为析构函数的加密。然而,更接近实际的加密方案的形式化如下:

  • 对于确定性的双射加密方案,更好的形式化是基于方程的。
  • 其他的对称加密方案也是有概率性的。这可以以一种概率公钥加密的方式进行形式化。
    【Proverif语法(七)】_第13张图片在此形式化中,加密会隐藏加密密钥。如果要建模一个不隐藏该密钥的加密方案,则可以添加以下destructor:

在这里插入图片描述
此析构函数允许攻击者测试是否使用同一密钥构建了两个密文。这种析构函数的存在对于可达性属性(保密、通信)没有任何区别,因为它不能允许攻击者构造它无法构造的术语。然而,它确实对观测等价性质有所影响。(请注意,将加密密钥交给攻击者,以模拟一个不隐藏密钥的方案,显然是一个严重的错误。)

非对称加密
也可以模拟加密泄漏密钥。由于加密密钥是公共的,我们可以通过将密钥交给攻击者:

在这里插入图片描述
非对称加密的另一种(和等价的)形式考虑一元构造函数pk’,sk’接受类型种子的参数,以捕获从某个send’构造密钥对的概念。

【Proverif语法(七)】_第14张图片数字签名

非确定性的(也就是带有概率的)数字签名,也可以分为带有消息恢复(Message Recovery,从签名中获取签名之前的原消息)机制的:

【Proverif语法(七)】_第15张图片
和不能获取原来的消息的(不带Message Recovery机制):

在这里插入图片描述在这里插入图片描述
这种签名方法是把原始消息隐藏了,在做验证的时候把消息m传入,这样来检测是否能验证成功。

如果想要重新引入泄露消息的机制,就只要加个解构器就可以了(也就是给攻击者建模能从中获取消息的能力):

在这里插入图片描述
如果想建模公钥泄露,那么也是加一个解构器,使得能从签名结果取出公钥(也就是给攻击者建模这样一个能力:对于每个签名结果总是知道使用了哪个公钥来签名):
在这里插入图片描述

消息认证码
形式上和不带解构器的哈希很像,也是随机预言模型,但是建模的时候多提供了一个密钥:

在这里插入图片描述

你可能感兴趣的:(Proverif,安全)