Class: Ksef::Crypto::Encryptor
- Inherits:
-
Object
- Object
- Ksef::Crypto::Encryptor
- Defined in:
- lib/ksef/crypto/encryptor.rb
Overview
A session's symmetric key, and the AES-256-CBC encryption done under it (docs/REFERENCE.md §10.1).
The IV is not prefixed to the ciphertext
This is the trap worth knowing about before reading anything else here.
sesja-interaktywna.md describes the IV as "dołączanego jako prefiks do szyfrogramu"
— prefixed to the ciphertext. It is not, and following that prose produces a
payload KSeF cannot decrypt. The IV travels once, as a discrete field of the
session-open request, and every per-invoice ciphertext is bare. Three higher-precedence
sources agree against the prose, the OpenAPI contract among them (§14.1) — and the
contract's own worked example settles it arithmetically: a 6480-byte invoice becomes
6496 bytes encrypted. 6480 is a whole number of blocks, so PKCS#7 adds exactly one
block of padding; a prefixed IV would have made it 6512.
So #encrypt returns bare ciphertext, and the IV is exposed separately for the session-open request via #encryption_info.
Defined Under Namespace
Classes: Sealed
Constant Summary collapse
- REDACTED =
"[REDACTED]"
Instance Attribute Summary collapse
-
#iv ⇒ Object
readonly
The IV is not secret — it is sent to the API in the clear — so unlike the key it has a reader.
Class Method Summary collapse
-
.generate ⇒ Object
A fresh key and IV from the CSPRNG.
Instance Method Summary collapse
-
#decrypt(ciphertext) ⇒ Object
The inverse.
-
#encrypt(plaintext) ⇒ String
Bare ciphertext, binary — see the class note on the IV.
-
#encryption_info(certificate) ⇒ Hash
The contract's
EncryptionInfo, as sent onPOST /sessions/online,POST /sessions/batchandPOST /invoices/exports. -
#initialize(key:, iv:) ⇒ Encryptor
constructor
A new instance of Encryptor.
- #inspect ⇒ Object
-
#seal(plaintext) ⇒ Sealed
Encrypt once, and measure both artifacts while they are in hand.
-
#to_s ⇒ Object
Redacted, and
#to_sdeliberately too: DESIGN.md §4.5 forbids leaking symmetric keys and IVs at default log level, and"key: #{encryptor}"in someone's debug line is exactly how that happens.
Constructor Details
Instance Attribute Details
#iv ⇒ Object (readonly)
The IV is not secret — it is sent to the API in the clear — so unlike the key it has a reader. It is still kept out of #inspect, which DESIGN.md §4.5 requires.
36 37 38 |
# File 'lib/ksef/crypto/encryptor.rb', line 36 def iv @iv end |
Class Method Details
.generate ⇒ Object
A fresh key and IV from the CSPRNG. §10.1 records a per-session key as recommended by the docs rather than required; recommended is reason enough.
40 41 42 43 |
# File 'lib/ksef/crypto/encryptor.rb', line 40 def self.generate cipher = OpenSSL::Cipher.new(CIPHER) new(key: cipher.random_key, iv: cipher.random_iv) end |
Instance Method Details
#decrypt(ciphertext) ⇒ Object
The inverse. KSeF never asks us to decrypt anything, so this exists for tests and for a caller wanting to prove to itself that a payload round-trips before sending it — which beats discovering a key mismatch from a rejected invoice.
63 |
# File 'lib/ksef/crypto/encryptor.rb', line 63 def decrypt(ciphertext) = transform(:decrypt, ciphertext) |
#encrypt(plaintext) ⇒ String
Returns bare ciphertext, binary — see the class note on the IV.
58 |
# File 'lib/ksef/crypto/encryptor.rb', line 58 def encrypt(plaintext) = transform(:encrypt, plaintext) |
#encryption_info(certificate) ⇒ Hash
The contract's EncryptionInfo, as sent on POST /sessions/online,
POST /sessions/batch and POST /invoices/exports.
publicKeyId is nullable in the contract but always sent here: it names which
published key did the wrapping, and without it a key rotation turns a decryptable
payload into an undecryptable one with nothing to diagnose it by (§10.2).
87 88 89 90 91 92 93 |
# File 'lib/ksef/crypto/encryptor.rb', line 87 def encryption_info(certificate) { encryptedSymmetricKey: Crypto.encode(certificate.encrypt(@key)), initializationVector: Crypto.encode(@iv), publicKeyId: certificate.public_key_id } end |
#inspect ⇒ Object
99 |
# File 'lib/ksef/crypto/encryptor.rb', line 99 def inspect = "#<Ksef::Crypto::Encryptor key=#{REDACTED} iv=#{REDACTED}>" |
#seal(plaintext) ⇒ Sealed
Encrypt once, and measure both artifacts while they are in hand.
69 70 71 72 73 74 75 76 |
# File 'lib/ksef/crypto/encryptor.rb', line 69 def seal(plaintext) ciphertext = encrypt(plaintext) Sealed.new( ciphertext: ciphertext, plaintext_digest: Digest.of(plaintext), ciphertext_digest: Digest.of(ciphertext) ) end |
#to_s ⇒ Object
Redacted, and #to_s deliberately too: DESIGN.md §4.5 forbids leaking symmetric
keys and IVs at default log level, and "key: #{encryptor}" in someone's debug
line is exactly how that happens.
98 |
# File 'lib/ksef/crypto/encryptor.rb', line 98 def to_s = REDACTED |