文章目录
7. Encryption Schemes
简称RSAES。本部分介绍非对称加密RSA使用的加密填充/编码方案。
当RSA明文较短时,则会有选择密文攻击的风险。
选择密文攻击,Chosen Ciphertext Attack,CCA。
攻击者具备条件:解密流程可控,即选定密文,得到明文。
比如,e==3,m < n^(1/3),那只需要对密文求立方根就能解密。
有两个加解密方案:
- OAEP,适用于新应用
- PKCS1-v1_5,处理兼容性
应用:
- key establishment protocol, eg, PKCS #7 [RFC2315]
源码参考PyCryptodome \Crypto\Cipher\PKCS1_OAEP.py
。
7.1. RSAES-OAEP
RSA Encryption Schemes-Optimal(最优) Asymmetric Encryption Padding
相关技术:
- EME-OAEP encoding
- Bellare and Rogaway’s Optimal Asymmetric Encryption scheme [OAEP]
- Integer Factorization Encryption Scheme (IFES) defined in IEEE 1363 [IEEE1363]
- encryption primitives, IFEP-RSA
- decryption primitives, IFDP-RSA
- plaintext awareness (PA94) == plaintext awareness (PA98)
- The indifferent attack scenario (denoted CCA1) == the adaptive scenario (denoted CCA2)
可操作消息长度: k - 2hLen -2
octets
- hLen is the length of the output from the underlying hash function
- k is the length in octets of the recipient’s RSA modulus
7.1.1. Encryption Operation
def RSAES-OAEP-ENCRYPT ((n, e), M, L):...
# (n, e) recipient’s RSA public key
# M message to be encrypted, an octet string of length mLen, where mLen <= k - 2hLen - 2
# L optional label, default empty string
return C # ciphertext, an octet string of length k
Options:
- Hash, hash function (hLen denotes the length in octets of the hash function output)
- MGF, mask generation function (Appendix B.2)
steps:
- Length checking
# See 7.1.1 in RFC3447
modBits = Crypto.Util.number.size(self._key.n) # 1024
k = ceil_div(modBits, 8) # 128 Convert from bits to bytes
hLen = self._hashObj.digest_size # sha1 20
mLen = len(message) # eg 19
# Step 1a
# length of L
# Step 1b
ps_len = k - mLen - 2 * hLen - 2 # 67 = 128-19-40-2
if ps_len < 0:
raise ValueError("Plaintext is too long.")
- EME-OAEP encoding (see Figure 1 below)
# Step 2a
lHash = self._hashObj.new(self._label).digest()
# Step 2b padding string
ps = b'\x00' * ps_len
# Step 2c data block
# DB = lHash || PS || 0x01 || M
db = lHash + ps + b'\x01' + _copy_bytes(None, None, message)
# Step 2d
seed = self._randfunc(hLen)
# Step 2e
dbMask = self._mgf(seed, k-hLen-1)
# Step 2f
maskedDB = strxor(db, dbMask)
# Step 2g
seedMask = self._mgf(maskedDB, hLen)
# Step 2h
maskedSeed = strxor(ros, seedMask)
# Step 2i
# EM = 0x00 || maskedSeed || maskedDB
em = b'\x00' + maskedSeed + maskedDB
- RSA encryption:
# Step 3a (OS2IP)
em_int = bytes_to_long(em)
# Step 3b (RSAEP)
m_int = self._key._encrypt(em_int)
# Step 3c (I2OSP)
c = long_to_bytes(m_int, k)
return c
- Output the ciphertext C
Figure 1: EME-OAEP Encoding Operation
+----------+------+--+-------+
DB = | lHash | PS |01| M |
+----------+------+--+-------+
|
+----------+ |
| seed | |
+----------+ |
| |
|-------> MGF ---> xor
| |
+--+ V |
|00| xor <----- MGF <-----|
+--+ | |
| | |
V V V
+--+----------+----------------------------+
EM = |00|maskedSeed| maskedDB |
+--+----------+----------------------------+
EM长度填充为密钥长度。
BT为00,则PS==00,EM变成了小数,减小了计算量。
7.1.2. Decryption Operation
def RSAES-OAEP-DECRYPT (K, C, L):
# K recipient’s RSA private key (k denotes the length inoctets of the RSA modulus n), where k >= 2hLen + 2
# C ciphertext to be decrypted, an octet string of length k
# L optional label
return M # message, an octet string of length mLen, where mLen <= k - 2hLen - 2
steps:
- Length checking
# See 7.1.2 in RFC3447
modBits = Crypto.Util.number.size(self._key.n)
k = ceil_div(modBits,8) # Convert from bits to bytes
hLen = self._hashObj.digest_size
# Step 1b and 1c
if len(ciphertext) != k or k<hLen+2:
raise ValueError("Ciphertext with incorrect length.")
- RSA decryption
# Step 2a (O2SIP)
ct_int = bytes_to_long(ciphertext)
# Step 2b (RSADP)
m_int = self._key._decrypt(ct_int)
# Complete step 2c (I2OSP)
em = long_to_bytes(m_int, k)
- EME-OAEP decoding
- Output the message M
# Step 3a
lHash = self._hashObj.new(self._label).digest()
# Step 3b
# EM = Y || maskedSeed || maskedDB
y = em[0]
# y must be 0, but we MUST NOT check it here in order not to
# allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143)
maskedSeed = em[1:hLen+1]
maskedDB = em[hLen+1:] # length k - hLen - 1
# Step 3c
seedMask = self._mgf(maskedDB, hLen)
# Step 3d
seed = strxor(maskedSeed, seedMask)
# Step 3e
dbMask = self._mgf(seed, k-hLen-1)
# Step 3f
db = strxor(maskedDB, dbMask)
# Step 3g
# DB = lHash’ || PS || 0x01 || M
one_pos = hLen + db[hLen:].find(b'\x01')
lHash1 = db[:hLen]
invalid = bord(y) | int(one_pos < hLen)
hash_compare = strxor(lHash1, lHash)
for x in hash_compare:
invalid |= bord(x)
for x in db[hLen:one_pos]:
invalid |= bord(x)
if invalid != 0:
raise ValueError("Incorrect decryption.")
# Step 4
return db[one_pos + 1:]
Appendix B. Supporting Techniques
B.2. Mask Generation Functions
MGF是一个依赖于hash函数的伪随机函数,应用于RSAES-OAEP和RSASSA-PSS。
PyCryptodome源码:\Crypto\Signature\pss.py
def MGF1(mgfSeed, maskLen, hash_gen):
T = b""
for counter in iter_range(ceil_div(maskLen, hash_gen.digest_size)):
c = long_to_bytes(counter, 4)
hobj = hash_gen.new()
hobj.update(mgfSeed + c)
T = T + hobj.digest()
assert(len(T) >= maskLen)
return T[:maskLen]
C实现:https://github.com/C0deStarr/CryptoImp/tree/main/common/util.h , MGF()
实现
https://github.com/C0deStarr/CryptoImp/tree/main/pubkey/rsa
- pkcs1_oaep.c
- pkcs1_oaep.h
参考资料
RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2