Class: ActiveCipherStorage::StreamCipher

Inherits:
Object
  • Object
show all
Includes:
KeyUtils
Defined in:
lib/active_cipher_storage/stream_cipher.rb

Instance Method Summary collapse

Constructor Details

#initialize(config = ActiveCipherStorage.configuration) ⇒ StreamCipher

Returns a new instance of StreamCipher.



8
9
10
11
12
13
# File 'lib/active_cipher_storage/stream_cipher.rb', line 8

def initialize(config = ActiveCipherStorage.configuration)
  config.validate!
  @config     = config
  @provider   = config.provider
  @chunk_size = config.chunk_size
end

Instance Method Details

#decrypt(input_io, output_io) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/active_cipher_storage/stream_cipher.rb', line 45

def decrypt(input_io, output_io)
  header = Format.read_header(input_io)
  raise Errors::InvalidFormat, "Payload is not chunked; use Cipher#decrypt" unless header.chunked

  key = @provider.decrypt_data_key(header.encrypted_dek)
  expected_seq = 1
  loop do
    frame = Format.read_chunk(input_io)
    raise Errors::InvalidFormat, "Unexpected end of stream — missing final frame" if frame.nil?

    validate_frame_sequence!(frame[:seq], expected_seq)
    output_io.write(decrypt_chunk(frame[:ciphertext], key, frame[:iv], frame[:auth_tag], frame[:seq]))
    break if frame[:seq] == Format::FINAL_SEQ

    expected_seq += 1
  end

  ensure_no_trailing_bytes!(input_io)
ensure
  zero_bytes!(key)
end

#decrypt_to_io(io) ⇒ Object



74
75
76
77
78
79
# File 'lib/active_cipher_storage/stream_cipher.rb', line 74

def decrypt_to_io(io)
  out = StringIO.new("".b)
  decrypt(io, out)
  out.rewind
  out
end

#encrypt(input_io, output_io) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/active_cipher_storage/stream_cipher.rb', line 15

def encrypt(input_io, output_io)
  dek_bundle = @provider.generate_data_key
  key        = dek_bundle.fetch(:plaintext_key)

  Format.write_header(output_io, Format::Header.new(
    version:       Format::VERSION,
    algorithm:     Format::ALGO_AES256GCM,
    chunked:       true,
    chunk_size:    @chunk_size,
    provider_id:   @provider.provider_id,
    encrypted_dek: dek_bundle.fetch(:encrypted_key)
  ))

  seq  = 0
  done = false
  until done
    plaintext = input_io.read(@chunk_size) || "".b
    done      = plaintext.bytesize < @chunk_size
    seq      += 1
    frame_seq = done ? Format::FINAL_SEQ : seq
    iv        = SecureRandom.random_bytes(Format::IV_SIZE)
    ct, tag   = encrypt_chunk(plaintext, key, iv, frame_seq)
    Format.write_chunk(output_io, seq: frame_seq, iv: iv, ciphertext: ct, auth_tag: tag)
  end

  seq
ensure
  zero_bytes!(key)
end

#encrypt_to_io(io) ⇒ Object



67
68
69
70
71
72
# File 'lib/active_cipher_storage/stream_cipher.rb', line 67

def encrypt_to_io(io)
  out = StringIO.new("".b)
  encrypt(io, out)
  out.rewind
  out
end