Module: PQCrypto::Seal::Format

Defined in:
lib/pq_crypto/seal/format.rb

Defined Under Namespace

Classes: Header, HeaderCodec, InnerCodec, Section, SectionCodec, VerifiedInner

Constant Summary collapse

MAGIC =
"PQCSEAL1".b
VERSION =
1
CONTENT_SUITE_AEGIS256 =
1
WRAP_SUITE_MLKEM768_X25519_AEGIS256 =
1
LOOKUP_HINT =
1
FLAGS =
0
INNER_VERSION =
1
INNER_FLAGS =
0
PADDING_NONE =
0
PADDING_PADME =
1
PADDING_FIXED =
2
PADDING_BUCKETS =
3
PADDING_POLICY_IDS =
[PADDING_NONE, PADDING_PADME, PADDING_FIXED, PADDING_BUCKETS].freeze
PAYLOAD_ID_BYTES =
32
NONCE_BYTES =
32
TAG_BYTES =
32
HINT_BYTES =
32
SECTION_ID_BYTES =
32
XWING_PUBLIC_KEY_BYTES =
1216
XWING_CIPHERTEXT_BYTES =
1120
XWING_SHARED_SECRET_BYTES =
32
DEK_BYTES =
32
STANZA_BYTES =
HINT_BYTES + XWING_CIPHERTEXT_BYTES + NONCE_BYTES + DEK_BYTES + TAG_BYTES
DEFAULT_SLOT_SIZE =
2048
MIN_SLOT_SIZE =
2048
MAX_SLOT_SIZE =
8192
SLOT_GRANULARITY =
256
DEFAULT_RECIPIENT_CAPACITY =
4
MAX_RECIPIENT_CAPACITY =
32
MAX_PUBLIC_METADATA_BYTES =
1 * 1024 * 1024
MAX_PRIVATE_METADATA_BYTES =
16 * 1024 * 1024
MAX_HEADER_BYTES =
2 * 1024 * 1024
DEFAULT_MAX_PLAINTEXT_BYTES =
64 * 1024 * 1024
DEFAULT_MAX_STAGING_BYTES =
DEFAULT_MAX_PLAINTEXT_BYTES + MAX_PRIVATE_METADATA_BYTES + 64
DEFAULT_MAX_ENVELOPE_BYTES =
MAX_HEADER_BYTES +
(2 + SECTION_ID_BYTES + MAX_RECIPIENT_CAPACITY * MAX_SLOT_SIZE) +
DEFAULT_MAX_STAGING_BYTES +
TAG_BYTES
LIMIT_DEFAULTS =
{
  max_staging_bytes: DEFAULT_MAX_STAGING_BYTES,
  max_plaintext_bytes: DEFAULT_MAX_PLAINTEXT_BYTES,
  max_envelope_bytes: DEFAULT_MAX_ENVELOPE_BYTES
}.freeze
HEADER_PREFIX_BYTES =
MAGIC.bytesize + 1 + 4
INNER_PREFIX_BYTES =
14
SLOT_FIELDS =
[
  [:hint, HINT_BYTES],
  [:kem_ciphertext, XWING_CIPHERTEXT_BYTES],
  [:wrap_nonce, NONCE_BYTES],
  [:wrapped_dek, DEK_BYTES],
  [:wrap_tag, TAG_BYTES]
].freeze

Class Method Summary collapse

Class Method Details

.build_header(**attributes) ⇒ Object



264
265
266
# File 'lib/pq_crypto/seal/format.rb', line 264

def build_header(**attributes)
  HeaderCodec.build(**attributes)
end

.inner_prefix(content_length, private_metadata_length) ⇒ Object



293
294
295
# File 'lib/pq_crypto/seal/format.rb', line 293

def inner_prefix(content_length, )
  InnerCodec.prefix(content_length, )
end

.parse_header(bytes) ⇒ Object



268
269
270
# File 'lib/pq_crypto/seal/format.rb', line 268

def parse_header(bytes)
  HeaderCodec.parse(bytes)
end

.parse_section(bytes, offset, header) ⇒ Object



280
281
282
# File 'lib/pq_crypto/seal/format.rb', line 280

def parse_section(bytes, offset, header)
  SectionCodec.parse(bytes, offset, header)
end

.parse_verified_inner(bytes) ⇒ Object



297
298
299
300
# File 'lib/pq_crypto/seal/format.rb', line 297

def parse_verified_inner(bytes)
  inner = InnerCodec.parse(bytes)
  [inner.content, inner., inner.padding_bytes]
end

.section_length(header) ⇒ Object



272
273
274
# File 'lib/pq_crypto/seal/format.rb', line 272

def section_length(header)
  section_length_for(header.recipient_capacity, header.slot_size)
end

.section_length_for(capacity, slot_size) ⇒ Object



276
277
278
# File 'lib/pq_crypto/seal/format.rb', line 276

def section_length_for(capacity, slot_size)
  2 + SECTION_ID_BYTES + Integer(capacity) * Integer(slot_size)
end

.split_slot(slot, slot_size) ⇒ Object



284
285
286
287
288
289
290
291
# File 'lib/pq_crypto/seal/format.rb', line 284

def split_slot(slot, slot_size)
  reader = Binary::Reader.new(slot)
  fields = SLOT_FIELDS.each_with_object({}) do |(name, length), result|
    result[name] = reader.bytes(length)
  end
  fields[:padding] = reader.bytes(Integer(slot_size) - reader.offset)
  fields
end

.validate_capacity!(capacity, recipient_count = nil, error: InvalidConfigurationError) ⇒ Object

Raises:

  • (error)


249
250
251
252
253
254
255
# File 'lib/pq_crypto/seal/format.rb', line 249

def validate_capacity!(capacity, recipient_count = nil, error: InvalidConfigurationError)
  value = Integer(capacity)
  raise error, "recipient_capacity must be 1..#{MAX_RECIPIENT_CAPACITY}" unless value.between?(1, MAX_RECIPIENT_CAPACITY)
  raise RecipientCapacityExceeded, "#{recipient_count} recipients do not fit capacity #{value}" if recipient_count && recipient_count > value

  value
end

.validate_padding_policy_id!(policy_id, error: FormatError) ⇒ Object

Raises:

  • (error)


257
258
259
260
261
262
# File 'lib/pq_crypto/seal/format.rb', line 257

def validate_padding_policy_id!(policy_id, error: FormatError)
  value = Integer(policy_id)
  raise error, "unsupported padding policy id #{value}" unless PADDING_POLICY_IDS.include?(value)

  value
end

.validate_slot_size!(slot_size, error: InvalidConfigurationError) ⇒ Object

Raises:

  • (error)


240
241
242
243
244
245
246
247
# File 'lib/pq_crypto/seal/format.rb', line 240

def validate_slot_size!(slot_size, error: InvalidConfigurationError)
  size = Integer(slot_size)
  valid = size.between?(MIN_SLOT_SIZE, MAX_SLOT_SIZE) && (size % SLOT_GRANULARITY).zero?
  raise error, "slot_size must be #{MIN_SLOT_SIZE}..#{MAX_SLOT_SIZE} and divisible by #{SLOT_GRANULARITY}" unless valid
  raise error, "slot_size is too small for current wrap suite" if size < STANZA_BYTES

  size
end