1. What is strobe?
strobe为面向IoT物联网密码学协议框架,使其在小的IoT设备上更易于开发、部署和分析。strobe适于在16位或32位的微处理器上运行。
strobe同时也是一种轻量级的网络协议,可作为TLS的替换。因为对于资源有限的IoT设备来说,可能无法运行TLS协议。
strobe基于SHA-3 KECCAK-f
函数来进行消息的加密和认证。
目前有NACL, Noise, BLINKER等多种协议框架,strobe跟BLINKER是同一家族的。
目前的现状为:
- NACL:基于2012年论文《The security impact of a new cryptographic library》,基于rust语言的实现有
https://github.com/Yawning/rust-crypto-nacl
自2015年起就不再更新了; - Noise:基于论文(2016年发布,2018年有修订)《The Noise Protocol Framework》,其rust代码库
https://github.com/mcginty/snow
仍很活跃,且在各大密码学学术会议上,基于Noise协议不断有新的研究成果展示。Noise协议已在WhatsAPP上落地应用。 - BLINKER:基于2013年论文《Beyond Modes: Building a Secure Record Protocol from a Cryptographic Sponge Permutation》,暂未找到实现源代码。
- strobe:基于2017年论文《The Strobe protocol framework》,rust代码实现有
https://github.com/rozbb/strobe-rs
。strobe视频解说:https://www.youtube.com/watch?v=l7xV5z1eJLw 。
由此可知,主要考虑noise和strobe两个协议。
2017年,Facebook工程师有博客专门讨论Noise+Strobe=Disco,对noise(非对称加密)和strobe(对称加密)做了对比,并用go语言做了一个实现,代码仓库为:https://github.com/mimoo/disco ,该仓库至今仍较活跃。
2. KECCAK-f
2012年10月,美国NIST选择了Keccak算法作为SHA-3的标准算法,Keccak拥有良好的加密性能以及抗解密能力。
Keccak算法已经作为面向移动通信(TUAK)3GPP TS 35.231的标准,以及NIST FIPS 202和SP 800-185中的标准,经过了大量公开的密码学审查和第三方密码学分析。
KECCAK-f主要有KECCAK-f[25]、KECCAK-f[50]、KECCAK-f[100]、KECCAK-f[200]、KECCAK-f[400]、KECCAK-f[800]、KECCAK-f[1600]这几种。
Keccak[r, c]与KECCAK-f的关系为:f=r+c
Keccak的一些实例标准有:
3. strobe的作用
4. strobe的设计思想
论文《The Strobe protocol framework》中指出,strobe的主要设计原则为:在任意阶段的密码学输出,除依赖于密钥外,还依赖于之前所有的输入。比如,加密一个消息,所生成的密文除依赖于key和明文外,还依赖于之前的数据如nonces和关联数据等。为实现该设计原则,strobe一直在keep a running hash of the protocol transcript。
strobe的该设计原则更侧重于简单和安全,而不是速度。
因为有running hash的缘故,strobe特别适于保证在传输层消息的有序传输的可靠性;strobe也适于半双工模式,即双方依次发送消息而不是同时发送。如TLS安全传输协议在握手阶段采用的就是半双工模式,当信道打开后才是全双工模式。在握手阶段,通常需要两个不同的密钥key,一个用于从Alice给Bob发消息,另一个用于从Bob给Alice发消息。
strobe主要承担协议中的对称加密部分,公钥加密和签名等仍需要借助其它密码学原型,如RSA或椭圆曲线等。
4.1 strobe的oprations
strobe框架主要位于两个领域:
- 应用层:应用层有直接使用的数据,如密钥、铭文消息、nonces随机数和关联数据。需要保护这些数据的隐私和完整。
- 传输层:主要有各方传输的数据,以及需要存储或从不可信内存恢复的数据。这些数据包括密文,传输中的可读消息,MACs和签名。
应用层不直接操作传输层,应用层读写数据均借助strobe的加解密操作。
在上述图中,做了一个假设,即传输层不可信,而应用层是可信的。
通过strobe交互的信息可分为四大类:
上图中的:
RATCHET
既没有输入也没有输出,用于防止回滚攻击。RATCHET
通常会清理掉部分状态,当设备太小无法运行memory-hard函数时,可通过由密码生成密钥来实现相同的功能。KEY
和AD
操作具有相同的数据刘翔,两者均是对随机预言机的输入。KEY
操作会用新key重写overwrite部分状态,所以只要新key一直存在,就可像RATCHET
一样防止回滚。strobe仅在新block开始时才会有重写操作,所以KEY
操作比AD
更安规。non-sponge framework非海绵框架如Noise协议,也会做key和associated data的区分。strobe做该区分有利于其与其它协议的对接移植。
(I,A,C,T)
为4个bit位,实际执行时采用的是一整个byte,共8个bit位。剩余的4个bit位中有一个表示为M
,用于区分metadata,对I/A/C/T等操作无影响,仅用于hashed into the protocol transcript;有一个表示为K
,作为DPA-resistant \key tree" extension,对I/A/C/T等操作有影响;剩余的2个bit作为保留位,目前暂未使用。
4.2 strobe的protocol transcripts
strobe一直在维护 a running hash of the protocol transcript,实际即为从应用层看到的一序列所有的操作和数据。其中的操作包含KEY
和AD
等不给传输层数据的操作。
protocol transcript为一系列组合对:
(
A
d
j
D
i
r
(
I
0
;
o
p
e
r
a
t
i
o
n
)
,
A
p
p
D
a
t
a
)
(AdjDir(I_0;operation),AppData)
(AdjDir(I0;operation),AppData)
(
A
d
j
D
i
r
(
I
0
;
.
)
(AdjDir(I_0;.)
(AdjDir(I0;.)函数可adjusts the operations so that when Alice sends a message (with
I
=
0
I=0
I=0) and Bob receives it (with
I
=
1
I=1
I=1), the two protocol transcripts will match.
A
d
j
D
i
r
(
I
0
,
(
I
,
A
,
C
,
T
,
M
,
K
)
)
:
=
(
I
⊕
(
T
⋅
I
0
)
,
A
,
C
,
T
,
M
,
K
)
AdjDir(I_0,(I,A,C,T,M,K)):=(I\oplus (T\cdot I_0), A,C,T,M,K)
AdjDir(I0,(I,A,C,T,M,K)):=(I⊕(T⋅I0),A,C,T,M,K)
where
I
0
I_0
I0 is the value of the
I
I
I flag in the first operstion that has
T
=
1
T=1
T=1.也就是说,对于发起者来说
I
0
=
0
I_0=0
I0=0,对于接收者来说
I
0
=
1
I_0=1
I0=1。
- 当 A = 1 A=1 A=1时,若 I = 0 I=0 I=0, A p p D a t a AppData AppData将从应用层发出;若 I = 1 I=1 I=1,则 A p p D a t a AppData AppData将由应用层接受。
- 当 A = 0 A=0 A=0时,则 A p p D a t a : = L × [ [ 0 ] ] AppData:=L\times[[0]] AppData:=L×[[0]] (为L-byte操作),也就是说,此时protocol transcript包含了MACs的长度,但是没有相应的值,即所有的值都重置为了0。通过这种方式,当接收到不正确的MACs时,protocol会进行终止操作。
第
i
i
i个block的输出,取决于操作
o
p
∈
{
E
N
C
,
M
A
C
,
P
R
F
}
op\in\{ENC,MAC,PRF\}
op∈{ENC,MAC,PRF},输入有:前序的protocol transcript
T
r
Tr
Tr;当前block的input;前序block的application data。详细可表示为:
O
u
t
p
u
t
i
=
I
n
p
u
t
i
⊕
G
(
T
r
,
A
d
j
D
i
r
(
I
0
,
o
p
e
r
a
t
i
o
n
)
,
(
A
p
p
D
a
t
a
1
∣
∣
.
.
.
∣
∣
A
p
p
D
a
t
a
i
−
1
)
)
Output_i=Input_i\oplus G(Tr, AdjDir(I_0,operation),(AppData_1||...||AppData_{i-1}))
Outputi=Inputi⊕G(Tr,AdjDir(I0,operation),(AppData1∣∣...∣∣AppDatai−1))
其中
G
G
G为随机函数,
ϵ
\epsilon
ϵ-indifferenctiable。
所有的STROBE协议,都必须以 A D AD AD操作开始,且在 A D AD AD操作中应有不同domain的分隔符。该分隔符需为通一资源标志符(Universal Resource Identifier, URI),以保证能唯一描述协议,防止各种跨协议攻击。
对于简单的协议,每个操作应在上下文中具有唯一的涵义,使得transcript可解析。对于复杂的协议,相同的操作可能有多重涵义,此时要想解析transcript,一种简单的方法时在transcript中做备注来区分。此时信息可以通过协议帧来传输,如TCP stream中的tag-length-value帧(tag表明message type)。在STROBE协议中,通过
m
e
t
a
d
a
t
a
metadata
metadata来区分,通过
M
M
M标签来标记,对操作无任何影响,仅仅用于hashed into the protocol transcript,这样的操作页可称为
m
e
t
a
−
o
p
meta-op
meta−op操作。
大多数协议,都是以明文格式来发送帧数据,这时会使用
m
e
t
a
−
C
L
R
meta-CLR
meta−CLR transaction(除了在transcript中具有
M
M
M标签外,与
C
L
R
CLR
CLR操作类似)。
若使用
m
e
t
a
−
E
N
C
meta-ENC
meta−ENC操作,则相应的帧信息将被加密,这在图片隐写术和长度填充中有用。
在协议中若未传输足够的帧信息来保证完全可解析,可通过
m
e
t
a
−
A
D
meta-AD
meta−AD操作来补充传送帧信息。
推荐的做法为:对于每个non-metadata操作,后面都紧跟一个metadata操作来做补充描述。
以OP来表示操作,info标签来描述协议中消息message的含义。则在Info标签中应包含消息的长度(除非在协议中为固定值或不可知)。同时以标签-操作
符来描述帧信息及对应的操作符。(如meta-CLR,meta-AD,meta-CLR等)。
举例:ENC[app-ciphertext] (“hello”)可拆分为两个动作:
meta-CLR([[0x03,0x05,0x00]]); ENC(“hello”)
其中 0 x 03 0x03 0x03标签代表app-ciphertext,[[0x05,0x00]]为length标签,代表的时5个bytes。
5. strobe的实例化
论文《The Strobe protocol framework》的附录B.1中指出,当采用keccak-f[1600]时,即 b = 1600 b=1600 b=1600,STROBE security为 λ \lambda λ,有 c = 2 λ , r + c = b , F = f [ b ] c=2\lambda,r+c=b,F=f[b] c=2λ,r+c=b,F=f[b],于是有 r = b − c = 1600 − 2 λ r=b-c=1600-2\lambda r=b−c=1600−2λ。
根据附录A.1可知,
r
r
r为sponge rate,
c
c
c为capacity。
r
r
r的取值范围为
3
≤
r
≤
256
3\leq r\leq256
3≤r≤256,为了减小功耗分析攻击,sponge rate会调整为
r
^
=
r
/
8
−
2
=
200
−
λ
/
4
−
2
,
r
/
8
=
r
^
+
2
\hat{r}=r/8-2=200-\lambda/4-2, r/8=\hat{r}+2
r^=r/8−2=200−λ/4−2,r/8=r^+2,对应的https://github.com/rozbb/strobe-rs
中代码实现为:
let rate = KECCAK_BLOCK_SIZE * 8 - (sec as usize) / 4 - 2; //KECCAK_BLOCK_SIZE =25, rate即为上文的$\hat{r}$
assert!(rate >= 1);
assert!(rate < 254);
rate即为上文的
r
^
\hat{r}
r^。
初始化状态为:
对应的代码实现为:
// Initialize state: st = F([0x01, R+2, 0x01, 0x00, 0x01, 0x60] + b"STROBEvX.Y.Z")
let mut st_buf = [0u8; KECCAK_BLOCK_SIZE * 8];
st_buf[0..6].copy_from_slice(&[0x01, (rate as u8) + 2, 0x01, 0x00, 0x01, 0x60]);
st_buf[6..13].copy_from_slice(b"STROBEv");
st_buf[13..18].copy_from_slice(STROBE_VERSION.as_bytes());
//运行F函数,keccak-f[1600]
let mut st = AlignedKeccakState(st_buf);
keccakf_u8(&mut st);
let mut strobe = Strobe {
st: st,
sec: sec,
rate: rate,
pos: 0,
pos_begin: 0,
is_receiver: None,
};
// Mix the protocol into the state
let _ = strobe.meta_ad(proto, false);
//对应地:
def_op_no_mut!(
ad,
meta_ad,
OpFlags::A,
"Mixes associated data into the internal state."
);
// This defines an operation and meta-operation that does not mutate its input
macro_rules! def_op_no_mut {
($name:ident, $meta_name:ident, $flags:expr, $doc_str:expr) => (
#[doc = $doc_str]
pub fn $name(&mut self, data: &[u8], more: bool) {
let flags = $flags;
self.operate_no_mutate(flags, data, more);
}
#[doc = $doc_str]
pub fn $meta_name(&mut self, data: &[u8], more: bool) {
let flags = $flags | OpFlags::M;
self.operate_no_mutate(flags, data, more);
}
)
}
/// Performs the state transformation that corresponds to the given flags. If `more` is given,
/// this will treat `data` as a continuation of the data given in the previous call to
/// `operate`. This uses non-mutating variants of the specializations of the `duplex` function.
pub(crate) fn operate_no_mutate(&mut self, flags: OpFlags, data: &[u8], more: bool) {
assert!(!flags.contains(OpFlags::K), "Op flag K not implemented");
if !more {
self.begin_op(flags);
}
// There are no non-mutating variants of things with flags & (C | T | I) == C | T
if flags.contains(OpFlags::C) && flags.contains(OpFlags::T) && !flags.contains(OpFlags::I) {
panic!("operate_no_mutate called on something that requires mutation");
} else if flags.contains(OpFlags::C) {
// This is equivalent to a non-mutating form of the `duplex` operation in the Python
// implementation, with `cbefore = True`
self.overwrite(data);
} else {
// This is equivalent to the `duplex` operation in the Python implementation, with
// `cbefore = cafter = False`
self.absorb(data);
};
}
参考资料:
[1] https://strobe.sourceforge.io/
[2] https://github.com/rozbb/strobe-rs
[3] https://keccak.team/keccak.html
[4] 论文《The Strobe protocol framework》
[5] strobe视频解说:https://www.youtube.com/watch?v=l7xV5z1eJLw
[6] https://www.cryptologie.net/article/408/noisestrobedisco/
[7] https://noisesocket.org/post/1/