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}