pdf/
crypt.rs

1/// PDF "cryptography" – This is why you don't write your own crypto.
2
3use crate as pdf;
4use aes::cipher::generic_array::{sequence::Split, GenericArray};
5use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
6use aes::cipher::block_padding::{NoPadding, Pkcs7};
7use sha2::{Digest, Sha256, Sha384, Sha512};
8use std::fmt;
9use std::collections::HashMap;
10use datasize::DataSize;
11use crate::object::PlainRef;
12use crate::primitive::{Dictionary, PdfString, Name};
13use crate::error::{PdfError, Result};
14
15type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
16type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
17type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
18
19const PADDING: [u8; 32] = [
20    0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41,
21    0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
22    0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80,
23    0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
24];
25
26#[derive(Copy)]
27pub struct Rc4 {
28    i: u8,
29    j: u8,
30    state: [u8; 256]
31}
32
33impl Clone for Rc4 { fn clone(&self) -> Rc4 { *self } }
34
35impl Rc4 {
36    pub fn new(key: &[u8]) -> Rc4 {
37        assert!(!key.is_empty() && key.len() <= 256);
38        let mut rc4 = Rc4 { i: 0, j: 0, state: [0; 256] };
39        for (i, x) in rc4.state.iter_mut().enumerate() {
40            *x = i as u8;
41        }
42        let mut j: u8 = 0;
43        for i in 0..256 {
44            j = j.wrapping_add(rc4.state[i]).wrapping_add(key[i % key.len()]);
45            rc4.state.swap(i, j as usize);
46        }
47        rc4
48    }
49    fn next(&mut self) -> u8 {
50        self.i = self.i.wrapping_add(1);
51        self.j = self.j.wrapping_add(self.state[self.i as usize]);
52        self.state.swap(self.i as usize, self.j as usize);
53        self.state[(self.state[self.i as usize].wrapping_add(self.state[self.j as usize])) as usize]
54    }
55    pub fn encrypt(key: &[u8], data: &mut [u8]) {
56        let mut rc4 = Rc4::new(key);
57        for b in data.iter_mut() {
58            *b ^= rc4.next();
59        }
60    }
61}
62
63/// 7.6.1 Table 20 + 7.6.3.2 Table 21
64#[derive(Object, Debug, Clone, DataSize)]
65pub struct CryptDict {
66    #[pdf(key="O")]
67    o: PdfString,
68
69    #[pdf(key="U")]
70    u: PdfString,
71
72    #[pdf(key="R")]
73    r: u32,
74
75    #[pdf(key="P")]
76    p: i32,
77
78    #[pdf(key="V")]
79    v: i32,
80
81    #[pdf(key="Length", default="40")]
82    bits: u32,
83
84    #[pdf(key="CF")]
85    crypt_filters: HashMap<Name, CryptFilter>,
86
87    #[pdf(key="StmF")]
88    default_crypt_filter: Option<Name>,
89
90    #[pdf(key="EncryptMetadata", default="true")]
91    encrypt_metadata: bool,
92
93    #[pdf(key = "OE")]
94    oe: Option<PdfString>,
95
96    #[pdf(key = "UE")]
97    ue: Option<PdfString>,
98
99    #[pdf(other)]
100    _other: Dictionary
101}
102
103#[derive(Object, Debug, Clone, Copy, DataSize)]
104pub enum CryptMethod {
105    None,
106    V2,
107    AESV2,
108    AESV3,
109}
110
111#[derive(Object, Debug, Clone, Copy, DataSize)]
112pub enum AuthEvent {
113    DocOpen,
114    EFOpen
115}
116
117#[derive(Object, Debug, Clone, DataSize)]
118#[pdf(Type="CryptFilter?")]
119pub struct CryptFilter {
120    #[pdf(key="CFM", default="CryptMethod::None")]
121    pub method: CryptMethod,
122
123    #[pdf(key="AuthEvent", default="AuthEvent::DocOpen")]
124    pub auth_event: AuthEvent,
125
126    #[pdf(key="Length")]
127    pub length: Option<u32>,
128
129    #[pdf(other)]
130    _other: Dictionary
131}
132
133pub struct Decoder {
134    key_size: usize,
135    key: Vec<u8>, // maximum length
136    method: CryptMethod,
137    /// A reference to the /Encrypt dictionary, if it is in an indirect
138    /// object. The strings in this dictionary are not encrypted, so
139    /// decryption must be skipped when accessing them.
140    pub(crate) encrypt_indirect_object: Option<PlainRef>,
141    /// A reference to the /Metadata dictionary, if it is an indirect
142    /// object. If /EncryptMedata is set to false in the /Encrypt dictionary,
143    /// then the strings in the /Metadata dictionary are not encrypted, so
144    /// decryption must be skipped when accessing them.
145    pub(crate) metadata_indirect_object: Option<PlainRef>,
146    /// Whether the metadata is encrypted, as indicated by /EncryptMetadata
147    /// in the /Encrypt dictionary.
148    encrypt_metadata: bool,
149}
150impl Decoder {
151    pub fn default(dict: &CryptDict, id: &[u8]) -> Result<Decoder> {
152        Decoder::from_password(dict, id, b"")
153    }
154
155    fn key(&self) -> &[u8] {
156        &self.key[.. std::cmp::min(self.key_size, 16)]
157    }
158
159    pub fn new(key: Vec<u8>, key_size: usize, method: CryptMethod, encrypt_metadata: bool) -> Decoder {
160        Decoder {
161            key_size,
162            key,
163            method,
164            encrypt_indirect_object: None,
165            metadata_indirect_object: None,
166            encrypt_metadata,
167        }
168    }
169
170    pub fn from_password(dict: &CryptDict, id: &[u8], pass: &[u8]) -> Result<Decoder> {
171        fn compute_u_rev_2(key: &[u8]) -> Vec<u8> {
172            // algorithm 4
173            let mut data = PADDING.to_vec();
174            Rc4::encrypt(key, &mut data);
175            data
176        }
177
178        fn check_password_rev_2(document_u: &[u8], key: &[u8]) -> bool {
179            compute_u_rev_2(key) == document_u
180        }
181
182        fn compute_u_rev_3_4(id: &[u8], key: &[u8]) -> [u8; 16] {
183            // algorithm 5
184            // a) we derived the key already.
185
186            // b)
187            let mut hash = md5::Context::new();
188            hash.consume(PADDING);
189
190            // c)
191            hash.consume(id);
192
193            // d)
194            let mut data = *hash.compute();
195            Rc4::encrypt(key, &mut data);
196
197            // e)
198            for i in 1u8..=19 {
199                let mut key = key.to_owned();
200                for b in &mut key {
201                    *b ^= i;
202                }
203                Rc4::encrypt(&key, &mut data);
204            }
205
206            // f)
207            data
208        }
209
210        fn check_password_rev_3_4(document_u: &[u8], id: &[u8], key: &[u8]) -> bool {
211            document_u.starts_with(&compute_u_rev_3_4(id, key))
212        }
213
214        fn check_password_rc4(revision: u32, document_u: &[u8], id: &[u8], key: &[u8]) -> bool {
215            if revision == 2 {
216                check_password_rev_2(document_u, key)
217            } else {
218                check_password_rev_3_4(document_u, id, key)
219            }
220        }
221
222        fn key_derivation_user_password_rc4(
223            revision: u32,
224            key_size: usize,
225            dict: &CryptDict,
226            id: &[u8],
227            pass: &[u8],
228        ) -> Vec<u8> {
229            let o = dict.o.as_bytes();
230            let p = dict.p;
231            // 7.6.3.3 - Algorithm 2
232            // a) and b)
233            let mut hash = md5::Context::new();
234            if pass.len() < 32 {
235                hash.consume(pass);
236                hash.consume(&PADDING[..32 - pass.len()]);
237            } else {
238                hash.consume(&pass[..32]);
239            }
240
241            // c)
242            hash.consume(o);
243
244            // d)
245            hash.consume(p.to_le_bytes());
246
247            // e)
248            hash.consume(id);
249
250            // f)
251            if revision >= 4 && !dict.encrypt_metadata {
252                hash.consume([0xff, 0xff, 0xff, 0xff]);
253            }
254
255            // g)
256            let mut data = *hash.compute();
257
258            // h)
259            if revision >= 3 {
260                for _ in 0..50 {
261                    data = *md5::compute(&data[..std::cmp::min(key_size, 16)]);
262                }
263            }
264
265            let mut key = vec![0u8; key_size.max(16)];
266            key[..16].copy_from_slice(&data);
267            key
268        }
269
270        fn key_derivation_owner_password_rc4(
271            revision: u32,
272            key_size: usize,
273            pass: &[u8],
274        ) -> Result<Vec<u8>> {
275            if key_size > 16 {
276                bail!("key size > 16");
277            }
278
279            let mut hash = md5::Context::new();
280            if pass.len() < 32 {
281                hash.consume(pass);
282                hash.consume(&PADDING[..32 - pass.len()]);
283            } else {
284                hash.consume(&pass[..32]);
285            }
286
287            if revision >= 3 {
288                for _ in 0..50 {
289                    let digest = *std::mem::replace(&mut hash, md5::Context::new()).compute();
290                    hash.consume(digest);
291                }
292            }
293
294            let digest = &hash.compute()[..key_size];
295            Ok(digest.to_vec())
296        }
297
298        let (key_bits, method) = match dict.v {
299            1 => (40, CryptMethod::V2),
300            2 => {
301                if dict.bits % 8 != 0 {
302                    err!(other!("invalid key length {}", dict.bits))
303                } else {
304                    (dict.bits, CryptMethod::V2)
305                }
306            },
307            4 ..= 6 => {
308                let default = dict
309                    .crypt_filters
310                    .get(try_opt!(dict.default_crypt_filter.as_ref()).as_str())
311                    .ok_or_else(|| other!("missing crypt filter entry {:?}", dict.default_crypt_filter.as_ref()))?;
312
313                match default.method {
314                    CryptMethod::V2 | CryptMethod::AESV2 => (
315                        default.length.map(|n| 8 * n).unwrap_or(dict.bits),
316                        default.method,
317                    ),
318                    CryptMethod::AESV3 if dict.v == 5 => (
319                        default.length.map(|n| 8 * n).unwrap_or(dict.bits),
320                        default.method,
321                    ),
322                    m => err!(other!("unimplemented crypt method {:?}", m)),
323                }
324            }
325            v => err!(other!("unsupported V value {}", v)),
326        };
327        let level = dict.r;
328        if !(2..=6).contains(&level) {
329            err!(other!("unsupported standard security handler revision {}", level))
330        };
331        if level <= 4 {
332            let key_size = key_bits as usize / 8;
333            let key = key_derivation_user_password_rc4(level, key_size, dict, id, pass);
334
335            if check_password_rc4(level, dict.u.as_bytes(), id, &key[..std::cmp::min(key_size, 16)]) {
336                let decoder = Decoder::new(key, key_size, method, dict.encrypt_metadata);
337                Ok(decoder)
338            } else {
339                let password_wrap_key = key_derivation_owner_password_rc4(level, key_size, pass)?;
340                let mut data = dict.o.as_bytes().to_vec();
341                let rounds = if level == 2 { 1u8 } else { 20u8 };
342                for round in 0..rounds {
343                    let mut round_key = password_wrap_key.clone();
344                    for byte in round_key.iter_mut() {
345                        *byte ^= round;
346                    }
347                    Rc4::encrypt(&round_key, &mut data);
348                }
349                let unwrapped_user_password = data;
350
351                let key = key_derivation_user_password_rc4(
352                    level,
353                    key_size,
354                    dict,
355                    id,
356                    &unwrapped_user_password,
357                );
358
359                if check_password_rc4(level, dict.u.as_bytes(), id, &key[..key_size]) {
360                    let decoder = Decoder::new(key, key_size, method, dict.encrypt_metadata);
361                    Ok(decoder)
362                } else {
363                    Err(PdfError::InvalidPassword)
364                }
365            }
366        } else if level == 5 || level == 6 {
367            let u = dict.u.as_bytes();
368            if u.len() != 48 {
369                err!(format!(
370                    "U in Encrypt dictionary should have a length of 48 bytes, not {}",
371                    u.len(),
372                )
373                .into());
374            }
375            let user_hash = &u[0..32];
376            let user_validation_salt = &u[32..40];
377            let user_key_salt = &u[40..48];
378
379            let o = dict.o.as_bytes();
380            if o.len() != 48 {
381                err!(format!(
382                    "O in Encrypt dictionary should have a length of 48 bytes, not {}",
383                    o.len(),
384                )
385                .into());
386            }
387            let owner_hash = &o[0..32];
388            let owner_validation_salt = &o[32..40];
389            let owner_key_salt = &o[40..48];
390
391            let password_unicode =
392                t!(String::from_utf8(pass.to_vec()).map_err(|_| PdfError::InvalidPassword));
393            let password_prepped =
394                t!(stringprep::saslprep(&password_unicode).map_err(|_| PdfError::InvalidPassword));
395            let mut password_encoded = password_prepped.as_bytes();
396
397            if password_encoded.len() > 127 {
398                password_encoded = &password_encoded[..127];
399            }
400
401            let ue = t!(dict.ue.as_ref().ok_or_else(|| PdfError::MissingEntry {
402                typ: "Encrypt",
403                field: "UE".into(),
404            }))
405            .as_bytes()
406            .to_vec();
407            let oe = t!(dict.oe.as_ref().ok_or_else(|| PdfError::MissingEntry {
408                typ: "Encrypt",
409                field: "OE".into(),
410            }))
411            .as_bytes()
412            .to_vec();
413
414            let (intermediate_key, mut wrapped_key) = if level == 6 {
415                let user_hash_computed =
416                    Self::revision_6_kdf(password_encoded, user_validation_salt, b"");
417                if user_hash_computed == user_hash {
418                    (
419                        Self::revision_6_kdf(password_encoded, user_key_salt, b"").into(),
420                        ue,
421                    )
422                } else {
423                    let owner_hash_computed =
424                        Self::revision_6_kdf(password_encoded, owner_validation_salt, u);
425                    if owner_hash_computed == owner_hash {
426                        (
427                            Self::revision_6_kdf(password_encoded, owner_key_salt, u).into(),
428                            oe,
429                        )
430                    } else {
431                        err!(PdfError::InvalidPassword);
432                    }
433                }
434            } else {
435                // level == 5
436
437                let mut user_check_hash = Sha256::new();
438                user_check_hash.update(password_encoded);
439                user_check_hash.update(user_validation_salt);
440                let user_hash_computed = user_check_hash.finalize();
441                #[allow(clippy::branches_sharing_code)]
442                if user_hash_computed.as_slice() == user_hash {
443                    let mut intermediate_kdf_hash = Sha256::new();
444                    intermediate_kdf_hash.update(password_encoded);
445                    intermediate_kdf_hash.update(user_key_salt);
446                    (intermediate_kdf_hash.finalize(), ue)
447                } else {
448                    let mut owner_check_hash = Sha256::new();
449                    owner_check_hash.update(password_encoded);
450                    owner_check_hash.update(owner_validation_salt);
451                    owner_check_hash.update(u);
452                    let owner_hash_computed = owner_check_hash.finalize();
453                    if owner_hash_computed.as_slice() == owner_hash {
454                        let mut intermediate_kdf_hash = Sha256::new();
455                        intermediate_kdf_hash.update(password_encoded);
456                        intermediate_kdf_hash.update(owner_key_salt);
457                        intermediate_kdf_hash.update(u);
458                        (intermediate_kdf_hash.finalize(), oe)
459                    } else {
460                        err!(PdfError::InvalidPassword);
461                    }
462                }
463            };
464
465            let zero_iv = GenericArray::from_slice(&[0u8; 16]);
466            let key_slice = t!(Aes256CbcDec::new(&intermediate_key, zero_iv)
467                .decrypt_padded_mut::<NoPadding>(&mut wrapped_key)
468                .map_err(|_| PdfError::InvalidPassword));
469
470            let decoder = Decoder::new(key_slice.into(),  32, method, dict.encrypt_metadata);
471            Ok(decoder)
472        } else {
473            err!(format!("unsupported V value {}", level).into())
474        }
475    }
476
477    fn revision_6_kdf(password: &[u8], salt: &[u8], u: &[u8]) -> [u8; 32] {
478        let mut data = [0u8; (128 + 64 + 48) * 64];
479        let mut data_total_len = 0;
480
481        let mut sha256 = Sha256::new();
482        let mut sha384 = Sha384::new();
483        let mut sha512 = Sha512::new();
484
485        let mut input_sha256 = Sha256::new();
486        input_sha256.update(password);
487        input_sha256.update(salt);
488        input_sha256.update(u);
489        let input = input_sha256.finalize();
490        let (mut key, mut iv) = input.split();
491
492        let mut block = [0u8; 64];
493        let mut block_size = 32;
494        (block[..block_size]).copy_from_slice(&input[..block_size]);
495
496        let mut i = 0;
497        while i < 64 || i < data[data_total_len - 1] as usize + 32 {
498            let aes = Aes128CbcEnc::new(&key, &iv);
499            let data_repeat_len = password.len() + block_size + u.len();
500            data[..password.len()].copy_from_slice(password);
501            data[password.len()..password.len() + block_size].copy_from_slice(&block[..block_size]);
502            data[password.len() + block_size..data_repeat_len].copy_from_slice(u);
503            for j in 1..64 {
504                data.copy_within(..data_repeat_len, j * data_repeat_len);
505            }
506            data_total_len = data_repeat_len * 64;
507
508            // The plaintext length will always be a multiple of the block size, unwrap is okay
509            let encrypted = aes
510                .encrypt_padded_mut::<NoPadding>(&mut data[..data_total_len], data_total_len)
511                .unwrap();
512
513            let sum: usize = encrypted[..16].iter().map(|byte| *byte as usize).sum();
514            block_size = sum % 3 * 16 + 32;
515            match block_size {
516                32 => {
517                    sha256.update(encrypted);
518                    (block[..block_size]).copy_from_slice(&sha256.finalize_reset());
519                }
520                48 => {
521                    sha384.update(encrypted);
522                    (block[..block_size]).copy_from_slice(&sha384.finalize_reset());
523                }
524                64 => {
525                    sha512.update(encrypted);
526                    (block[..block_size]).copy_from_slice(&sha512.finalize_reset());
527                }
528                _ => unreachable!(),
529            }
530
531            key.copy_from_slice(&block[..16]);
532            iv.copy_from_slice(&block[16..32]);
533
534            i += 1;
535        }
536        let mut hash = [0u8; 32];
537        hash.copy_from_slice(&block[..32]);
538        hash
539    }
540
541    pub fn decrypt<'buf>(&self, id: PlainRef, data: &'buf mut [u8]) -> Result<&'buf [u8]> {
542        if self.encrypt_indirect_object == Some(id) {
543            // Strings inside the /Encrypt dictionary are not encrypted
544            return Ok(data);
545        }
546
547        if !self.encrypt_metadata && self.metadata_indirect_object == Some(id) {
548            // Strings inside the /Metadata dictionary are not encrypted when /EncryptMetadata is
549            // false
550            return Ok(data);
551        }
552
553        if data.is_empty() {
554            return Ok(data);
555        }
556
557        // Algorithm 1
558        // a) we have those already
559
560        match self.method {
561            CryptMethod::None => unreachable!(),
562            CryptMethod::V2 => {
563                // b)
564                let mut key = [0; 16 + 5];
565                let n = self.key().len();
566                key[..n].copy_from_slice(self.key());
567                key[n..n + 3].copy_from_slice(&id.id.to_le_bytes()[..3]);
568                key[n + 3..n + 5].copy_from_slice(&id.gen.to_le_bytes()[..2]);
569
570                // c)
571                let key = *md5::compute(&key[..n + 5]);
572
573                // d)
574                Rc4::encrypt(&key[..(n + 5).min(16)], data);
575                Ok(data)
576            }
577            CryptMethod::AESV2 => {
578                // b)
579                let mut key = [0; 32 + 5 + 4];
580                let n = std::cmp::min(self.key_size, 16);
581                key[..n].copy_from_slice(self.key());
582                key[n..n + 3].copy_from_slice(&id.id.to_le_bytes()[..3]);
583                key[n + 3..n + 5].copy_from_slice(&id.gen.to_le_bytes()[..2]);
584                key[n + 5..n + 9].copy_from_slice(b"sAlT");
585
586                // c)
587                let key = *md5::compute(&key[..n + 9]);
588
589                // d)
590                let key = &key[..(n + 5).min(16)];
591                if data.len() < 16 {
592                    return Err(PdfError::DecryptionFailure);
593                }
594                let (iv, ciphertext) = data.split_at_mut(16);
595                let cipher =
596                    t!(Aes128CbcDec::new_from_slices(key, iv).map_err(|_| PdfError::DecryptionFailure));
597                Ok(t!(cipher
598                    .decrypt_padded_mut::<Pkcs7>(ciphertext)
599                    .map_err(|_| PdfError::DecryptionFailure)))
600            }
601            CryptMethod::AESV3 => {
602                if data.len() < 16 {
603                    return Err(PdfError::DecryptionFailure);
604                }
605                let (iv, ciphertext) = data.split_at_mut(16);
606                let cipher =
607                    t!(Aes256CbcDec::new_from_slices(self.key(), iv).map_err(|_| PdfError::DecryptionFailure));
608                Ok(t!(cipher
609                    .decrypt_padded_mut::<Pkcs7>(ciphertext)
610                    .map_err(|_| PdfError::DecryptionFailure)))
611            }
612        }
613    }
614}
615impl fmt::Debug for Decoder {
616    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
617        f.debug_struct("Decoder")
618            .field("key", &self.key())
619            .field("method", &self.method)
620            .finish()
621    }
622}
623
624#[cfg(test)]
625mod tests {
626    #[test]
627    fn unencrypted_strings() {
628        let data_prefix = b"%PDF-1.5\n\
629            1 0 obj\n\
630            << /Type /Catalog /Pages 2 0 R >>\n\
631            endobj\n\
632            2 0 obj\n\
633            << /Type /Pages /Kids [3 0 R] /Count 1 >>\n\
634            endobj\n\
635            3 0 obj\n\
636            << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R >>\n\
637            endobj\n\
638            4 0 obj\n\
639            << /Length 0 >>\n\
640            stream\n\
641            endstream\n\
642            endobj\n\
643            5 0 obj\n\
644            <<\n\
645                /V 4\n\
646                /CF <<\n\
647                    /StdCF << /Type /CryptFilter /CFM /V2 >>\n\
648                >>\n\
649                /StmF /StdCF\n\
650                /StrF /StdCF\n\
651                /R 4\n\
652                /O (owner pwd hash!!)\n\
653                /U <E721D9D63EC4E7BD4DA6C9F0E30C8290>\n\
654                /P -4\n\
655            >>\n\
656            endobj\n\
657            xref\n\
658            1 5\n";
659        let mut data = data_prefix.to_vec();
660        for obj_nr in 1..=5 {
661            let needle = format!("\n{} 0 obj\n", obj_nr).into_bytes();
662            let offset = data_prefix
663                .windows(needle.len())
664                .position(|w| w == needle)
665                .unwrap()
666                + 1;
667            let mut line = format!("{:010} {:05} n\r\n", offset, 0).into_bytes();
668            assert_eq!(line.len(), 20);
669            data.append(&mut line);
670        }
671        let trailer_snippet = b"trailer\n\
672            <<\n\
673                /Size 6\n\
674                /Root 1 0 R\n\
675                /Encrypt 5 0 R\n\
676                /ID [<DEADBEEF> <DEADBEEF>]\n\
677            >>\n\
678            startxref\n";
679        data.extend_from_slice(trailer_snippet);
680        let xref_offset = data_prefix
681            .windows("xref".len())
682            .rposition(|w| w == b"xref")
683            .unwrap();
684        data.append(&mut format!("{}\n%%EOF", xref_offset).into_bytes());
685
686        let file = crate::file::FileOptions::uncached().load(data).unwrap();
687
688        // PDF reference says strings in the encryption dictionary are "not
689        // encrypted by the usual methods."
690        assert_eq!(
691            file.trailer.encrypt_dict.unwrap().o.as_ref(),
692            b"owner pwd hash!!",
693        );
694    }
695}