authenticode_parser/
lib.rs

1//! Bindings for the
2//! [authenticode parser library](https://github.com/avast/authenticode-parser) from Avast.
3
4#![warn(unsafe_op_in_unsafe_fn)]
5#![deny(clippy::all)]
6#![deny(clippy::pedantic)]
7#![deny(missing_docs)]
8#![deny(clippy::cargo)]
9#![deny(clippy::arithmetic_side_effects)]
10#![deny(clippy::as_conversions)]
11#![deny(clippy::as_underscore)]
12#![deny(clippy::undocumented_unsafe_blocks)]
13
14use authenticode_parser_sys as sys;
15
16/// Token indicating the library has been initialized.
17#[derive(Copy, Clone, Debug)]
18pub struct InitializationToken(());
19
20impl InitializationToken {
21    /// Initialize the library.
22    ///
23    /// Initializes all globals `OpenSSL` objects we need for parsing.
24    ///
25    /// # Safety
26    ///
27    /// This is not thread-safe and can cause crashes if called at the same time as other functions
28    /// from the OpenSSL library. Therefore, you need to ensure that this function is called when no
29    /// other threads might call OpenSSL functions, for example before setting up any multithreading
30    /// environment.
31    ///
32    /// See <https://github.com/openssl/openssl/issues/13524>.
33    #[must_use]
34    pub unsafe fn new() -> InitializationToken {
35        // Safety: enforced by safety comment on this function.
36        unsafe {
37            sys::initialize_authenticode_parser();
38        }
39        InitializationToken(())
40    }
41}
42
43/// Constructs `AuthenticodeArray` from binary data containing Authenticode signature.
44///
45/// Authenticode can contains nested Authenticode signatures as its unsigned attribute, which
46/// can also contain nested signatures. For this reason the function return an Array of parsed
47/// Authenticode signatures.
48///
49/// Any field of the parsed out structures can be NULL, depending on the input data.
50///
51/// WARNING: in case of this interface, the file and signature digest comparison is up to the
52/// library user, as there is no pe data to calculate file digest from.
53///
54/// Verification result is stored in `verify_flags` with the first verification error.
55#[must_use]
56pub fn parse(_token: &InitializationToken, data: &[u8]) -> Option<AuthenticodeArray> {
57    let len = i32::try_from(data.len()).unwrap_or(i32::MAX);
58    // Safety:
59    // - the data buffer is valid and the length is at worsed clamped
60    // - the library has been initialized as we have a `InitializationToken`.
61    let res = unsafe { sys::authenticode_new(data.as_ptr(), len) };
62    if res.is_null() {
63        None
64    } else {
65        Some(AuthenticodeArray(res))
66    }
67}
68
69/// Constructs `AuthenticodeArray` from PE file data.
70///
71/// Authenticode can contains nested Authenticode signatures as its unsigned attribute, which can
72/// also contain nested signatures. For this reason the function returns an Array of parsed
73/// Authenticode signatures.
74///
75/// Any field of the parsed out structures can be NULL, depending on the input data.
76///
77/// Verification result is stored in `verify_flags` with the first verification error.
78#[must_use]
79pub fn parse_pe(_token: &InitializationToken, data: &[u8]) -> Option<AuthenticodeArray> {
80    let len = u64::try_from(data.len()).unwrap_or(u64::MAX);
81    // Safety:
82    // - the data buffer is valid and the length is at worsed clamped
83    // - the library has been initialized as we have a `InitializationToken`.
84    let res = unsafe { sys::parse_authenticode(data.as_ptr(), len) };
85    if res.is_null() {
86        None
87    } else {
88        Some(AuthenticodeArray(res))
89    }
90}
91
92/// Array of authenticode signatures.
93//
94// Invariant: the pointer must not be null.
95#[repr(transparent)]
96#[derive(Debug)]
97pub struct AuthenticodeArray(*mut sys::AuthenticodeArray);
98
99impl Drop for AuthenticodeArray {
100    fn drop(&mut self) {
101        // Safety: pointer is not null and has been created by the authenticode-parser library.
102        unsafe {
103            sys::authenticode_array_free(self.0);
104        }
105    }
106}
107
108impl AuthenticodeArray {
109    /// Array of authenticode signatures.
110    #[must_use]
111    pub fn signatures(&self) -> &[Authenticode] {
112        // Safety: invariant of the struct: pointer must not be null.
113        let this = unsafe { &(*self.0) };
114
115        if this.signatures.is_null() {
116            &[]
117        } else {
118            // Safety:
119            // The signatures field has type `*mut *mut sys::Authenticode`. It is safe to cast
120            // to `*mut Authenticode` because:
121            // - The Authenticode type is a transparent wrapper on a &sys::Authenticode
122            // - The `*mut sys::Authenticode` pointers in the array are guaranteed to be non-null
123            //   (checked by auditing the C code).
124            let signatures = this.signatures.cast::<Authenticode>();
125
126            // Safety:
127            // - The signatures + count pair is guaranteed by the library to represent an array.
128            // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
129            //   freed before the slice is dropped.
130            unsafe { std::slice::from_raw_parts(signatures, this.count) }
131        }
132    }
133}
134
135/// Authenticode signature.
136#[repr(transparent)]
137#[derive(Debug)]
138pub struct Authenticode<'a>(&'a sys::Authenticode);
139
140impl Authenticode<'_> {
141    /// Flags related to verification.
142    #[must_use]
143    pub fn verify_flags(&self) -> Option<AuthenticodeVerify> {
144        match self.0.verify_flags {
145            0 => Some(AuthenticodeVerify::Valid),
146            1 => Some(AuthenticodeVerify::CantParse),
147            2 => Some(AuthenticodeVerify::NoSignerCert),
148            3 => Some(AuthenticodeVerify::DigestMissing),
149            4 => Some(AuthenticodeVerify::InternalError),
150            5 => Some(AuthenticodeVerify::NoSignerInfo),
151            6 => Some(AuthenticodeVerify::WrongPkcs7Type),
152            7 => Some(AuthenticodeVerify::BadContent),
153            8 => Some(AuthenticodeVerify::Invalid),
154            9 => Some(AuthenticodeVerify::WrongFileDigest),
155            10 => Some(AuthenticodeVerify::UnknownAlgorithm),
156            _ => None,
157        }
158    }
159
160    /// Raw PCKS7 version.
161    #[must_use]
162    pub fn version(&self) -> i32 {
163        self.0.version
164    }
165
166    /// Name of the digest algorithm.
167    #[must_use]
168    pub fn digest_alg(&self) -> Option<&[u8]> {
169        // Safety: the pointer is valid as long as self is not dropped.
170        unsafe { cstr_ptr_to_slice(&self.0.digest_alg) }
171    }
172
173    /// File digest stored in the signature.
174    #[must_use]
175    pub fn digest(&self) -> Option<&[u8]> {
176        byte_array_to_slice(&self.0.digest)
177    }
178
179    /// Actual calculated file digest.
180    #[must_use]
181    pub fn file_digest(&self) -> Option<&[u8]> {
182        byte_array_to_slice(&self.0.file_digest)
183    }
184
185    /// `SignerInfo` information of the authenticode
186    #[must_use]
187    pub fn signer(&self) -> Option<Signer> {
188        if self.0.signer.is_null() {
189            None
190        } else {
191            // Safety:
192            // - The pointer is not null.
193            // - The pointer is valid as long as self is not dropped.
194            Some(Signer(unsafe { &*self.0.signer }))
195        }
196    }
197
198    /// All certificates in the Signature.
199    ///
200    /// This includes the ones in timestamp countersignatures.
201    #[must_use]
202    pub fn certs(&self) -> &[Certificate] {
203        if self.0.certs.is_null() {
204            &[]
205        } else {
206            // Safety: pointer is not null.
207            let this = unsafe { &(*self.0.certs) };
208
209            let certs = if this.certs.is_null() {
210                std::ptr::NonNull::<Certificate>::dangling().as_ptr()
211            } else {
212                // Safety:
213                // The certs field has type `*mut *mut sys::Certificate`. It is safe to cast
214                // to `*mut Certificate` because the Certificate type is a transparent wrapper
215                // on a &sys::Certificate.
216                this.certs.cast::<Certificate>()
217            };
218
219            // Safety:
220            // - The certs + count pair is guaranteed by the library to represent an array.
221            // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
222            //   freed before the slice is dropped.
223            unsafe { std::slice::from_raw_parts(certs, this.count) }
224        }
225    }
226
227    /// Timestamp countersignatures.
228    #[must_use]
229    pub fn countersigs(&self) -> &[Countersignature] {
230        if self.0.countersigs.is_null() {
231            &[]
232        } else {
233            // Safety: pointer is not null.
234            let this = unsafe { &(*self.0.countersigs) };
235
236            // `this.counters` may be null, and in that case we can't simply cast the null
237            // pointer and pass it to `std::slice::from_raw_parts` because this function
238            // doesn't accept null pointers. We need a NonNull::dangling() pointer, which
239            // is the right way of creating an empty slice with `std::slice::from_raw_parts`.
240            let counters = if this.counters.is_null() {
241                std::ptr::NonNull::<Countersignature>::dangling().as_ptr()
242            } else {
243                // Safety:
244                // The certs field has type `*mut *mut sys::Countersignature`. It is safe to cast
245                // to `*mut Countersignature` because the Countersignature type is a transparent
246                // wrapper on a &sys::Countersignature.
247                this.counters.cast::<Countersignature>()
248            };
249
250            // Safety:
251            // - The counters + count pair is guaranteed by the library to represent an array.
252            // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
253            //   freed before the slice is dropped.
254            unsafe { std::slice::from_raw_parts(counters, this.count) }
255        }
256    }
257}
258
259/// Represents `SignerInfo` structure.
260#[repr(transparent)]
261#[derive(Debug)]
262pub struct Signer<'a>(&'a sys::Signer);
263
264impl Signer<'_> {
265    /// Message Digest of the `SignerInfo`
266    #[must_use]
267    pub fn digest(&self) -> Option<&[u8]> {
268        byte_array_to_slice(&self.0.digest)
269    }
270
271    /// Name of the digest algorithm.
272    #[must_use]
273    pub fn digest_alg(&self) -> Option<&[u8]> {
274        // Safety: the pointer is valid as long as self is not dropped.
275        unsafe { cstr_ptr_to_slice(&self.0.digest_alg) }
276    }
277
278    /// Program name stored in `SpcOpusInfo` structure of Authenticode */
279    #[must_use]
280    pub fn program_name(&self) -> Option<&[u8]> {
281        // Safety: the pointer is valid as long as self is not dropped.
282        unsafe { cstr_ptr_to_slice(&self.0.program_name) }
283    }
284
285    /// Certificate chain of the signer
286    #[must_use]
287    pub fn certificate_chain(&self) -> &[Certificate] {
288        if self.0.chain.is_null() {
289            &[]
290        } else {
291            // Safety: pointer is not null.
292            let this = unsafe { &(*self.0.chain) };
293
294            let certs = if this.certs.is_null() {
295                std::ptr::NonNull::<Certificate>::dangling().as_ptr()
296            } else {
297                // Safety:
298                // The certs field has type `*mut *mut sys::Certificate`. It is safe to cast
299                // to `*mut Certificate` because the Certificate type is a transparent wrapper
300                // on a &sys::Certificate.
301                this.certs.cast::<Certificate>()
302            };
303
304            // Safety:
305            // - The certs + count pair is guaranteed by the library to represent an array.
306            // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
307            //   freed before the slice is dropped.
308            unsafe { std::slice::from_raw_parts(certs, this.count) }
309        }
310    }
311}
312
313/// Authenticode counter signature.
314#[repr(transparent)]
315#[derive(Debug)]
316pub struct Countersignature<'a>(&'a sys::Countersignature);
317
318impl Countersignature<'_> {
319    /// Countersignature verify flags.
320    #[must_use]
321    pub fn verify_flags(&self) -> Option<CounterSignatureVerify> {
322        match self.0.verify_flags {
323            0 => Some(CounterSignatureVerify::Valid),
324            1 => Some(CounterSignatureVerify::CantParse),
325            2 => Some(CounterSignatureVerify::NoSignerCert),
326            3 => Some(CounterSignatureVerify::UnknownAlgorithm),
327            4 => Some(CounterSignatureVerify::Invalid),
328            5 => Some(CounterSignatureVerify::CantDecryptDigest),
329            6 => Some(CounterSignatureVerify::DigestMissing),
330            7 => Some(CounterSignatureVerify::DoesntMatchSignature),
331            8 => Some(CounterSignatureVerify::InternalError),
332            9 => Some(CounterSignatureVerify::TimeMissing),
333            _ => None,
334        }
335    }
336
337    /// Signing time of the timestamp countersignature.
338    #[must_use]
339    pub fn sign_time(&self) -> i64 {
340        self.0.sign_time
341    }
342
343    /// Name of the digest algorithm.
344    #[must_use]
345    pub fn digest_alg(&self) -> Option<&[u8]> {
346        // Safety: the pointer is valid as long as self is not dropped.
347        unsafe { cstr_ptr_to_slice(&self.0.digest_alg) }
348    }
349
350    /// Stored message digest.
351    #[must_use]
352    pub fn digest(&self) -> Option<&[u8]> {
353        byte_array_to_slice(&self.0.digest)
354    }
355
356    /// Certificate chain of the signer
357    #[must_use]
358    pub fn certificate_chain(&self) -> &[Certificate] {
359        if self.0.chain.is_null() {
360            &[]
361        } else {
362            // Safety: pointer is not null.
363            let this = unsafe { &(*self.0.chain) };
364
365            let certs = if this.certs.is_null() {
366                std::ptr::NonNull::<Certificate>::dangling().as_ptr()
367            } else {
368                // Safety:
369                // The certs field has type `*mut *mut sys::Certificate`. It is safe to cast
370                // to `*mut Certificate` because the Certificate type is a transparent wrapper
371                // on a &sys::Certificate.
372                this.certs.cast::<Certificate>()
373            };
374
375            // Safety:
376            // - The certs + count pair is guaranteed by the library to represent an array.
377            // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
378            //   freed before the slice is dropped.
379            unsafe { std::slice::from_raw_parts(certs, this.count) }
380        }
381    }
382
383    /// All certs stored inside Countersignature.
384    ///
385    /// This can be a superset of `certificate_chain` in case of non PKCS9 countersignature.
386    #[must_use]
387    pub fn certs(&self) -> &[Certificate] {
388        if self.0.certs.is_null() {
389            &[]
390        } else {
391            // Safety: pointer is not null.
392            let this = unsafe { &(*self.0.certs) };
393
394            let certs = if this.certs.is_null() {
395                std::ptr::NonNull::<Certificate>::dangling().as_ptr()
396            } else {
397                // Safety:
398                // The certs field has type `*mut *mut sys::Certificate`. It is safe to cast
399                // to `*mut Certificate` because the Certificate type is a transparent wrapper
400                // on a &sys::Certificate.
401                this.certs.cast::<Certificate>()
402            };
403
404            // Safety:
405            // - The certs + count pair is guaranteed by the library to represent an array.
406            // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
407            //   freed before the slice is dropped.
408            unsafe { std::slice::from_raw_parts(certs, this.count) }
409        }
410    }
411}
412
413/// Authenticode certificate.
414#[repr(transparent)]
415#[derive(Debug)]
416pub struct Certificate<'a>(&'a sys::Certificate);
417
418impl Certificate<'_> {
419    /// Raw version of X509.
420    #[must_use]
421    pub fn version(&self) -> i64 {
422        // False positive: this conversion is needed on arch where long is i32
423        #[allow(clippy::useless_conversion)]
424        i64::from(self.0.version)
425    }
426
427    /// Oneline name of Issuer.
428    #[must_use]
429    pub fn issuer(&self) -> Option<&[u8]> {
430        // Safety: the pointer is valid as long as self is not dropped.
431        unsafe { cstr_ptr_to_slice(&self.0.issuer) }
432    }
433    /// Oneline name of Subject.
434    #[must_use]
435    pub fn subject(&self) -> Option<&[u8]> {
436        // Safety: the pointer is valid as long as self is not dropped.
437        unsafe { cstr_ptr_to_slice(&self.0.subject) }
438    }
439    /// Serial number in format 00:01:02:03:04...
440    #[must_use]
441    pub fn serial(&self) -> Option<&[u8]> {
442        // Safety: the pointer is valid as long as self is not dropped.
443        unsafe { cstr_ptr_to_slice(&self.0.serial) }
444    }
445
446    /// SHA1 of the DER representation of the cert.
447    #[must_use]
448    pub fn sha1(&self) -> Option<&[u8]> {
449        byte_array_to_slice(&self.0.sha1)
450    }
451
452    /// SHA256 of the DER representation of the cert.
453    #[must_use]
454    pub fn sha256(&self) -> Option<&[u8]> {
455        byte_array_to_slice(&self.0.sha256)
456    }
457
458    /// Name of the key algorithm.
459    #[must_use]
460    pub fn key_alg(&self) -> Option<&[u8]> {
461        // Safety: the pointer is valid as long as self is not dropped.
462        unsafe { cstr_ptr_to_slice(&self.0.key_alg) }
463    }
464
465    /// Name of the signature algorithm.
466    #[must_use]
467    pub fn sig_alg(&self) -> Option<&[u8]> {
468        // Safety: the pointer is valid as long as self is not dropped.
469        unsafe { cstr_ptr_to_slice(&self.0.sig_alg) }
470    }
471
472    /// OID of the signature algorithm.
473    #[must_use]
474    pub fn sig_alg_oid(&self) -> Option<&[u8]> {
475        // Safety: the pointer is valid as long as self is not dropped.
476        unsafe { cstr_ptr_to_slice(&self.0.sig_alg_oid) }
477    }
478
479    /// `NotBefore` validity.
480    #[must_use]
481    pub fn not_before(&self) -> i64 {
482        self.0.not_before
483    }
484
485    /// `NotAfter` validity.
486    #[must_use]
487    pub fn not_after(&self) -> i64 {
488        self.0.not_after
489    }
490
491    /// PEM encoded public key.
492    #[must_use]
493    pub fn key(&self) -> Option<&[u8]> {
494        // Safety: the pointer is valid as long as self is not dropped.
495        unsafe { cstr_ptr_to_slice(&self.0.key) }
496    }
497
498    /// Parsed X509 Attributes of Issuer.
499    #[must_use]
500    pub fn issuer_attrs(&self) -> Attributes {
501        Attributes(&self.0.issuer_attrs)
502    }
503
504    /// Parsed X509 Attributes of Subject.
505    #[must_use]
506    pub fn subject_attrs(&self) -> Attributes {
507        Attributes(&self.0.subject_attrs)
508    }
509}
510
511/// Various X509 attributes parsed out in raw bytes.
512pub struct Attributes<'a>(&'a sys::Attributes);
513
514impl Attributes<'_> {
515    /// Country
516    #[must_use]
517    pub fn country(&self) -> Option<&[u8]> {
518        byte_array_to_slice(&self.0.country)
519    }
520
521    /// Organization
522    #[must_use]
523    pub fn organization(&self) -> Option<&[u8]> {
524        byte_array_to_slice(&self.0.organization)
525    }
526
527    /// Organizational unit
528    #[must_use]
529    pub fn organizational_unit(&self) -> Option<&[u8]> {
530        byte_array_to_slice(&self.0.organizationalUnit)
531    }
532
533    /// Name qualifier
534    #[must_use]
535    pub fn name_qualifier(&self) -> Option<&[u8]> {
536        byte_array_to_slice(&self.0.nameQualifier)
537    }
538
539    /// State
540    #[must_use]
541    pub fn state(&self) -> Option<&[u8]> {
542        byte_array_to_slice(&self.0.state)
543    }
544
545    /// Common name
546    #[must_use]
547    pub fn common_name(&self) -> Option<&[u8]> {
548        byte_array_to_slice(&self.0.commonName)
549    }
550
551    /// Serial number
552    #[must_use]
553    pub fn serial_number(&self) -> Option<&[u8]> {
554        byte_array_to_slice(&self.0.serialNumber)
555    }
556
557    /// Locality
558    #[must_use]
559    pub fn locality(&self) -> Option<&[u8]> {
560        byte_array_to_slice(&self.0.locality)
561    }
562
563    /// Title
564    #[must_use]
565    pub fn title(&self) -> Option<&[u8]> {
566        byte_array_to_slice(&self.0.title)
567    }
568
569    /// Surname
570    #[must_use]
571    pub fn surname(&self) -> Option<&[u8]> {
572        byte_array_to_slice(&self.0.surname)
573    }
574
575    /// Given name
576    #[must_use]
577    pub fn given_name(&self) -> Option<&[u8]> {
578        byte_array_to_slice(&self.0.givenName)
579    }
580
581    /// Initials
582    #[must_use]
583    pub fn initials(&self) -> Option<&[u8]> {
584        byte_array_to_slice(&self.0.initials)
585    }
586
587    /// Pseudonym
588    #[must_use]
589    pub fn pseudonym(&self) -> Option<&[u8]> {
590        byte_array_to_slice(&self.0.pseudonym)
591    }
592
593    /// Generation qualifier
594    #[must_use]
595    pub fn generation_qualifier(&self) -> Option<&[u8]> {
596        byte_array_to_slice(&self.0.generationQualifier)
597    }
598
599    /// Email address
600    #[must_use]
601    pub fn email_address(&self) -> Option<&[u8]> {
602        byte_array_to_slice(&self.0.emailAddress)
603    }
604}
605
606fn byte_array_to_slice(digest: &sys::ByteArray) -> Option<&[u8]> {
607    if digest.data.is_null() {
608        None
609    } else {
610        let len = if digest.len <= 0 {
611            0
612        } else {
613            match usize::try_from(digest.len) {
614                Ok(v) => v,
615                Err(_) => usize::MAX,
616            }
617        };
618        // Safety:
619        // - The data + len pair is guaranteed by the library to represent an array.
620        // - The lifetime of the slice is tied to the lifetime of self, so the memory cannot be
621        //   freed before the slice is dropped.
622        Some(unsafe { std::slice::from_raw_parts(digest.data, len) })
623    }
624}
625
626/// Cast a ptr to a cstring to slice of bytes.
627///
628/// Safety:
629///
630/// The pointer must be valid at least as long as the borrow used to generate the lifetime of
631/// the slice.
632unsafe fn cstr_ptr_to_slice(ptr: &*mut std::os::raw::c_char) -> Option<&[u8]> {
633    if ptr.is_null() {
634        None
635    } else {
636        // Safety:
637        // - The pointer is not null.
638        // - The library guarantees this is a pointer to a c string (so it has a null terminator).
639        // - The pointer is valid as long as the lifetime used.
640        let cstr = unsafe { std::ffi::CStr::from_ptr(ptr.cast()) };
641        Some(cstr.to_bytes())
642    }
643}
644
645/// Status of verification for a counter signature.
646#[derive(Debug, PartialEq, Eq)]
647pub enum CounterSignatureVerify {
648    /// Countersignature is valid
649    Valid,
650    /// Parsing error (from OpenSSL functions)
651    CantParse,
652    /// Signers certificate is missing
653    NoSignerCert,
654    /// Unknown algorithm, can't proceed with verification
655    UnknownAlgorithm,
656    /// Verification failed, digest mismatch
657    Invalid,
658    /// Failed to decrypt countersignature enc_digest for verification
659    CantDecryptDigest,
660    /// No digest saved inside the countersignature
661    DigestMissing,
662    /// Message digest inside countersignature doesn't match signature it countersigns
663    DoesntMatchSignature,
664    /// Non verification errors - allocations etc.
665    InternalError,
666    /// Time is missing in the timestamp signature
667    TimeMissing,
668}
669
670/// Status of verification for an authenticode signature.
671#[derive(Debug, PartialEq, Eq)]
672pub enum AuthenticodeVerify {
673    /// Signature is valid
674    Valid,
675    /// Parsing error (from OpenSSL functions)
676    CantParse,
677    /// Signers certificate is missing
678    NoSignerCert,
679    /// No digest saved inside the signature
680    DigestMissing,
681    /// Non verification errors - allocations etc.
682    InternalError,
683    /// SignerInfo part of PKCS7 is missing
684    NoSignerInfo,
685    /// PKCS7 doesn't have type of SignedData, can't proceed
686    WrongPkcs7Type,
687    /// PKCS7 doesn't have corrent content, can't proceed
688    BadContent,
689    /// Contained and calculated digest don't match
690    Invalid,
691    /// Signature hash and file hash doesn't match
692    WrongFileDigest,
693    /// Unknown algorithm, can't proceed with verification
694    UnknownAlgorithm,
695}