Class: PqcAsn1::DER::KeyInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/pqc_asn1.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(oid, parameters, key, public_key, format) ⇒ KeyInfo

Returns a new instance of KeyInfo.

API:

  • private — constructed by DER.parse_spki / DER.parse_pkcs8 via C extension



297
298
299
300
301
302
303
304
# File 'lib/pqc_asn1.rb', line 297

def initialize(oid, parameters, key, public_key, format)
  @oid = oid
  @parameters = parameters
  @key = key
  @public_key = public_key
  @format = format
  freeze
end

Instance Attribute Details

#format ⇒ Symbol (readonly)

Returns :spki or :pkcs8.

Returns:

  • :spki or :pkcs8



294
295
296
# File 'lib/pqc_asn1.rb', line 294

def format
  @format
end

#key ⇒ String, PqcAsn1::SecureBuffer (readonly)

Returns key bytes. For :spki this is the public key (frozen String). For :pkcs8 this is a SecureBuffer holding the secret key.

Returns:

  • key bytes. For :spki this is the public key (frozen String). For :pkcs8 this is a SecureBuffer holding the secret key.



287
288
289
# File 'lib/pqc_asn1.rb', line 287

def key
  @key
end

#oid ⇒ PqcAsn1::OID (readonly)

Returns parsed algorithm OID.

Returns:

  • parsed algorithm OID



278
279
280
# File 'lib/pqc_asn1.rb', line 278

def oid
  @oid
end

#parameters ⇒ String? (readonly)

Returns raw AlgorithmIdentifier parameter bytes (ASCII-8BIT, frozen), or nil when absent.

Returns:

  • raw AlgorithmIdentifier parameter bytes (ASCII-8BIT, frozen), or nil when absent.



282
283
284
# File 'lib/pqc_asn1.rb', line 282

def parameters
  @parameters
end

#public_key ⇒ String? (readonly)

Returns optional public key from the PKCS#8 OneAsymmetricKey publicKey [1] field, or nil if absent.

Returns:

  • optional public key from the PKCS#8 OneAsymmetricKey publicKey [1] field, or nil if absent.



291
292
293
# File 'lib/pqc_asn1.rb', line 291

def public_key
  @public_key
end

Class Method Details

.detect_format(der) ⇒ Symbol

Detect whether DER bytes are SPKI or PKCS#8.

Parameters:

  • DER bytes

Returns:

  • :spki or :pkcs8

Raises:

  • if unrecognised



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/pqc_asn1.rb', line 276

