comparison lib/Test/Nginx/HTTP3.pm @ 1909:46bb1ffbb960

Tests: TLS_CHACHA20_POLY1305_SHA256 support in QUIC handshake.
author Sergey Kandaurov <pluknet@nginx.com>
date Tue, 13 Jun 2023 17:58:30 +0400
parents 1baf5fe1d86c
children e0b53fbdb5cf
comparison
equal deleted inserted replaced
1908:1baf5fe1d86c 1909:46bb1ffbb960
24 24
25 require Crypt::KeyDerivation; 25 require Crypt::KeyDerivation;
26 require Crypt::PK::X25519; 26 require Crypt::PK::X25519;
27 require Crypt::PRNG; 27 require Crypt::PRNG;
28 require Crypt::AuthEnc::GCM; 28 require Crypt::AuthEnc::GCM;
29 require Crypt::AuthEnc::ChaCha20Poly1305;
29 require Crypt::Mode::CTR; 30 require Crypt::Mode::CTR;
31 require Crypt::Stream::ChaCha;
30 require Crypt::Digest; 32 require Crypt::Digest;
31 require Crypt::Mac::HMAC; 33 require Crypt::Mac::HMAC;
32 34
33 $self->{socket} = IO::Socket::INET->new( 35 $self->{socket} = IO::Socket::INET->new(
34 Proto => "udp", 36 Proto => "udp",
1654 } 1656 }
1655 1657
1656 return $pn; 1658 return $pn;
1657 } 1659 }
1658 1660
1661 sub decrypt_aead_f {
1662 my ($level, $cipher) = @_;
1663 if ($level == 0 || $cipher == 0x1301 || $cipher == 0x1302) {
1664 return \&Crypt::AuthEnc::GCM::gcm_decrypt_verify, 'AES';
1665 }
1666 \&Crypt::AuthEnc::ChaCha20Poly1305::chacha20poly1305_decrypt_verify;
1667 }
1668
1659 sub decrypt_aead { 1669 sub decrypt_aead {
1660 my ($self, $buf) = @_; 1670 my ($self, $buf) = @_;
1661 my $flags = unpack("C", substr($buf, 0, 1)); 1671 my $flags = unpack("C", substr($buf, 0, 1));
1662 return 0, $self->decrypt_retry($buf) if ($flags & 0xf0) == 0xf0; 1672 return 0, $self->decrypt_retry($buf) if ($flags & 0xf0) == 0xf0;
1663 my $level = $flags & 0x80 ? $flags - 0xc0 >> 4 : 3; 1673 my $level = $flags & 0x80 ? $flags - 0xc0 >> 4 : 3;
1675 : (0, length($buf) - $offpn); 1685 : (0, length($buf) - $offpn);
1676 $offpn += $len; 1686 $offpn += $len;
1677 1687
1678 my $sample = substr($buf, $offpn + 4, 16); 1688 my $sample = substr($buf, $offpn + 4, 16);
1679 my ($ad, $pnl, $pn) = $self->decrypt_ad($buf, 1689 my ($ad, $pnl, $pn) = $self->decrypt_ad($buf,
1680 $self->{keys}[$level]{r}{hp}, $sample, $offpn, $level == 3); 1690 $self->{keys}[$level]{r}{hp}, $sample, $offpn, $level);
1681 Test::Nginx::log_core('||', "ad = " . unpack("H*", $ad)); 1691 Test::Nginx::log_core('||', "ad = " . unpack("H*", $ad));
1682 $pn = $self->decode_pn($pn, $pnl, $level); 1692 $pn = $self->decode_pn($pn, $pnl, $level);
1683 my $nonce = substr(pack("x12") . pack("N", $pn), -12) 1693 my $nonce = substr(pack("x12") . pack("N", $pn), -12)
1684 ^ $self->{keys}[$level]{r}{iv}; 1694 ^ $self->{keys}[$level]{r}{iv};
1685 my $ciphertext = substr($buf, $offpn + $pnl, $val - 16 - $pnl); 1695 my $ciphertext = substr($buf, $offpn + $pnl, $val - 16 - $pnl);
1686 my $tag = substr($buf, $offpn + $val - 16, 16); 1696 my $tag = substr($buf, $offpn + $val - 16, 16);
1687 my $plaintext = Crypt::AuthEnc::GCM::gcm_decrypt_verify('AES', 1697 my ($f, @args) = decrypt_aead_f($level, $self->{cipher});
1698 my $plaintext = $f->(@args,
1688 $self->{keys}[$level]{r}{key}, $nonce, $ad, $ciphertext, $tag); 1699 $self->{keys}[$level]{r}{key}, $nonce, $ad, $ciphertext, $tag);
1689 return if !defined $plaintext; 1700 return if !defined $plaintext;
1690 Test::Nginx::log_core('||', 1701 Test::Nginx::log_core('||',
1691 "pn = $pn, level = $level, length = " . length($plaintext)); 1702 "pn = $pn, level = $level, length = " . length($plaintext));
1692 1703
1697 return ($level, $plaintext, 1708 return ($level, $plaintext,
1698 substr($buf, length($ad . $ciphertext . $tag)), ''); 1709 substr($buf, length($ad . $ciphertext . $tag)), '');
1699 } 1710 }
1700 1711
1701 sub decrypt_ad { 1712 sub decrypt_ad {
1702 my ($self, $buf, $hp, $sample, $offset, $short) = @_; 1713 my ($self, $buf, $hp, $sample, $offset, $level) = @_;
1714
1715 goto aes if $level == 0 || $self->{cipher} != 0x1303;
1716
1717 my $counter = unpack("V", substr($sample, 0, 4));
1718 my $nonce = substr($sample, 4, 12);
1719 my $stream = Crypt::Stream::ChaCha->new($hp, $nonce, $counter);
1720 my $mask = $stream->crypt($self->{zero});
1721 goto mask;
1722 aes:
1703 my $m = Crypt::Mode::CTR->new('AES'); 1723 my $m = Crypt::Mode::CTR->new('AES');
1704 my $mask = $m->encrypt($self->{zero}, $hp, $sample); 1724 $mask = $m->encrypt($self->{zero}, $hp, $sample);
1705 substr($buf, 0, 1) ^= substr($mask, 0, 1) & ($short ? "\x1f" : "\x0f"); 1725 mask:
1726 substr($buf, 0, 1) ^= substr($mask, 0, 1)
1727 & ($level == 3 ? "\x1f" : "\x0f");
1706 my $pnl = unpack("C", substr($buf, 0, 1) & "\x03") + 1; 1728 my $pnl = unpack("C", substr($buf, 0, 1) & "\x03") + 1;
1707 for (my $i = 0; $i < $pnl; $i++) { 1729 substr($buf, $offset, $pnl) ^= substr($mask, 1);
1708 substr($buf, $offset + $i, 1) ^= substr($mask, $i + 1, 1);
1709 }
1710 my $pn = unpack("C", substr($buf, $offset, $pnl)); 1730 my $pn = unpack("C", substr($buf, $offset, $pnl));
1711 my $ad = substr($buf, 0, $offset + $pnl); 1731 my $ad = substr($buf, 0, $offset + $pnl);
1712 return ($ad, $pnl, $pn); 1732 return ($ad, $pnl, $pn);
1733 }
1734
1735 sub encrypt_aead_f {
1736 my ($level, $cipher) = @_;
1737 if ($level == 0 || $cipher == 0x1301 || $cipher == 0x1302) {
1738 return \&Crypt::AuthEnc::GCM::gcm_encrypt_authenticate, 'AES';
1739 }
1740 \&Crypt::AuthEnc::ChaCha20Poly1305::chacha20poly1305_encrypt_authenticate;
1713 } 1741 }
1714 1742
1715 sub encrypt_aead { 1743 sub encrypt_aead {
1716 my ($self, $payload, $level) = @_; 1744 my ($self, $payload, $level) = @_;
1717 my $pn = ++$self->{pn}[0][$level]; 1745 my $pn = ++$self->{pn}[0][$level];
1724 if $level == 0; 1752 if $level == 0;
1725 $ad .= build_int(length($payload) + 16 + 4) unless $level == 3; 1753 $ad .= build_int(length($payload) + 16 + 4) unless $level == 3;
1726 $ad .= pack("N", $pn); 1754 $ad .= pack("N", $pn);
1727 my $nonce = substr(pack("x12") . pack("N", $pn), -12) 1755 my $nonce = substr(pack("x12") . pack("N", $pn), -12)
1728 ^ $self->{keys}[$level]{w}{iv}; 1756 ^ $self->{keys}[$level]{w}{iv};
1729 my ($ciphertext, $tag) = Crypt::AuthEnc::GCM::gcm_encrypt_authenticate( 1757 my ($f, @args) = encrypt_aead_f($level, $self->{cipher});
1730 'AES', $self->{keys}[$level]{w}{key}, $nonce, $ad, $payload); 1758 my ($ciphertext, $tag) = $f->(@args,
1759 $self->{keys}[$level]{w}{key}, $nonce, $ad, $payload);
1731 my $sample = substr($ciphertext . $tag, 0, 16); 1760 my $sample = substr($ciphertext . $tag, 0, 16);
1732 1761
1733 $ad = $self->encrypt_ad($ad, $self->{keys}[$level]{w}{hp}, 1762 $ad = $self->encrypt_ad($ad, $self->{keys}[$level]{w}{hp},
1734 $sample, $level == 3); 1763 $sample, $level);
1735 return $ad . $ciphertext . $tag; 1764 return $ad . $ciphertext . $tag;
1736 } 1765 }
1737 1766
1738 sub encrypt_ad { 1767 sub encrypt_ad {
1739 my ($self, $ad, $hp, $sample, $short) = @_; 1768 my ($self, $ad, $hp, $sample, $level) = @_;
1769
1770 goto aes if $level == 0 || $self->{cipher} != 0x1303;
1771
1772 my $counter = unpack("V", substr($sample, 0, 4));
1773 my $nonce = substr($sample, 4, 12);
1774 my $stream = Crypt::Stream::ChaCha->new($hp, $nonce, $counter);
1775 my $mask = $stream->crypt($self->{zero});
1776 goto mask;
1777 aes:
1740 my $m = Crypt::Mode::CTR->new('AES'); 1778 my $m = Crypt::Mode::CTR->new('AES');
1741 my $mask = $m->encrypt($self->{zero}, $hp, $sample); 1779 $mask = $m->encrypt($self->{zero}, $hp, $sample);
1742 substr($ad, 0, 1) ^= substr($mask, 0, 1) & ($short ? "\x1f" : "\x0f"); 1780 mask:
1781 substr($ad, 0, 1) ^= substr($mask, 0, 1)
1782 & ($level == 3 ? "\x1f" : "\x0f");
1743 substr($ad, -4) ^= substr($mask, 1); 1783 substr($ad, -4) ^= substr($mask, 1);
1744 return $ad; 1784 return $ad;
1745 } 1785 }
1746 1786
1747 sub decrypt_retry { 1787 sub decrypt_retry {