Class ProtectedPacket

java.lang.Object
io.github.mahmoudimus.quic.packet.ProtectedPacket

public final class ProtectedPacket extends Object
A packet whose unprotected header fields are parsed but whose packet number and payload are still protected: quinn's PartialDecode. The version, type and connection IDs are enough to pick the keys; the sample offset is where header protection samples. Removing protection works in place on the datagram buffer:

 ProtectedPacket p = ProtectedPacket.parse(datagram, localIdLength);
 PacketHeader.Numbered h = p.removeHeaderProtection(keys.remote().header(), largestReceived);
 Packet packet = p.decrypt(keys.remote().packet()); // null if authentication failed
 

A Retry or Version Negotiation packet is fully decoded by parse(java.nio.ByteBuffer, int); see header().

Instances are single-use and not thread-safe; they share content with the datagram buffer.

  • Method Details

    • parse

      public static ProtectedPacket parse(ByteBuffer datagram, int shortHeaderIdLength) throws PacketDecodeException
      Parses the packet at the buffer's position, requiring the Fixed Bit.
      Parameters:
      datagram - the datagram; on success its position moves to the end of this packet, which for short headers, Retry and Version Negotiation is the buffer's limit
      shortHeaderIdLength - the length of this endpoint's connection IDs, which short headers do not encode
      Returns:
      the packet
      Throws:
      UnsupportedVersionException - for a long header of a version other than 1 and 2
      PacketDecodeException - if the header is malformed; the position is then unchanged
    • parse

      public static ProtectedPacket parse(ByteBuffer datagram, int shortHeaderIdLength, boolean allowClearFixedBit) throws PacketDecodeException
      Parses the packet at the buffer's position.
      Parameters:
      datagram - the datagram; on success its position moves to the end of this packet
      shortHeaderIdLength - the length of this endpoint's connection IDs
      allowClearFixedBit - whether to accept a cleared Fixed Bit, as an endpoint that sent the grease_quic_bit transport parameter must (RFC 9287)
      Returns:
      the packet
      Throws:
      UnsupportedVersionException - for a long header of a version other than 1 and 2
      PacketDecodeException - if the header is malformed; the position is then unchanged
    • splitDatagram

      public static List<ProtectedPacket> splitDatagram(ByteBuffer datagram, int shortHeaderIdLength, boolean allowClearFixedBit) throws PacketDecodeException
      Splits a datagram into its coalesced packets (RFC 9000 section 12.2). Parsing stops, and the rest of the datagram is ignored, at the first packet after the first that fails to parse or carries a different Destination Connection ID.
      Parameters:
      datagram - the datagram between its position and limit; the buffer's position is not changed, but protection is later removed in its content
      shortHeaderIdLength - the length of this endpoint's connection IDs
      allowClearFixedBit - whether to accept a cleared Fixed Bit (RFC 9287)
      Returns:
      the packets, at least one
      Throws:
      PacketDecodeException - if the first packet cannot be parsed
    • type

      public PacketType type()
      The packet type.
      Returns:
      the type
    • version

      public QuicVersion version()
      The QUIC version of a long header.
      Returns:
      the version, or null for short headers and Version Negotiation
    • destinationId

      public ConnectionId destinationId()
      The Destination Connection ID.
      Returns:
      the connection ID
    • sourceId

      public ConnectionId sourceId()
      The Source Connection ID of a long header.
      Returns:
      the connection ID, or null for short headers
    • token

      public byte[] token()
      The token of an Initial or Retry packet.
      Returns:
      a copy of the token, or null for other types
    • start

      public int start()
      The index of the first byte of this packet in the datagram buffer.
      Returns:
      the absolute start index
    • end

      public int end()
      The index just past this packet in the datagram buffer.
      Returns:
      the absolute end index
    • length

      public int length()
      The packet length, header and tag included.
      Returns:
      end() - start()
    • packetNumberOffset

      public int packetNumberOffset()
      The index of the (protected) packet number.
      Returns:
      the absolute index, or -1 for Retry and Version Negotiation
    • sampleOffset

      public int sampleOffset()
      The index of the header protection sample: four bytes past the start of the packet number.
      Returns:
      the absolute index, or -1 for Retry and Version Negotiation
    • bytes

      public ByteBuffer bytes()
      A view of the whole packet, header protection included or removed as it stands.
      Returns:
      a new buffer over [start(), end())
    • header

      public PacketHeader header()
      The decoded header: available at once for Retry and Version Negotiation packets, and for protected packets after removeHeaderProtection(io.github.mahmoudimus.quic.crypto.HeaderKey, long).
      Returns:
      the header, or null before header protection is removed
    • verifyRetry

      public boolean verifyRetry(ConnectionId originalDestinationId)
      Checks the integrity tag of a Retry packet (RFC 9001 section 5.8).
      Parameters:
      originalDestinationId - the Destination Connection ID of the client's first Initial
      Returns:
      true if the tag is valid
      Throws:
      IllegalStateException - if this is not a Retry packet
    • removeHeaderProtection

      public PacketHeader.Numbered removeHeaderProtection(HeaderKey key, long largestPacketNumber)
      Removes header protection in place and recovers the full packet number.
      Parameters:
      key - the header protection key of the packet's level (it is the same for both key phases)
      largestPacketNumber - the largest packet number successfully processed in this packet number space, or -1 if none
      Returns:
      the header; for short headers it carries the key phase that picks the packet key
      Throws:
      IllegalStateException - if the packet is not protected or protection was already removed
    • decrypt

      public Packet decrypt(PacketKey key) throws QuicException
      Opens the payload in place, once header protection is removed. After authentication succeeds, the reserved header bits must be zero (RFC 9000 section 17).
      Parameters:
      key - the packet key for the packet's level and, for 1-RTT, its key phase
      Returns:
      the packet, or null if it failed authentication (drop it; its payload bytes are then unspecified)
      Throws:
      QuicException - with PROTOCOL_VIOLATION if an authenticated packet has reserved bits set
      IllegalStateException - if header protection was not removed first
    • unprotect

      public Packet unprotect(DirectionalKeys keys, long largestPacketNumber) throws QuicException
      Removes header and payload protection in one step, for levels without key phases.
      Parameters:
      keys - the receiver's keys for the packet's level (Keys.remote())
      largestPacketNumber - the largest packet number processed in this space, or -1
      Returns:
      the packet, or null if it failed authentication
      Throws:
      QuicException - with PROTOCOL_VIOLATION if reserved bits are set