]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "librbd/crypto/BlockCrypto.h" | |
5 | #include "include/byteorder.h" | |
6 | #include "include/ceph_assert.h" | |
20effc67 | 7 | #include "include/scope_guard.h" |
f67539c2 TL |
8 | |
9 | #include <stdlib.h> | |
10 | ||
11 | namespace librbd { | |
12 | namespace crypto { | |
13 | ||
14 | template <typename T> | |
15 | BlockCrypto<T>::BlockCrypto(CephContext* cct, DataCryptor<T>* data_cryptor, | |
16 | uint64_t block_size, uint64_t data_offset) | |
17 | : m_cct(cct), m_data_cryptor(data_cryptor), m_block_size(block_size), | |
18 | m_data_offset(data_offset), m_iv_size(data_cryptor->get_iv_size()) { | |
19 | ceph_assert(isp2(block_size)); | |
20 | ceph_assert((block_size % data_cryptor->get_block_size()) == 0); | |
21 | ceph_assert((block_size % 512) == 0); | |
22 | } | |
23 | ||
24 | template <typename T> | |
25 | BlockCrypto<T>::~BlockCrypto() { | |
26 | if (m_data_cryptor != nullptr) { | |
27 | delete m_data_cryptor; | |
28 | m_data_cryptor = nullptr; | |
29 | } | |
30 | } | |
31 | ||
32 | template <typename T> | |
33 | int BlockCrypto<T>::crypt(ceph::bufferlist* data, uint64_t image_offset, | |
34 | CipherMode mode) { | |
35 | if (image_offset % m_block_size != 0) { | |
36 | lderr(m_cct) << "image offset: " << image_offset | |
37 | << " not aligned to block size: " << m_block_size << dendl; | |
38 | return -EINVAL; | |
39 | } | |
40 | if (data->length() % m_block_size != 0) { | |
41 | lderr(m_cct) << "data length: " << data->length() | |
42 | << " not aligned to block size: " << m_block_size << dendl; | |
43 | return -EINVAL; | |
44 | } | |
45 | ||
46 | unsigned char* iv = (unsigned char*)alloca(m_iv_size); | |
47 | memset(iv, 0, m_iv_size); | |
48 | ||
49 | bufferlist src = *data; | |
50 | data->clear(); | |
51 | ||
52 | auto ctx = m_data_cryptor->get_context(mode); | |
53 | if (ctx == nullptr) { | |
54 | lderr(m_cct) << "unable to get crypt context" << dendl; | |
55 | return -EIO; | |
56 | } | |
20effc67 TL |
57 | |
58 | auto sg = make_scope_guard([&] { | |
59 | m_data_cryptor->return_context(ctx, mode); }); | |
60 | ||
f67539c2 TL |
61 | auto sector_number = image_offset / 512; |
62 | auto appender = data->get_contiguous_appender(src.length()); | |
63 | unsigned char* out_buf_ptr = nullptr; | |
64 | unsigned char* leftover_block = (unsigned char*)alloca(m_block_size); | |
65 | uint32_t leftover_size = 0; | |
66 | for (auto buf = src.buffers().begin(); buf != src.buffers().end(); ++buf) { | |
67 | auto in_buf_ptr = reinterpret_cast<const unsigned char*>(buf->c_str()); | |
68 | auto remaining_buf_bytes = buf->length(); | |
69 | while (remaining_buf_bytes > 0) { | |
70 | if (leftover_size == 0) { | |
20effc67 | 71 | auto block_offset_le = ceph_le64(sector_number); |
f67539c2 TL |
72 | memcpy(iv, &block_offset_le, sizeof(block_offset_le)); |
73 | auto r = m_data_cryptor->init_context(ctx, iv, m_iv_size); | |
74 | if (r != 0) { | |
75 | lderr(m_cct) << "unable to init cipher's IV" << dendl; | |
76 | return r; | |
77 | } | |
78 | ||
79 | out_buf_ptr = reinterpret_cast<unsigned char*>( | |
80 | appender.get_pos_add(m_block_size)); | |
81 | sector_number += m_block_size / 512; | |
82 | } | |
83 | ||
84 | if (leftover_size > 0 || remaining_buf_bytes < m_block_size) { | |
85 | auto copy_size = std::min( | |
86 | (uint32_t)m_block_size - leftover_size, remaining_buf_bytes); | |
87 | memcpy(leftover_block + leftover_size, in_buf_ptr, copy_size); | |
88 | in_buf_ptr += copy_size; | |
89 | leftover_size += copy_size; | |
90 | remaining_buf_bytes -= copy_size; | |
91 | } | |
92 | ||
93 | int crypto_output_length = 0; | |
94 | if (leftover_size == 0) { | |
95 | crypto_output_length = m_data_cryptor->update_context( | |
96 | ctx, in_buf_ptr, out_buf_ptr, m_block_size); | |
97 | ||
98 | in_buf_ptr += m_block_size; | |
99 | remaining_buf_bytes -= m_block_size; | |
100 | } else if (leftover_size == m_block_size) { | |
101 | crypto_output_length = m_data_cryptor->update_context( | |
102 | ctx, leftover_block, out_buf_ptr, m_block_size); | |
103 | leftover_size = 0; | |
104 | } | |
105 | ||
106 | if (crypto_output_length < 0) { | |
107 | lderr(m_cct) << "crypt update failed" << dendl; | |
108 | return crypto_output_length; | |
109 | } | |
110 | ||
111 | out_buf_ptr += crypto_output_length; | |
112 | } | |
113 | } | |
114 | ||
f67539c2 TL |
115 | return 0; |
116 | } | |
117 | ||
118 | template <typename T> | |
119 | int BlockCrypto<T>::encrypt(ceph::bufferlist* data, uint64_t image_offset) { | |
120 | return crypt(data, image_offset, CipherMode::CIPHER_MODE_ENC); | |
121 | } | |
122 | ||
123 | template <typename T> | |
124 | int BlockCrypto<T>::decrypt(ceph::bufferlist* data, uint64_t image_offset) { | |
125 | return crypt(data, image_offset, CipherMode::CIPHER_MODE_DEC); | |
126 | } | |
127 | ||
128 | } // namespace crypto | |
129 | } // namespace librbd | |
130 | ||
131 | template class librbd::crypto::BlockCrypto<EVP_CIPHER_CTX>; |