Class: PqcRails::Cipher

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

Overview

ActiveRecord::Encryption::Cipher互換のインターフェースを持つ独立したクラス。 ActiveRecord::Encryption.configure(cipher: PqcRails::Cipher.new, key_provider: ...)で デフォルトのAES-256-GCM単体実装を完全に置き換える。

ActiveRecord::Encryptionのkey:は「鍵そのもの」をそのまま渡してくる設計 (KeyProvider#encryption_key.secretの値がそのまま渡る)。本実装ではKeyProvider側で key.secretがPqcRails::HybridKem::Keypair(公開鍵・秘密鍵のペア)を返すように 揃えてあるので、ここではそのKeypairを直接受け取れる。

内部的にはPhase 2と同じKEM-DEM構成(HybridKem+EnvelopeCipher)。 Session::Encryptorを再利用しないのは、ARはCipher層に外からkey:を渡す設計のため 鍵の持ち方が異なるから(Session::Encryptorは自分でKeypairを保持する)。

Constant Summary collapse

DEFAULT_PQ_ALG_NAME =
PqcRails::Session::Encryptor::DEFAULT_PQ_ALG_NAME

Instance Method Summary collapse

Constructor Details

#initialize(pq_alg_name: DEFAULT_PQ_ALG_NAME) ⇒ Cipher



24
25
26
# File 'lib/pqc_rails/cipher.rb', line 24

def initialize(pq_alg_name: DEFAULT_PQ_ALG_NAME)
  @pq_alg_name = pq_alg_name
end

Instance Method Details

#decrypt(encrypted_message, key:) ⇒ Object

Raises:

  • (::ActiveRecord::Encryption::Errors::Decryption)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/pqc_rails/cipher.rb', line 41

def decrypt(encrypted_message, key:)
  keypairs = key.is_a?(::Array) ? key : [key]
  last_error = nil

  HybridKem.open(@pq_alg_name) do |hybrid|
    keypairs.each do |keypair|
      return decrypt_with(hybrid, encrypted_message, keypair)
    rescue OpenSSL::Cipher::CipherError, PqcRails::Error, ArgumentError, TypeError => e
      last_error = e
    end
  end

  raise ::ActiveRecord::Encryption::Errors::Decryption, last_error&.message
end

#encrypt(clear_text, key:, deterministic: false) ⇒ Object

Raises:

  • (ArgumentError)


28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/pqc_rails/cipher.rb', line 28

def encrypt(clear_text, key:, deterministic: false)
  raise ArgumentError, "PqcRails::Cipher does not support deterministic encryption" if deterministic

  HybridKem.open(@pq_alg_name) do |hybrid|
    encap = hybrid.encapsulate(key.public_key)
    envelope = EnvelopeCipher.new(encap.shared_secret).encrypt(clear_text)

    ::ActiveRecord::Encryption::Message.new(payload: envelope).tap do |message|
      message.headers[:kem_ct] = encap.ciphertext
    end
  end
end

#iv_lengthObject



60
61
62
# File 'lib/pqc_rails/cipher.rb', line 60

def iv_length
  EnvelopeCipher::IV_LENGTH
end

#key_lengthObject



56
57
58
# File 'lib/pqc_rails/cipher.rb', line 56

def key_length
  EnvelopeCipher::KEY_LENGTH
end