首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Poly1305与ChaCha20的正确方式?

使用Poly1305与ChaCha20的正确方式?
EN

Stack Overflow用户
提问于 2020-07-16 11:36:48
回答 2查看 1.8K关注 0票数 1

我正在尝试使用来自ChaCha20-Poly1305模块的cryptography密码,但是只有ChaCha20密码和Poly1305 MAC可用。这就是我一开始尝试将它们结合起来的方式:

代码语言:javascript
复制
from cryptography.hazmat.primitives.poly1305 import Poly1305
from cryptography.hazmat.primitives.ciphers import (
    Cipher,
    algorithms as algo,
)
from cryptography.hazmat.backends import default_backend as defb


class ChaCha20Poly1305:
    def __init__(self, locking, key, nonce):
        self._locking = locking
        # only accepts 16 bytes nonce
        cipher = Cipher(algo.ChaCha20(key, nonce), None, defb())
        if locking:
            self._cipher = cipher.encryptor()
        else:
            self._cipher = cipher.decryptor()
        self._auth = Poly1305(key)
        self._auth.update(nonce)

    def update(self, data):
        ctxt = self._cipher.update(data)
        self._auth.update(ctxt)
        return ctxt

    def finalize(self, tag=None):
        if not self._locking
            if tag is None:
                raise ValueError('tag required')
            self._auth.verify(tag)

    def calculate_tag(self):
        return self._auth.calculate_tag()

这是在Poly1305中使用这个密码的正确方法吗?

编辑:虽然cryptography提供ChaCha20Poly1305,但它支持而不是不断地加密数据。它只需获取一段数据,对其进行加密,并以附加的MAC返回密文。这不是我想要的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-07-18 22:02:56

可以使用类似于ChaCha20Poly1305的密码技术实现PyCryptodome实现ChaCha20_Poly1305来实现流认证加密/解密。发布的代码基本上已经做到了这一点,因此缺少了以下要点或错误:

  • 密码学实现ChaCha20需要完整的16字节IV,即nonce (12字节)和计数器(4字节),以小endian格式s. RFC 7539秒2.3表示。
  • 计数器值0用于生成用于加密、s. RFC 7539秒2.4第2.6节Poly1305密钥、来自并包含1的计数器值。
  • Poly1305密钥不仅与加密密钥相对应,而且必须从该密钥和具有计数器0,s. RFC 7539秒2.6的当前密钥派生出来。
  • 除了Poly1305键的派生之外,nonce没有进一步参与标记的计算。
  • 附加的认证数据(AAD)必须考虑到,s. RFC 7539秒2.8
  • 在计算标记之前,数据( AAD,如果存在,则为密文)以一种定义的方式格式化,对AAD和密文使用零填充,并将AAD和密文的长度附加为一个8字节整数,按小字节顺序,s. RFC 7539秒2.8

下面的代码考虑到了这些要点,应该说明基本原理,并且必须/可以根据个人的需要进行调整:

代码语言:javascript
复制
from cryptography.hazmat.backends import default_backend as defb
from cryptography.hazmat.primitives.poly1305 import Poly1305
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers import algorithms as algo

