1 use std
::os
::unix
::prelude
::AsRawFd
;
4 use anyhow
::{bail, format_err, Error}
;
5 use endian_trait
::Endian
;
7 use proxmox
::tools
::io
::{ReadExt, WriteExt}
;
9 use crate::tools
::sgutils2
::{
11 alloc_page_aligned_buffer
,
14 /// Test if drive supports hardware encryption
16 /// We search for AES_CGM algorithm with 256bits key.
17 pub fn has_encryption
<F
: AsRawFd
>(
21 let data
= match sg_spin_data_encryption_caps(file
) {
23 Err(_
) => return false,
25 decode_spin_data_encryption_caps(&data
).is_ok()
28 /// Set or clear encryption key
30 /// We always use mixed mode,
31 pub fn set_encryption
<F
: AsRawFd
>(
33 key
: Option
<[u8; 32]>,
34 ) -> Result
<(), Error
> {
36 let data
= match sg_spin_data_encryption_caps(file
) {
38 Err(err
) if key
.is_none() => {
39 /// Assume device does not support HW encryption
40 /// We can simply ignore the clear key request
43 Err(err
) => return Err(err
),
46 let algorithm_index
= decode_spin_data_encryption_caps(&data
)?
;
48 sg_spout_set_encryption(file
, algorithm_index
, key
)?
;
50 let data
= sg_spin_data_encryption_status(file
)?
;
51 let status
= decode_spin_data_encryption_status(&data
)?
;
54 DataEncryptionMode
::Off
=> {
59 DataEncryptionMode
::Mixed
=> {
67 bail
!("got unexpected encryption mode {:?}", status
.mode
);
72 struct SspSetDataEncryptionPage
{
86 fn sg_spout_set_encryption
<F
: AsRawFd
>(
89 key
: Option
<[u8; 32]>,
90 ) -> Result
<(), Error
> {
92 let mut sg_raw
= SgRaw
::new(file
, 0)?
;
94 let mut outbuf_len
= std
::mem
::size_of
::<SspSetDataEncryptionPage
>();
95 if let Some(ref key
) = key
{
96 outbuf_len
+= key
.len();
99 let mut outbuf
= alloc_page_aligned_buffer(outbuf_len
)?
;
102 let page
= SspSetDataEncryptionPage
{
104 page_len
: (outbuf_len
- 4) as u16,
105 scope_byte
: (0b10 << 5), // all IT nexus
106 control_byte_5
: (chok
<< 2),
107 encryption_mode
: if key
.is_some() { 2 }
else { 0 }
,
108 decryption_mode
: if key
.is_some() { 3 }
else { 0 }
, // mixed mode
112 key_len
: if let Some(ref key
) = key { key.len() as u16 }
else { 0 }
,
115 let mut writer
= &mut outbuf
[..];
116 unsafe { writer.write_be_value(page)? }
;
118 if let Some(ref key
) = key
{
119 writer
.write_all(key
)?
;
122 let mut cmd
= Vec
::new();
123 cmd
.push(0xB5); // SECURITY PROTOCOL IN (SPOUT)
124 cmd
.push(0x20); // Tape Data Encryption Page
125 cmd
.push(0);cmd
.push(0x10); // Set Data Encryption page
128 cmd
.extend(&(outbuf_len
as u32).to_be_bytes()); // data out len
132 sg_raw
.do_out_command(&cmd
, &outbuf
)
133 .map_err(|err
| format_err
!("set data encryption SPOUT(20h[0010h]) failed - {}", err
))
136 // Warning: this blocks and fails if there is no media loaded
137 fn sg_spin_data_encryption_status
<F
: AsRawFd
>(file
: &mut F
) -> Result
<Vec
<u8>, Error
> {
139 let allocation_len
: u32 = 8192+4;
141 let mut sg_raw
= SgRaw
::new(file
, allocation_len
as usize)?
;
143 let mut cmd
= Vec
::new();
144 cmd
.push(0xA2); // SECURITY PROTOCOL IN (SPIN)
145 cmd
.push(0x20); // Tape Data Encryption Page
146 cmd
.push(0);cmd
.push(0x20); // Data Encryption Status page
149 cmd
.extend(&allocation_len
.to_be_bytes());
153 sg_raw
.do_command(&cmd
)
154 .map_err(|err
| format_err
!("read data encryption status SPIN(20h[0020h]) failed - {}", err
))
158 // Warning: this blocks and fails if there is no media loaded
159 fn sg_spin_data_encryption_caps
<F
: AsRawFd
>(file
: &mut F
) -> Result
<Vec
<u8>, Error
> {
161 let allocation_len
: u32 = 8192+4;
163 let mut sg_raw
= SgRaw
::new(file
, allocation_len
as usize)?
;
165 let mut cmd
= Vec
::new();
166 cmd
.push(0xA2); // SECURITY PROTOCOL IN (SPIN)
167 cmd
.push(0x20); // Tape Data Encryption Page
168 cmd
.push(0);cmd
.push(0x10); // Data Encryption Capabilities page
171 cmd
.extend(&allocation_len
.to_be_bytes());
175 sg_raw
.do_command(&cmd
)
176 .map_err(|err
| format_err
!("read data encryption caps SPIN(20h[0010h]) failed - {}", err
))
181 enum DataEncryptionMode
{
189 struct DataEncryptionStatus
{
190 mode
: DataEncryptionMode
,
195 struct SspDataEncryptionCapabilityPage
{
198 extdecc_cfgp_byte
: u8,
204 struct SspDataEncryptionAlgorithmDescriptor
{
220 // Returns the algorythm_index for AES-CGM
221 fn decode_spin_data_encryption_caps(data
: &[u8]) -> Result
<u8, Error
> {
223 proxmox
::try_block
!({
224 let mut reader
= &data
[..];
225 let page
: SspDataEncryptionCapabilityPage
= unsafe { reader.read_be_value()? }
;
227 let extdecc
= (page
.extdecc_cfgp_byte
& 0b00001100) >> 2;
229 bail
!("not external data encryption control capable");
232 let cfg_p
= page
.extdecc_cfgp_byte
& 0b00000011;
234 bail
!("not allow to change logical block encryption parameters");
237 let mut aes_cgm_index
= None
;
240 if reader
.is_empty() { break; }
;
241 let desc
: SspDataEncryptionAlgorithmDescriptor
=
242 unsafe { reader.read_be_value()? }
;
243 if desc
.descriptor_len
!= 0x14 {
244 bail
!("got wrong key descriptior len");
246 if (desc
.control_byte_4
& 0b00000011) != 2 {
247 continue; // cant encrypt in hardware
249 if ((desc
.control_byte_4
& 0b00001100) >> 2) != 2 {
250 continue; // cant decrypt in hardware
252 if desc
.algorithm_code
== 0x00010014 && desc
.key_size
== 32 {
253 aes_cgm_index
= Some(desc
.algorythm_index
);
258 match aes_cgm_index
{
259 Some(index
) => Ok(index
),
260 None
=> bail
!("drive dies not support AES-CGM encryption"),
262 }).map_err(|err
: Error
| format_err
!("decode data encryption caps page failed - {}", err
))
268 struct SspDataEncryptionStatusPage
{
275 key_instance_counter
: u32,
282 fn decode_spin_data_encryption_status(data
: &[u8]) -> Result
<DataEncryptionStatus
, Error
> {
284 proxmox
::try_block
!({
285 let mut reader
= &data
[..];
286 let page
: SspDataEncryptionStatusPage
= unsafe { reader.read_be_value()? }
;
288 if page
.page_code
!= 0x20 {
289 bail
!("invalid response");
292 let mode
= match (page
.encryption_mode
, page
.decryption_mode
) {
293 (0, 0) => DataEncryptionMode
::Off
,
294 (2, 1) => DataEncryptionMode
::RawRead
,
295 (2, 2) => DataEncryptionMode
::On
,
296 (2, 3) => DataEncryptionMode
::Mixed
,
297 _
=> bail
!("unknown encryption mode"),
300 let status
= DataEncryptionStatus
{
306 }).map_err(|err
| format_err
!("decode data encryption status page failed - {}", err
))