Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VMess协议的IV长度只能为12个字节,是否会导致安全性问题?能否增加选项设置IV长度? #2130

Closed
poly-1 opened this issue Dec 25, 2019 · 23 comments
Labels

Comments

@poly-1
Copy link

poly-1 commented Dec 25, 2019

如题,VMess协议现在默认的加密方式为AES-128-GCM与ChaCha20-Poly1305,两种方式的IV均为12字节。他们的本质为AES-CTR,即计数器模式,必须保证IV不能重用。因为对于CTR而言,重用IV会导致完全失去安全性,详见维基百科-分组密码工作模式

初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密,然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV。对于CBC和CFB,重用IV会导致泄露明文首个块的某些信息,亦包括两个不同消息中相同的前缀。对于OFB和CTR而言,重用IV会导致完全失去安全性。另外,在CBC模式中,IV在加密时必须是无法预测的;特别的,在许多实现中使用的产生IV的方法,例如SSL2.0使用的,即采用上一个消息的最后一块密文作为下一个消息的IV,是不安全的

对于Shadowsocks,现已采用16字节甚至24字节的IV长度(例如SS中的xchacha20-ietf-poly1305)以解决这个问题,讨论的地方在此

我看了一下VMess协议细节(https://www.v2ray.com/developer/protocols/vmess.html), 现在两种方式——AES-128-GCM与ChaCha20-Poly1305的IV均为12字节,长期采用同一密钥容易发生碰撞。

是否需要更改一下VMess协议的实现?毕竟增加IV长度不难

@Balnea
Copy link

Balnea commented Dec 27, 2019

其实早该更新了

@kotori2
Copy link

kotori2 commented Dec 28, 2019

查了下,GCM是推荐使用12B的IV的(参考
另外个人觉得,既然AES是按16B的block计算,大于16B的IV意义不大?毕竟计算时要把信息熵缩减到与16B等同。
还有,对于IV重用好像并没有在vmess中出现问题,vmess协议是在每个请求的请求头(指令部分)随机生成一个IV,加密数据包用过以后也不会重用。

@kotori2
Copy link

kotori2 commented Dec 28, 2019

顺带一提,xchacha20-ietf-poly1305还没咕出来(#1884

@poly-1
Copy link
Author

poly-1 commented Dec 29, 2019

顺带一提,xchacha20-ietf-poly1305还没咕出来(#1884

看了一下,你发的这个issue链接里面已经写了xchacha20-ietf-poly1305出来了,最后一条comment。见此

https://godoc.org/golang.org/x/crypto/chacha20poly1305#NewX
XChaCha20-Poly1305 is a ChaCha20-Poly1305 variant that takes a longer nonce, suitable to be generated randomly without risk of collisions. It should be preferred when nonce uniqueness cannot be trivially ensured, or whenever nonces are randomly generated.

另外,刚翻到这个帖子说 XChaCha20-Poly1305-IETF 是在chacha类算法中当前唯一推荐的算法

@poly-1
Copy link
Author

poly-1 commented Dec 29, 2019

查了下,GCM是推荐使用12B的IV的(参考
另外个人觉得,既然AES是按16B的block计算,大于16B的IV意义不大?毕竟计算时要把信息熵缩减到与16B等同。
还有,对于IV重用好像并没有在vmess中出现问题,vmess协议是在每个请求的请求头(指令部分)随机生成一个IV,加密数据包用过以后也不会重用。

这个问题不能这么想,就好比AES虽然固定 128 bits block,但key size可以是 128, 192, or 256 bits ,不能说更长的 key size 就没有意义了。

对于GCM推荐使用12B的IV这点,要分情况讨论。正如这里所说,如果采用临时或短期密钥,即使8B的IV也是安全的。但是VMess应该是采用了长期不变的密钥,一旦config配置好了就不变了,这样的话,只要传输64GB以上的内容将会导致碰撞,以致基于计数器模式下的算法完全丧失安全性

这是目前我的理解,如有不对之处请见谅。

@kotori2
Copy link

kotori2 commented Dec 29, 2019

@poly-1 是 Golang出了,v2ray还没整(

@kotori2
Copy link

kotori2 commented Dec 29, 2019

@poly-1 这个问题不能这么看。
AES支持大于16B的密钥是因为,如果使用了更长的密钥,则会增加迭代的轮数

The key size used for an AES cipher specifies the number of transformation rounds that convert the input, called the plaintext, into the final output, called the ciphertext. The number of rounds are as follows:

10 rounds for 128-bit keys.

12 rounds for 192-bit keys.

14 rounds for 256-bit keys.

而IV的作用则是简单地跟加密前的前16B数据异或,防止相同的明文得到相同的结果。更长或者更短的IV不太清楚是怎么处理的,但是估计大同小异?
另外你提到的key一样的问题,数据加密用的key是随机生成以后包含在控制指令里的,所以不会重复。而控制指令的key是通过用户ID算出来的

Key:MD5(用户 ID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))

这个倒是有可能碰撞,不过这样算的话,需要发送2x10^17次数据包才可能出现重复,概率还是蛮低的?

@poly-1
Copy link
Author

poly-1 commented Dec 29, 2019

@kotori2 原来密钥长是为了迭代轮数:thumbsup:

不过计算碰撞的话,一般是看这个概率表

为了避免碰撞,通常希望概率在10^-18到10^-15,

https://en.wikipedia.org/wiki/Birthday_problem#Probability_table
For comparison, 10−18 to 10−15 is the uncorrectable bit error rate of a typical hard disk.

如果采用12B,保证碰撞概率在10^-15以下大概需要1.3*10^7个数据包
如果采用16B或24B,对于数据包个数为8.2*10^11和3.5*10^21.

这样看来似乎即使当前使用12B,安全性也还不错,不过不知1.3*10^7个控制指令的数据包大概对应实际情况的多长时间或是多少数据流量?

另外,你所说的“数据加密用的key是随机生成以后包含在控制指令里”这个有相应的协议文档吗?还是读源码读到的?

@kotori2
Copy link

kotori2 commented Dec 29, 2019

@kotori2 原来密钥长是为了迭代轮数👍

不过计算碰撞的话,一般是看这个概率表

为了避免碰撞,通常希望概率在10^-18到10^-15,

https://en.wikipedia.org/wiki/Birthday_problem#Probability_table
For comparison, 10−18 to 10−15 is the uncorrectable bit error rate of a typical hard disk.

如果采用12B,保证碰撞概率在10^-15以下大概需要1.310^7个数据包
如果采用16B或24B,对于数据包个数为8.2
10^11和3.5*10^21.

这样看来似乎即使当前使用12B,安全性也还不错,不过不知1.3*10^7个控制指令的数据包大概对应实际情况的多长时间或是多少数据流量?

另外,你所说的“数据加密用的key是随机生成以后包含在控制指令里”这个有相应的协议文档吗?还是读源码读到的?

我觉得你把两套key和iv搞混了。下图来自你发的文档链接
image

@poly-1
Copy link
Author

poly-1 commented Dec 29, 2019

@kotori2 没有吧,我们的最终目的是使IV不重用(因为在计数器模式下重用会基本完全丧失安全性,从这个角度来说CFB反而好一些,不过由于CFB不能抗干扰,所以不能用)。

而要计算IV是否reuse的概率,其计算方法跟 hash collision 的方法我觉得应该是一致的?所以我给出了那张概率表

@k79e
Copy link

k79e commented Jan 1, 2020

12b=96bits
谁知道iv一般多久换一次 算算不就知道了.
96bit就是如果更换到那么多次 就会有碰撞 或者其他人干脆穷举都可以.
ss他们的nonce都是12字节. 12应该是够用了.

64g是一次输入的数据的大小 超过了之后不太安全. 不是总共所有人跑个64g就爆表了.
那个32位block counter是怎么回事也不太清楚 不同加密库还都不太一样.

有没有高人去看看go那个源码 内部块计数器限制是不是也是32bit的.?
我听说libsodium的不一样 也不知道是真是假. 搞不清楚那个东西是不是规范呢. 是规范的话为什么其他人乱改呢....

@kotori2
Copy link

kotori2 commented Jan 1, 2020

@poly-1 我还是觉得,对于数据块的IV不用担心重用碰撞问题。
你说的碰撞,前提是他IV是公开的,而在VMess协议里,数据块的IV和key都是随机生成的,而且是加密后存在控制数据里的,要碰撞也只能在key和IV均相同的情况碰撞,也就是10^-28。
所以我觉得更需要担心的是控制块的IV。因为他对于一个用户来讲是固定的key。不过由于他是MD5生成的,也许需要分析MD5(64b时间戳*4)重复的概率🤔

@kotori2
Copy link

kotori2 commented Jan 1, 2020

@k79e

谁知道iv一般多久换一次 算算不就知道了.

每次握手换一次,取决于客户端的buffer以及你发送/接收的数据大小。

64g是一次输入的数据的大小 超过了之后不太安全. 不是总共所有人跑个64g就爆表了.

这里的64G是不更换数据加密key的前提的,因为每次握手的数据加密key是不一样的,所以没意义。

那个32位block counter是怎么回事也不太清楚

跟CTR一样,把IV+Counter变成真IV。只是GCM最后多了个MAC

有没有高人去看看go那个源码 内部块计数器限制是不是也是32bit的.?

https://golang.org/src/crypto/cipher/gcm.go L340

// gcmInc32 treats the final four bytes of counterBlock as a big-endian value
// and increments it.

应该就是32b

我听说libsodium的不一样

https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_aead/aes256gcm/aesni/aead_aes256gcm_aesni.c#L604
粗略看了下,counter估计也是int

@k79e
Copy link

k79e commented Jan 1, 2020

如你所说 这指令部分的iv竟然就是时间的MD5. 这真的安全么!!!
怎么有种后患无穷的感觉....

@kotori2
Copy link

kotori2 commented Jan 1, 2020

如你所说 这指令部分的iv竟然就是时间的MD5. 这真的安全么!!!
怎么有种后患无穷的感觉....

因为重用的几率太小了(除非你设备时间错乱了而且正好跟你上一个包同一毫秒发送的),所以很安全。
Edit:

M = UTC 时间,精确到秒,取值为当前时间的前后 30 秒随机值(8 字节, Big Endian)

不过概率还是很小就对了

@k79e
Copy link

k79e commented Jan 1, 2020

如果有人搞碰撞 用机器生成呢? 这样会不会有影响.
不清楚cfb的iv如果碰撞会咋样 有谁知道来解说下么?

@kotori2
Copy link

kotori2 commented Jan 1, 2020

如果有人搞碰撞 用机器生成呢? 这样会不会有影响.

首先对于GCM来讲,不能通过修改iv的方法来使明文发生变化,因为GCM TAG有校验(建议自己Google)
其次泄露数据可能是因为,你用了同一个key和iv,第一个128b的数据又正好相同,导致加密结果也是完全相同的。这种只有知道key的人才可能触发。所以你说的情况不可能发生。

@kotori2
Copy link

kotori2 commented Jan 1, 2020

不清楚cfb的iv如果碰撞会咋样 有谁知道来解说下么?

IV 在 vmess里 是 算出来的 不是 直接传输的,作者肯定考虑到了这一点
如果你想修改iv来修改CFB里面的某些字节的话,理论上是行得通的,只需要把iv的对应位+1就行了,但是由于这是MD5出来的,所以你需要找到能符合你要求的时间,比如(这只是个栗子,实际不是这样算的):
MD5("2020-01-01 01:23:45") = "0102030405060708"
MD5("9281-04-32 08:25:01") = "0102030405060808"
这样才有可能。但是VMess为了防止这种攻击特地增加了时间检验,只能允许,比如与服务器时间相差只有10分钟的客户端连入,导致这种攻击不可行。除非你人品爆炸真算出来一个。

@aptx17
Copy link

aptx17 commented Jan 16, 2020

VMess 使用的加密方式(AES-GCM、AES-CFB、CHACHA20)暂时都没有已知的破解方法。这里的不能破解指的是,即使同时拿到了加密前和加密后的文本,也无法得到密钥。再多的文本也没用。

前向安全性
前向安全性(Forward Secrecy)指的是,假设某一天通讯的密钥破解了,而之前的某些通讯数据被截获,被截获的数据无法通过密钥解开。能做到一点的协议被称为具有前向安全性。

目前只有 TLS(和其它基于 TLS 的协议)实现了前向安全性,VMess 和 Shadowsocks 都不具有这一特性。VMess 不添加这个特性的原因是:

  1. V2Ray 已支持套用 TLS;
  2. 实现前向安全性就必须增加额外的密钥交换环节,会增加延时;
  3. 并不是所有人都关心这一特性;

@poly-1
Copy link
Author

poly-1 commented Jan 23, 2020

@poly-1 我还是觉得,对于数据块的IV不用担心重用碰撞问题。
你说的碰撞,前提是他IV是公开的,而在VMess协议里,数据块的IV和key都是随机生成的,而且是加密后存在控制数据里的,要碰撞也只能在key和IV均相同的情况碰撞,也就是10^-28。
所以我觉得更需要担心的是控制块的IV。因为他对于一个用户来讲是固定的key。不过由于他是MD5生成的,也许需要分析MD5(64b时间戳*4)重复的概率🤔

@kotori2 仔细看了一下,你分析的很对。数据块的IV和key都是随机生成的话,确实就安全很多了,可以说当前可以无需担心其安全性(不过隔多久更换随机key和IV可能是个问题?)

现在短板在于指令部分的安全性,指令部分现在的实现有利有弊吧,利是用了cfb而不是ctr等计数器模式,抗iv重用要强一些;弊是没有用aead不能防篡改。

@liberal-boy
Copy link
Member

@poly-1 vmess 的指令部分有 FNV1a 效验,篡改要如何实现?

@github-actions
Copy link

github-actions bot commented Sep 7, 2020

This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants
@k79e @Balnea @kotori2 @liberal-boy @aptx17 @poly-1 and others