1 #include "decryption_state_machine.h"
2 #include <limits>
3 #include "state_graph.h"
4 #include "state_machine.h"
5 #include "encryptmsg/emsg_exception.h"
6 #include "decryption_state_debug.h"
7 #include "x2_key_loader.h"
8 #include "key_file_converter.h"
9 #include "wad_reader_writer.h"
10 #include "epad_result.h"
11 #include "encryptmsg/openpgp_conversions.h"
12 
13 using namespace LightStateMachine;
14 using namespace EncryptMsg;
15 using namespace EncryptPad;
16 
17 namespace EncryptPad
18 {
19 
20     struct DecryptionStateMachine : public LightStateMachine::NonCopyable
21     {
22         int filter_count_;
23         Format format_;
24         EpadResult result_;
25         bool is_wad_head_finished_;
26         InStream &in_;
27         OutStream &out_;
28         const EncryptParams &encrypt_params_;
29         PacketMetadata &metadata_;
30         EncryptMsg::SafeVector buffer_;
31         EncryptMsg::SafeVector pending_buffer_;
32         std::unique_ptr<DecryptionSession> passphrase_session_;
33         std::unique_ptr<DecryptionSession> key_file_session_;
34         ProgressEvent progress_event_;
35 
36         StateGraph graph_;
37         StateMachineContext context_;
38         StateMachine state_machine_;
39 
40         bool ReadIn_CanEnter(StateMachineContext &ctx);
41         bool ParseFormat_CanEnter(StateMachineContext &ctx);
42         bool SetPwdKey_CanEnter(StateMachineContext &ctx);
43         bool ReadKeyFile_CanEnter(StateMachineContext &ctx);
44         bool GPG_CanEnter(StateMachineContext &ctx);
45         bool WADHead_CanEnter(StateMachineContext &ctx);
46         bool WriteOut_CanEnter(StateMachineContext &ctx);
47         bool End_CanEnter(StateMachineContext &ctx);
48 
49         void ReadIn_OnEnter(StateMachineContext &ctx);
50         void ParseFormat_OnEnter(StateMachineContext &ctx);
51         void SetPwdKey_OnEnter(StateMachineContext &ctx);
52         void ReadKeyFile_OnEnter(StateMachineContext &ctx);
53         void GPG_OnEnter(StateMachineContext &ctx);
54         void WADHead_OnEnter(StateMachineContext &ctx);
55         void WriteOut_OnEnter(StateMachineContext &ctx);
56         void Fail_OnEnter(StateMachineContext &ctx);
57         void End_OnEnter(StateMachineContext &ctx);
58 
59         bool IsEncryptedEmptyString();
60 
61         DecryptionStateMachine(InStream &in, OutStream &out,
62                 const EncryptParams &encrypt_params, PacketMetadata &metadata);
63         void DecryptStream();
64     };
65 
DecryptionStateMachine(InStream & in,OutStream & out,const EncryptParams & encrypt_params,PacketMetadata & metadata)66     DecryptionStateMachine::DecryptionStateMachine(InStream &in, OutStream &out,
67             const EncryptParams &encrypt_params, PacketMetadata &metadata):
68         filter_count_(0),
69         format_(Format::Unknown),
70         result_(EpadResult::None),
71         is_wad_head_finished_(false),
72         in_(in),
73         out_(out),
74         encrypt_params_(encrypt_params),
75         metadata_(metadata),
76         progress_event_(in_.GetCount(), 0),
77         state_machine_(graph_, context_)
78     {
79         using Self = DecryptionStateMachine;
80         using VoidF = LightStateMachine::VoidMemberFunction<DecryptionStateMachine>;
81         using BoolF = LightStateMachine::BoolMemberFunction<DecryptionStateMachine>;
82 
83         // Start state
84 
85         graph_.Create(StateID::Start);
86 
87         // Fail state
88 
89         graph_.Create(StateID::Fail,
90                 VoidF(this, &Self::Fail_OnEnter), StubVoidFunction,
91                 StubBoolFunction, AlwaysFalseBoolFunction);
92 
93         graph_.SetStartStateID(StateID::Start);
94         graph_.SetFailStateID(StateID::Fail);
95 
96         // Other states
97 
98         graph_.Create(StateID::ReadIn,
99                 VoidF(this, &Self::ReadIn_OnEnter), StubVoidFunction,
100                 BoolF(this, &Self::ReadIn_CanEnter), StubBoolFunction);
101 
102         graph_.Create(StateID::ParseFormat,
103                 VoidF(this, &Self::ParseFormat_OnEnter), StubVoidFunction,
104                 BoolF(this, &Self::ParseFormat_CanEnter), StubBoolFunction);
105 
106         graph_.Create(StateID::SetPwdKey,
107                 VoidF(this, &Self::SetPwdKey_OnEnter), StubVoidFunction,
108                 BoolF(this, &Self::SetPwdKey_CanEnter), StubBoolFunction);
109 
110         graph_.Create(StateID::ReadKeyFile,
111                 VoidF(this, &Self::ReadKeyFile_OnEnter), StubVoidFunction,
112                 BoolF(this, &Self::ReadKeyFile_CanEnter), StubBoolFunction);
113 
114         graph_.Create(StateID::GPG,
115                 VoidF(this, &Self::GPG_OnEnter), StubVoidFunction,
116                 BoolF(this, &Self::GPG_CanEnter), StubBoolFunction);
117 
118         graph_.Create(StateID::WADHead,
119                 VoidF(this, &Self::WADHead_OnEnter), StubVoidFunction,
120                 BoolF(this, &Self::WADHead_CanEnter), StubBoolFunction);
121 
122         graph_.Create(StateID::WriteOut,
123                 VoidF(this, &Self::WriteOut_OnEnter), StubVoidFunction,
124                 BoolF(this, &Self::WriteOut_CanEnter), StubBoolFunction);
125 
126         graph_.Create(StateID::End,
127                 VoidF(this, &Self::End_OnEnter), StubVoidFunction,
128                 BoolF(this, &Self::End_CanEnter), AlwaysFalseBoolFunction);
129 
130 
131         graph_.Link(StateID::Start, StateID::ReadIn);
132         graph_.Link(StateID::ReadIn, StateID::End);
133         graph_.Link(StateID::ReadIn, StateID::ParseFormat);
134         graph_.Link(StateID::ReadIn, StateID::GPG);
135         graph_.Link(StateID::ReadIn, StateID::WADHead);
136 
137         graph_.Link(StateID::ParseFormat, StateID::ReadIn);
138         graph_.Link(StateID::ParseFormat, StateID::GPG);
139         graph_.Link(StateID::ParseFormat, StateID::WADHead);
140         graph_.Link(StateID::ParseFormat, StateID::ReadKeyFile);
141         graph_.Link(StateID::ParseFormat, StateID::SetPwdKey);
142         graph_.Link(StateID::ParseFormat, StateID::WriteOut);
143 
144         graph_.Link(StateID::SetPwdKey, StateID::GPG);
145 
146         graph_.Link(StateID::ReadKeyFile, StateID::ReadIn);
147         graph_.Link(StateID::ReadKeyFile, StateID::GPG);
148 
149         graph_.Link(StateID::GPG, StateID::ParseFormat);
150         graph_.Link(StateID::GPG, StateID::ReadIn);
151         graph_.Link(StateID::GPG, StateID::WADHead);
152         graph_.Link(StateID::GPG, StateID::GPG);
153         graph_.Link(StateID::GPG, StateID::WriteOut);
154 
155         graph_.Link(StateID::WADHead, StateID::ReadKeyFile);
156         graph_.Link(StateID::WADHead, StateID::GPG);
157         graph_.Link(StateID::WADHead, StateID::ReadIn);
158 
159         graph_.Link(StateID::WriteOut, StateID::ReadIn);
160 
161         state_machine_.SetStateIDToStringConverter(
162                 [](StateMachineStateID id){
163                     return std::string("decryption: ") +
164                     PrintDecryptionStateMachineStateID(id);});
165     }
166 
DecryptStream()167     void DecryptionStateMachine::DecryptStream()
168     {
169         while(state_machine_.NextState())
170         {
171         }
172     }
173 
DecryptStream(InStream & in,const EncryptParams & encrypt_params,OutStream & out,PacketMetadata & metadata)174     EpadResult DecryptStream(InStream &in, const EncryptParams &encrypt_params,
175             OutStream &out, PacketMetadata &metadata)
176     {
177         DecryptionStateMachine state_machine(in, out, encrypt_params, metadata);
178         state_machine.DecryptStream();
179         return state_machine.result_;
180     }
181 
ReadIn_CanEnter(LightStateMachine::StateMachineContext & ctx)182     bool DecryptionStateMachine::ReadIn_CanEnter(LightStateMachine::StateMachineContext &ctx)
183     {
184         return buffer_.size() == 0;
185     }
186 
ReadIn_OnEnter(LightStateMachine::StateMachineContext & ctx)187     void DecryptionStateMachine::ReadIn_OnEnter(LightStateMachine::StateMachineContext &ctx)
188     {
189         //memory_buffer should be smaller than max sizes of both types
190         assert(static_cast<uint64_t>(encrypt_params_.memory_buffer) <=
191                 std::min(
192                     static_cast<uint64_t>(std::numeric_limits<stream_length_type>::max()),
193                     static_cast<uint64_t>(std::numeric_limits<size_t>::max())
194                     ));
195         stream_length_type size2read = std::min(static_cast<stream_length_type>(encrypt_params_.memory_buffer), in_.GetCount());
196         buffer_.resize(static_cast<size_t>(size2read));
197         size_t size = in_.Read(buffer_.data(), buffer_.size());
198         buffer_.resize(size);
199         filter_count_ = 0;
200         encrypt_params_.progress_callback(progress_event_);
201         if(progress_event_.cancel)
202         {
203             result_ = EpadResult::Cancelled;
204             ctx.SetFailed(true);
205         }
206         progress_event_.complete_bytes += size;
207 
208     }
209 
End_CanEnter(LightStateMachine::StateMachineContext & ctx)210     bool DecryptionStateMachine::End_CanEnter(LightStateMachine::StateMachineContext &ctx)
211     {
212         return in_.IsEOF() && buffer_.empty() && pending_buffer_.empty()
213             && result_ == EpadResult::Success;
214     }
215 
End_OnEnter(LightStateMachine::StateMachineContext & ctx)216     void DecryptionStateMachine::End_OnEnter(LightStateMachine::StateMachineContext &ctx)
217     {
218         // Take outer reader
219         MessageReader *reader = nullptr;
220         switch(format_)
221         {
222             case Format::NestedWAD:
223             case Format::GPG:
224                 reader = &passphrase_session_->reader;
225                 break;
226 
227             case Format::GPGByKeyFile:
228             case Format::WAD:
229                 reader = &key_file_session_->reader;
230                 break;
231 
232             default:
233                 result_ = EpadResult::UnexpectedError;
234                 ctx.SetFailed(true);
235                 return;
236         }
237         auto &metadata = metadata_;
238         auto &config = reader->GetMessageConfig();
239 
240         metadata.file_name = config.GetFileName();
241         metadata.file_date = config.GetFileDate();
242         metadata.is_binary = config.GetBinary();
243         metadata.cipher_algo = config.GetCipherAlgo();
244         metadata.compression = config.GetCompression();
245         metadata.hash_algo = config.GetHashAlgo();
246         metadata.iterations = DecodeS2KIterations(config.GetIterations());
247         metadata.salt = reader->GetSalt();
248 
249         result_ = EpadResult::Success;
250     }
251 
ParseFormat_CanEnter(LightStateMachine::StateMachineContext & ctx)252     bool DecryptionStateMachine::ParseFormat_CanEnter(LightStateMachine::StateMachineContext &ctx)
253     {
254         switch(format_)
255         {
256             case Format::Unknown:
257                 break;
258             case Format::GPGOrNestedWad:
259                 if(filter_count_ != 1)
260                     return false;
261                 break;
262             default:
263                 return false;
264         }
265 
266         return buffer_.size() > 0 || pending_buffer_.size() > 0 || IsEncryptedEmptyString();
267     }
268 
ParseFormat_OnEnter(LightStateMachine::StateMachineContext & ctx)269     void DecryptionStateMachine::ParseFormat_OnEnter(LightStateMachine::StateMachineContext &ctx)
270     {
271         size_t required_bytes = filter_count_ == 1 ? 4 : 1;
272 
273         if(IsEncryptedEmptyString())
274         {
275             format_ = Format::GPG;
276             return;
277         }
278 
279         pending_buffer_.insert(pending_buffer_.end(), buffer_.begin(), buffer_.end());
280         buffer_.clear();
281 
282         // We need more bytes
283         if(pending_buffer_.size() < required_bytes && !in_.IsEOF())
284             return;
285 
286         if(filter_count_ == 0)
287         {
288             uint8_t b = pending_buffer_[0];
289             if((b & 0x80 && b != 0xEF) || b == '-')
290             {
291                 if(metadata_.key_only)
292                 {
293                     format_ = Format::GPGByKeyFile;
294                 }
295                 else
296                 {
297                     format_ = Format::GPGOrNestedWad;
298                 }
299             }
300             else // wad starts from I or P, in which the most significant bit is not set
301             {
302                 format_ = Format::WAD;
303             }
304         }
305         else if(filter_count_ == 1)
306         {
307             std::string marker;
308 
309             if(pending_buffer_.size() >= 4)
310             {
311                 marker.insert(0U, reinterpret_cast<const char*>(pending_buffer_.data()), 4U);
312             }
313 
314             if(marker == "IWAD" || marker == "PWAD")
315             {
316                 format_ = Format::NestedWAD;
317             }
318             else
319             {
320                 format_ = Format::GPG;
321             }
322         }
323         else
324         {
325             // Filter count can be only 0 or 1
326             assert(false);
327         }
328 
329         buffer_.swap(pending_buffer_);
330     }
331 
GPG_CanEnter(LightStateMachine::StateMachineContext & ctx)332     bool DecryptionStateMachine::GPG_CanEnter(LightStateMachine::StateMachineContext &ctx)
333     {
334         if(filter_count_ > 1)
335             return false;
336 
337         switch(format_)
338         {
339             case Format::Empty:
340             case Format::Unknown:
341                 return false;
342 
343             case Format::GPG:
344             case Format::GPGOrNestedWad:
345                 if(!passphrase_session_)
346                     return false;
347                 if(filter_count_ == 1)
348                     return false;
349                 break;
350 
351             case Format::GPGByKeyFile:
352                 if(!key_file_session_)
353                     return false;
354                 if(filter_count_ == 1)
355                     return false;
356                 break;
357 
358             case Format::WAD:
359                 if(!is_wad_head_finished_)
360                     return false;
361                 if(!key_file_session_)
362                     return false;
363                 if(filter_count_ == 1)
364                     return false;
365                 break;
366 
367             case Format::NestedWAD:
368                 assert(passphrase_session_);
369                 if(filter_count_ == 1 && !is_wad_head_finished_)
370                     return false;
371                 if(filter_count_ == 1 && !key_file_session_)
372                     return false;
373                 break;
374 
375             default:
376                 break;
377 
378         }
379 
380         if(buffer_.size() == 0)
381             return false;
382 
383         return true;
384     }
385 
GPG_OnEnter(LightStateMachine::StateMachineContext & ctx)386     void DecryptionStateMachine::GPG_OnEnter(LightStateMachine::StateMachineContext &ctx)
387     {
388         using namespace EncryptMsg;
389         MessageReader *reader = nullptr;
390         bool is_key_file_session = false;
391         switch(format_)
392         {
393             case Format::GPG:
394             case Format::GPGOrNestedWad:
395                 assert(filter_count_ == 0);
396                 reader = &passphrase_session_->reader;
397                 break;
398 
399             case Format::NestedWAD:
400                 is_key_file_session = (filter_count_ > 0);
401                 reader = (!is_key_file_session)
402                     ?
403                     &passphrase_session_->reader
404                     :
405                     &key_file_session_->reader;
406                 break;
407 
408             default:
409                 is_key_file_session = true;
410                 reader = &key_file_session_->reader;
411                 break;
412         }
413 
414         try
415         {
416             if(in_.IsEOF())
417             {
418                 reader->Finish(buffer_);
419             }
420             else
421             {
422                 reader->Update(buffer_);
423             }
424         }
425         catch(const EmsgException &e)
426         {
427             result_ = ToEpadResult(e.result);
428             if(is_key_file_session && (result_ == EpadResult::InvalidSurrogateIV ||
429                         result_ == EpadResult::MDCError))
430             {
431                 result_ = EpadResult::InvalidKeyFile;
432             }
433             ctx.SetFailed(true);
434             return;
435         }
436         filter_count_ ++;
437         result_ = EpadResult::Success;
438     }
439 
SetPwdKey_CanEnter(LightStateMachine::StateMachineContext & ctx)440     bool DecryptionStateMachine::SetPwdKey_CanEnter(LightStateMachine::StateMachineContext &ctx)
441     {
442         switch(format_)
443         {
444             case Format::GPG:
445             case Format::GPGOrNestedWad:
446                 if(!passphrase_session_)
447                     return true;
448                 break;
449 
450             default:
451                 break;
452         }
453         return false;
454     }
455 
SetPwdKey_OnEnter(LightStateMachine::StateMachineContext & ctx)456     void DecryptionStateMachine::SetPwdKey_OnEnter(LightStateMachine::StateMachineContext &ctx)
457     {
458         passphrase_session_.reset(
459                 new DecryptionSession(
460                     encrypt_params_.key_service,
461                     encrypt_params_.passphrase)
462                 );
463     }
464 
WriteOut_CanEnter(LightStateMachine::StateMachineContext & ctx)465     bool DecryptionStateMachine::WriteOut_CanEnter(LightStateMachine::StateMachineContext &ctx)
466     {
467         if(buffer_.size() == 0)
468             return false;
469 
470         switch(format_)
471         {
472             case Format::Empty:
473             case Format::Unknown:
474             case Format::GPGOrNestedWad:
475                 return false;
476 
477             case Format::GPGByKeyFile:
478             case Format::GPG:
479             case Format::WAD:
480                 if(filter_count_ != 1)
481                     return false;
482                 break;
483 
484             case Format::NestedWAD:
485                 if(filter_count_ != 2)
486                     return false;
487                 break;
488 
489             default:
490                 break;
491         }
492         return true;
493     }
494 
WriteOut_OnEnter(LightStateMachine::StateMachineContext & ctx)495     void DecryptionStateMachine::WriteOut_OnEnter(LightStateMachine::StateMachineContext &ctx)
496     {
497         out_.Write(buffer_.data(), buffer_.size());
498         buffer_.clear();
499     }
500 
Fail_OnEnter(LightStateMachine::StateMachineContext & ctx)501     void DecryptionStateMachine::Fail_OnEnter(LightStateMachine::StateMachineContext &ctx)
502     {
503         // If we came here because no states can enter
504         if(result_ == EpadResult::Success)
505             result_ = EpadResult::UnexpectedError;
506     }
507 
ReadKeyFile_CanEnter(LightStateMachine::StateMachineContext & ctx)508     bool DecryptionStateMachine::ReadKeyFile_CanEnter(LightStateMachine::StateMachineContext &ctx)
509     {
510         if(key_file_session_)
511             return false;
512 
513         switch(format_)
514         {
515             case Format::GPGByKeyFile:
516                 break;
517 
518             case Format::WAD:
519             case Format::NestedWAD:
520                 if(!is_wad_head_finished_)
521                     return false;
522                 break;
523 
524             default:
525                 return false;
526         }
527         return true;
528     }
529 
ReadKeyFile_OnEnter(LightStateMachine::StateMachineContext & ctx)530     void DecryptionStateMachine::ReadKeyFile_OnEnter(LightStateMachine::StateMachineContext &ctx)
531     {
532         PacketMetadata &metadata = metadata_;
533 
534         if(metadata.key_file.empty())
535         {
536             result_ = EpadResult::KeyFileNotSpecified;
537             ctx.SetFailed(true);
538             return;
539         }
540 
541         key_file_session_.reset(new DecryptionSession());
542 
543         std::string empty_str;
544         EpadResult result = LoadKeyFromFile(metadata.key_file,
545                 encrypt_params_.libcurl_path ? *encrypt_params_.libcurl_path : empty_str,
546                 encrypt_params_.libcurl_parameters ? *encrypt_params_.libcurl_parameters : empty_str,
547                 key_file_session_->own_passphrase);
548 
549         if(result != EpadResult::Success)
550         {
551             result_ = result;
552             ctx.SetFailed(true);
553             return;
554         }
555 
556         if(!DecryptKeyFileContent(key_file_session_->own_passphrase,
557                     encrypt_params_.key_file_encrypt_params, key_file_session_->own_passphrase))
558         {
559             result_ = EpadResult::InvalidKeyFilePassphrase;
560             ctx.SetFailed(true);
561             return;
562         }
563 
564         if(!ValidateDecryptedKeyFile(key_file_session_->own_passphrase))
565         {
566             result_ = EpadResult::InvalidKeyFile;
567             ctx.SetFailed(true);
568             return;
569         }
570         result_ = EpadResult::Success;
571     }
572 
WADHead_CanEnter(LightStateMachine::StateMachineContext & ctx)573     bool DecryptionStateMachine::WADHead_CanEnter(LightStateMachine::StateMachineContext &ctx)
574     {
575         if(is_wad_head_finished_)
576             return false;
577 
578         if(buffer_.size() == 0 && pending_buffer_.size() == 0)
579             return false;
580 
581         switch(format_)
582         {
583             case Format::WAD:
584             case Format::NestedWAD:
585                 break;
586 
587             default:
588                 return false;
589         }
590         return true;
591     }
592 
WADHead_OnEnter(LightStateMachine::StateMachineContext & ctx)593     void DecryptionStateMachine::WADHead_OnEnter(LightStateMachine::StateMachineContext &ctx)
594     {
595         pending_buffer_.insert(pending_buffer_.end(), buffer_.begin(), buffer_.end());
596         buffer_.clear();
597 
598         InPacketStreamMemory stm_in(pending_buffer_.data(),
599                 pending_buffer_.data() + pending_buffer_.size());
600 
601         uint32_t payload_offset = 0;
602         uint32_t payload_size = 0;
603         std::string key_file;
604         EpadResult result = ParseWad(stm_in, key_file, payload_offset, payload_size);
605 
606         switch(result)
607         {
608             case EpadResult::Success:
609                 break;
610 
611             case EpadResult::InvalidOrIncompleteWadFile:
612                 if(in_.IsEOF())
613                 {
614                     result_ = result;
615                     ctx.SetFailed(true);
616                 }
617                 return;
618 
619             default:
620                 result_ = result;
621                 ctx.SetFailed(true);
622                 return;
623         }
624 
625         if(metadata_.key_file.empty())
626             metadata_.key_file = key_file;
627 
628         buffer_.swap(pending_buffer_);
629         buffer_.erase(buffer_.begin(), buffer_.begin() + payload_offset);
630         if(payload_size != 0 && payload_size < buffer_.size())
631         {
632             //This is 3.2.1 format, in which the key string and the dictionary goes after the payload.
633             //We need to trim the file
634             buffer_.erase(buffer_.begin() + payload_size, buffer_.end());
635         }
636         is_wad_head_finished_ = true;
637         result_ = EpadResult::Success;
638     }
639 
IsEncryptedEmptyString()640     bool DecryptionStateMachine::IsEncryptedEmptyString()
641     {
642         return in_.IsEOF() && filter_count_ == 1 && buffer_.empty() && pending_buffer_.empty()
643             && format_ == Format::GPGOrNestedWad;
644     }
645 }
646 
647