thekuwayama/tttls1.3

View on GitHub
lib/tttls1.3/message/extension/supported_versions.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# encoding: ascii-8bit
# frozen_string_literal: true

module TTTLS13
  using Refinements
  module Message
    module Extension
      class SupportedVersions
        attr_reader :extension_type
        attr_reader :msg_type
        attr_reader :versions

        # @param msg_type [TTTLS13::Message::ContentType]
        # @param versions [Array of ProtocolVersion]
        #
        # @raise [TTTLS13::Error::ErrorAlerts]
        # rubocop: disable Metrics/CyclomaticComplexity
        def initialize(msg_type:, versions: DEFAULT_VERSIONS)
          @extension_type = ExtensionType::SUPPORTED_VERSIONS
          @msg_type = msg_type
          @versions = versions || []
          case @msg_type
          when HandshakeType::CLIENT_HELLO
            raise Error::ErrorAlerts, :internal_error \
              if @versions.empty? || @versions.length > 127
          when HandshakeType::SERVER_HELLO, HandshakeType::HELLO_RETRY_REQUEST
            raise Error::ErrorAlerts, :internal_error \
              unless @versions.length == 1
          else
            raise Error::ErrorAlerts, :internal_error
          end
        end
        # rubocop: enable Metrics/CyclomaticComplexity

        # @return [String]
        def serialize
          binary = ''
          binary += (@versions.length * 2).to_uint8 \
            if @msg_type == HandshakeType::CLIENT_HELLO
          binary += @versions.join

          @extension_type + binary.prefix_uint16_length
        end

        # @param binary [String]
        # @param msg_type [TTTLS13::Message::HandshakeType]
        #
        # @raise [TTTLS13::Error::ErrorAlerts]
        #
        # @return [TTTLS13::Message::Extensions::SupportedVersions, nil]
        def self.deserialize(binary, msg_type)
          raise Error::ErrorAlerts, :internal_error if binary.nil?

          versions = []
          case msg_type
          when HandshakeType::CLIENT_HELLO
            versions = deserialize_versions(binary)
            return nil if versions.nil? # unparsable versions

          when HandshakeType::SERVER_HELLO, HandshakeType::HELLO_RETRY_REQUEST
            return nil if binary.length != 2

            versions << binary.slice(0, 2)
          else
            return nil
          end
          SupportedVersions.new(msg_type: msg_type, versions: versions)
        end

        # @param binary [String]
        #
        # @raise [TTTLS13::Error::ErrorAlerts]
        #
        # @return [Array of String, nil]
        # rubocop: disable Metrics/CyclomaticComplexity
        def self.deserialize_versions(binary)
          raise Error::ErrorAlerts, :internal_error if binary.nil?

          return nil if binary.empty?

          versions_len = Convert.bin2i(binary[0])
          i = 1
          versions = []
          while i < versions_len + 1
            return nil if i + 2 > binary.length

            versions << binary.slice(i, 2)
            i += 2
          end
          return nil if i != binary.length || i != versions_len + 1

          versions
        end
        # rubocop: enable Metrics/CyclomaticComplexity
      end
    end
  end
end