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