class KeyInfo
  # @return [PqcAsn1::OID] parsed algorithm OID
  attr_reader :oid

  # @return [String, nil] raw AlgorithmIdentifier parameter bytes
  #   (ASCII-8BIT, frozen), or nil when absent.
  attr_reader :parameters

  # @return [String, PqcAsn1::SecureBuffer] key bytes.
  #   For :spki this is the public key (frozen String).
  #   For :pkcs8 this is a SecureBuffer holding the secret key.
  attr_reader :key

  # @return [String, nil] optional public key from the PKCS#8
  #   OneAsymmetricKey publicKey [1] field, or nil if absent.
  attr_reader :public_key

  # @return [Symbol] :spki or :pkcs8
  attr_reader :format

  # @api private — constructed by DER.parse_spki / DER.parse_pkcs8 via C extension
  def initialize(oid, parameters, key, public_key, format)
    @oid = oid
    @parameters = parameters
    @key = key
    @public_key = public_key
    @format = format
    freeze
  end

  # @return [Hash{Symbol => Object}]
  def to_h
    {oid: @oid, parameters: @parameters, key: @key,
     public_key: @public_key, format: @format}
  end

  # Pattern-matching support (Ruby 2.7+).
  # When +keys+ is non-nil, only the requested keys are returned,
  # avoiding unnecessary access to fields like +key+ (a SecureBuffer
  # that requires mprotect toggling).
  # @param keys [Array<Symbol>, nil]
  # @return [Hash{Symbol => Object}]
  def deconstruct_keys(keys)
    return to_h if keys.nil?

    keys.each_with_object({}) do |k, h|
      case k
      when :oid then h[:oid] = @oid
      when :parameters then h[:parameters] = @parameters
      when :key then h[:key] = @key
      when :public_key then h[:public_key] = @public_key
      when :format then h[:format] = @format
      end
    end
  end

  # @param other [Object]
  # @return [Boolean]
  def ==(other)
    other.is_a?(KeyInfo) &&
      @oid == other.oid &&
      @parameters == other.parameters &&
      @key == other.key &&
      @public_key == other.public_key &&
      @format == other.format
  end

  alias_method :eql?, :==

  # @return [Integer]
  # For :pkcs8, the secret key is excluded from the hash to avoid
  # leaking key material into hash tables or log output.
  def hash
    if @format == :pkcs8
      [@oid, @parameters, @public_key, @format].hash
    else
      [@oid, @parameters, @key, @public_key, @format].hash
    end
  end

  # Human-readable algorithm name (e.g. "ML_DSA_44"), or the dotted
  # OID string if the algorithm is not a known constant.
  # @return [String]
  def algorithm
    @oid.name || @oid.dotted
  end

  # Re-encode this KeyInfo back to DER bytes.
  # For :spki returns a frozen binary String.
  # For :pkcs8 returns a SecureBuffer.
  # @return [String, PqcAsn1::SecureBuffer]
  def to_der
    opts = {validate: false}
    opts[:parameters] = @parameters if @parameters
    opts[:public_key] = @public_key if @public_key

    case @format
    when :spki
      PqcAsn1::DER.build_spki(@oid, @key, **opts)
    when :pkcs8
      PqcAsn1::DER.build_pkcs8(@oid, @key, **opts)
    else
      raise PqcAsn1::Error, "cannot re-encode format #{@format.inspect}"
    end
  end

  # Re-encode to PEM.  Uses "PUBLIC KEY" for :spki and
  # "PRIVATE KEY" for :pkcs8.
  #
  # Security caveat: for :pkcs8, the returned PEM String is an
  # ordinary Ruby String on the heap, NOT a SecureBuffer.  The
  # PEM text is not mmap-protected, not mlock'd, and may be
  # swapped to disk or copied by the GC compactor.  Discard the
  # result as soon as possible to limit exposure of secret key
  # material.
  #
  # @return [String] US-ASCII PEM string
  def to_pem
    der = to_der
    label =
      case @format
      when :spki then "PUBLIC KEY"
      when :pkcs8 then "PRIVATE KEY"
      else raise PqcAsn1::Error, "cannot PEM-encode format #{@format.inspect}"
      end
    PqcAsn1::PEM.encode(der, label)
  end

  # Key bytes are never shown for :pkcs8.
  # @return [String]
  def inspect
    alg = @parameters ? " params=#{@parameters.bytesize}B" : ""
    pk = @public_key ? " public_key=#{@public_key.bytesize}B" : ""
    name = algorithm
    case @format
    when :spki
      "#<PqcAsn1::DER::KeyInfo format=:spki oid=#{name}#{alg} key=#{@key.bytesize}B>"
    when :pkcs8
      "#<PqcAsn1::DER::KeyInfo format=:pkcs8 oid=#{name}#{alg} key=REDACTED#{pk}>"
    else
      "#<PqcAsn1::DER::KeyInfo format=#{@format.inspect}>"
    end
  end
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Parameters:

Returns:



334
335
336
337
338
339
340
341
# File 'lib/pqc_asn1.rb', line 334

def ==(other)
  other.is_a?(KeyInfo) &&
    @oid == other.oid &&
    @parameters == other.parameters &&
    @key == other.key &&
    @public_key == other.public_key &&
    @format == other.format
end

#algorithm ⇒ String

Human-readable algorithm name (e.g. "ML_DSA_44"), or the dotted OID string if the algorithm is not a known constant.