class ChaCha20Poly1305:
    def __init__(self, encrypt, key, nonce):
        self._encrypt = encrypt
        self._dataLength = 0;
        self._aadLength = 0;
        self._nonceCounter = (0).to_bytes(4, byteorder='little') + nonce      # Create 16 bytes IV for Poly1305 key derivation
        self._nonceEncrypt = (1).to_bytes(4, byteorder='little') + nonce      # Create 16 bytes IV for encryption / decryption
        
        cipher = Cipher(algo.ChaCha20(key, self._nonceEncrypt), None, defb())
        if encrypt:
            self._cipher = cipher.encryptor()
        else:
            self._cipher = cipher.decryptor()
        
        polyKey = self.__getPolyKey(key)                                      # Get Poly1305 key 
        self._auth = Poly1305(polyKey)
    
    # Add AAD and zero pad if nnecessary (optional, may only be called once and before first 'update' call)    
    def updateAAD(self, aad):
        self._auth.update(aad)
        self._aadLength = len(aad)
        self._auth.update(self.__getZeroBytes(self._aadLength))

    # Add ciphertext / plaintext for encryption / decryption and actualize tag
    def update(self, data):
        ctxt = self._cipher.update(data)
        self._dataLength += len(ctxt)
        if self._encrypt:   
            self._auth.update(ctxt)
        else:
            self._auth.update(data)
        return ctxt

    # Complete padding and verify tag (only decryption)
    def verify_tag(self, tag=None):
        if not self._encrypt:
            self.__pad()
            if tag is None:
                raise ValueError('tag required')
            self._auth.verify(tag)
        else:
            raise ValueError('Tag verification only during decryption')

    # Complete padding and calculate tag (only encryption)
    def calculate_tag(self):
        if self._encrypt:
            self.__pad()
            return self._auth.finalize()
        else:
            raise ValueError('Tag calculation only during encryption')
        
    # Complete formatting: zero pad ciphertext, append AAD and ciphertext lengths
    def __pad(self):
        self._auth.update(self.__getZeroBytes(self._dataLength))
        self._auth.update(self._aadLength.to_bytes(8, byteorder='little'))
        self._auth.update(self._dataLength.to_bytes(8, byteorder='little'))

    # Zero pad data (AAD or ciphertext)
    def __getZeroBytes(self, len):
        spareBytes = len % 16
        if (spareBytes != 0):
            length = 16 - spareBytes
            return bytes([0]) * length
        return b''

    # Derive Poly1305 key
    def __getPolyKey(self, key):
        cipher = Cipher(algo.ChaCha20(key, self._nonceCounter), None, defb())
        cipher = cipher.encryptor()
        key = cipher.update(b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
        return key

该实现满足来自RFC 7539,sec 2.8.2的测试向量。

代码语言:javascript
复制
# Test vector from RFC 7539, sec 2.8.2
plaintext1 = b"Ladies and Gentlemen of the class "
plaintext2 = b"of '99: If I could offer you only one"
plaintext3 = b" tip for the future, sunscreen would be it."
nonce = bytes.fromhex("070000004041424344454647")
key = bytes.fromhex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")

# Encryption
ccEnc = ChaCha20Poly1305(True, key, nonce)
ccEnc.updateAAD(bytes.fromhex('50515253c0c1c2c3c4c5c6c7'))
ct1 = ccEnc.update(plaintext1)
ct2 = ccEnc.update(plaintext2)
ct3 = ccEnc.update(plaintext3)
tag = ccEnc.calculate_tag()

print("Ciphertext:\n%s\n" % (ct1 + ct2 + ct3).hex())
print("Tag:\n%s\n" % tag.hex())

# Decryption
ccDec = ChaCha20Poly1305(False, key, nonce)
ccDec.updateAAD(bytes.fromhex('50515253c0c1c2c3c4c5c6c7'))
dt1 = ccDec.update(ct1)
dt2 = ccDec.update(ct2)
dt3 = ccDec.update(ct3)
ccDec.verify_tag(tag)

print("Decrypted:\n%s\n" % (dt1 + dt2 + dt3))

注意:--当然,在解密数据被成功验证之前,不信任它是很重要的!就像PyCryptodome实现一样,构造诱惑使用未经验证的(可能是损坏的)数据。意见中已经详细指出了这一问题,并提出了更可靠的备选办法(另见另一答复中的联系员额)。

票数 3
EN

Stack Overflow用户

发布于 2020-07-16 17:59:23

它只需获取一段数据,对其进行加密,并以附加的MAC返回密文。这不是我想要的。

我想这是你真正想要的。这就是流行的AE (和AEAD)的工作原理。

顺便说一句,你见过

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62933858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档