Returns:



359
360
361
# File 'lib/pqc_asn1.rb', line 359

def algorithm
  @oid.name || @oid.dotted
end

#deconstruct_keys(keys) ⇒ Hash{Symbol => Object}

Pattern-matching support (Ruby 2.7+). When keys is non-nil, only the requested keys are returned, avoiding unnecessary access to fields like key (a SecureBuffer that requires mprotect toggling).

Parameters:

Returns:



318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/pqc_asn1.rb', line 318

def deconstruct_keys(keys)
  return to_h if keys.nil?

  keys.each_with_object({}) do |k, h|
    case k
    when :oid then h[:oid] = @oid
    when :parameters then h[:parameters] = @parameters
    when :key then h[:key] = @key
    when :public_key then h[:public_key] = @public_key
    when :format then h[:format] = @format
    end
  end
end

#hash ⇒ Integer

For :pkcs8, the secret key is excluded from the hash to avoid leaking key material into hash tables or log output.

Returns:



348
349
350
351
352
353
354
# File 'lib/pqc_asn1.rb', line 348

def hash
  if @format == :pkcs8
    [@oid, @parameters, @public_key, @format].hash
  else
    [@oid, @parameters, @key, @public_key, @format].hash
  end
end

#inspect ⇒ String

Key bytes are never shown for :pkcs8.

Returns:



406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/pqc_asn1.rb', line 406

def inspect
  alg = @parameters ? " params=#{@parameters.bytesize}B" : ""
  pk = @public_key ? " public_key=#{@public_key.bytesize}B" : ""
  name = algorithm
  case @format
  when :spki
    "#<PqcAsn1::DER::KeyInfo format=:spki oid=#{name}#{alg} key=#{@key.bytesize}B>"
  when :pkcs8
    "#<PqcAsn1::DER::KeyInfo format=:pkcs8 oid=#{name}#{alg} key=REDACTED#{pk}>"
  else
    "#<PqcAsn1::DER::KeyInfo format=#{@format.inspect}>"
  end
end

#to_der ⇒ String, PqcAsn1::SecureBuffer

Re-encode this KeyInfo back to DER bytes. For :spki returns a frozen binary String. For :pkcs8 returns a SecureBuffer.

Returns:



367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/pqc_asn1.rb', line 367

def to_der
  opts = {validate: false}
  opts[:parameters] = @parameters if @parameters
  opts[:public_key] = @public_key if @public_key

  case @format
  when :spki
    PqcAsn1::DER.build_spki(@oid, @key, **opts)
  when :pkcs8
    PqcAsn1::DER.build_pkcs8(@oid, @key, **opts)
  else
    raise PqcAsn1::Error, "cannot re-encode format #{@format.inspect}"
  end
end

#to_h ⇒ Hash{Symbol => Object}

Returns:



307
308
309
310
# File 'lib/pqc_asn1.rb', line 307

def to_h
  {oid: @oid, parameters: @parameters, key: @key,
   public_key: @public_key, format: @format}
end

#to_pem ⇒ String

Re-encode to PEM. Uses "PUBLIC KEY" for :spki and "PRIVATE KEY" for :pkcs8.

Security caveat: for :pkcs8, the returned PEM String is an ordinary Ruby String on the heap, NOT a SecureBuffer. The PEM text is not mmap-protected, not mlock'd, and may be swapped to disk or copied by the GC compactor. Discard the result as soon as possible to limit exposure of secret key material.

Returns:

  • US-ASCII PEM string



393
394
395
396
397
398
399
400
401
402
# File 'lib/pqc_asn1.rb', line 393

def to_pem
  der = to_der
  label =
    case @format
    when :spki then "PUBLIC KEY"
    when :pkcs8 then "PRIVATE KEY"
    else raise PqcAsn1::Error, "cannot PEM-encode format #{@format.inspect}"
    end
  PqcAsn1::PEM.encode(der, label)
end