private void ValidateTable(BitSplitter bs) { int pointer = (int)bs.GetInteger(8); if (pointer != 0) { throw new DemuxException("Non-zero pointers are not currently supported"); } int table_id = (int)bs.GetInteger(8); if (table_id != 0x02) { throw new DemuxException("Wrong table ID for PMT"); } SectionSyntax = bs.GetFlag(); if (!SectionSyntax) { throw new DemuxException("Invalid PMT: incorrect section syntax"); } bool zero = bs.GetFlag(); if (zero) { throw new DemuxException("Invalid PMT: zero bit wasn't zero"); } }
/// <summary> /// Digest a PES payload into structured table. /// Does not handle split-payloads -- agregate payloads before calling /// </summary> public PES(byte[] RawPayload) { BitSplitter bs = new BitSplitter(RawPayload); int start_code = (int)bs.GetInteger(24); if (start_code != 1) { throw new DemuxException("Invalid PES: start code prefix missing"); } PTS = DTS = -1; StreamId = (int)bs.GetInteger(8); PacketLength = (int)bs.GetInteger(16); // Both these methods set 'FrameData' if (SpecialStream(StreamId)) { ReadSpecialForm(bs); } else { DecodeElementaryStream(bs); } }
private void ReadSpecialForm(BitSplitter bs) { byte[] data = bs.RemainingBytes(); if (data.Length < PacketLength) { throw new DemuxException("Invalid PES: packet shorter than described"); } MemoryStream ms = new MemoryStream(data, 0, PacketLength); FrameData = ms.ToArray(); }
private void ReadTransportHeader(BitSplitter bs) { bs.SkipToNextByte(); // Sync byte Error = bs.GetFlag(); StartIndicator = bs.GetFlag(); HighPriority = bs.GetFlag(); PID = (int)bs.GetInteger(13); ScrambleCode = (int)bs.GetInteger(2); HasAdaptionField = bs.GetFlag(); HasPayload = bs.GetFlag(); Counter = (int)bs.GetInteger(4); }
private void CheckPayloadType(BitSplitter bs) { if (payload.Length > 4) { if (payload[0] == 0 && payload[1] == 0 && payload[2] == 0x01) { PayloadIs_PES = true; } } if (!PayloadIs_PES && HasPayload && StartIndicator) { if (payload[0] != 0x00) { throw new DemuxException("Non-zero pointer values are not yet supported!"); } TableId = payload[1]; } }
private void CheckAdaptionField(BitSplitter bs) { if (HasAdaptionField) { int adaption_end = (int)bs.GetInteger(8); adaption_end += bs.ByteOffset; Discont = bs.GetFlag(); KeyFrame = bs.GetFlag(); ES_Prio = bs.GetFlag(); HasPCR = bs.GetFlag(); HasOPCR = bs.GetFlag(); HasSplice = bs.GetFlag(); PrivateFlag = bs.GetFlag(); AdapExtFlag = bs.GetFlag(); if (bs.BitOffset != 0) { throw new Exception("bit align problem"); } if (HasPCR) { PCR = (long)bs.GetInteger(33); bs.SkipBits(15); // throw away useless sync stuff. } if (HasOPCR) { bs.SkipBits(48); // throw away useless "old" timecode } if (HasSplice) { bs.SkipBits(8); // throw away splice counter } if (PrivateFlag) { int priv_len = (int)bs.GetInteger(8); bs.SkipBytes(priv_len); // skip private data } // ignore the rest of the adaption field (it's mostly to support stuff we ignore) int skip_len = adaption_end - bs.ByteOffset; bs.SkipBytes(skip_len); } }
/// <summary> /// Decode the bizzare PTS format /// </summary> private void ReadPTS(BitSplitter bs) { int marker = (int)bs.GetInteger(4); if (marker != 2) { throw new DemuxException("Invalid PES: PTS marker incorrect"); } long part_1 = (long)bs.GetInteger(3); bs.SkipBits(1); long part_2 = (long)bs.GetInteger(15); bs.SkipBits(1); long part_3 = (long)bs.GetInteger(15); bs.SkipBits(1); unchecked { // allow overflow so we can catch it later: PTS = (UInt32)(part_3 + (part_2 << 15) + (part_1 << 30)); } }
/// <summary> /// Reads the long list of flags in the default PES header. /// </summary> private void ReadFlags(BitSplitter bs) { ScrambleControl = (int)bs.GetInteger(2); HighPriority = bs.GetFlag(); HasAlignment = bs.GetFlag(); IsCopyright = bs.GetFlag(); IsOriginal = bs.GetFlag(); HasPTS = bs.GetFlag(); HasDTS = bs.GetFlag(); if (HasDTS && !HasPTS) { throw new DemuxException("Invalid PES: DTS without PTS is not allowed"); } HasESCR = bs.GetFlag(); HasEsRate = bs.GetFlag(); UsesTrickMode = bs.GetFlag(); MoreCopyright = bs.GetFlag(); HasPesCRC = bs.GetFlag(); HasPesExtension = bs.GetFlag(); }
/// <summary>Digest data into structured packet</summary> public Packet(byte[] RawPacket) { if (RawPacket[0] != 0x47) { throw new DemuxException("Sync byte missing"); } PCR = -1; TableId = -1; PayloadIs_PES = false; BitSplitter bs = new BitSplitter(RawPacket); ReadTransportHeader(bs); if (PID == 0x1FFF) { return; // null packet } CheckAdaptionField(bs); payload = bs.RemainingBytes(); CheckPayloadType(bs); }
private int SectionNumber, LastSection; // should always be zero /// <summary> /// Digest a packet payload into structured table. Payload should be from the pointer field onward. /// Does not yet handle multi-packet tables /// </summary> public PAT(byte[] RawPayload) { Map = new Dictionary <int, int>(); BitSplitter bs = new BitSplitter(RawPayload); ValidateTable(bs); bs.SkipBits(2); // reserved; SectionLength = (int)bs.GetInteger(12); TransportID = (int)bs.GetInteger(16); bs.SkipBits(2); // reserved Version = (int)bs.GetInteger(5); IsCurrent = bs.GetFlag(); SectionNumber = (int)bs.GetInteger(8); LastSection = (int)bs.GetInteger(8); int bits_left = (SectionLength - 5) - 4; // remaining length in bytes, excluding CRC int items = bits_left / 4; for (int i = 0; i < items; i++) { int prog = (int)bs.GetInteger(16); bs.SkipBits(3); int pid = (int)bs.GetInteger(13); if (!Map.ContainsKey(prog)) { Map.Add(prog, pid); } else { throw new DemuxException("Invalid PAT: program number specified more than once (" + prog + ")"); } } // Ignoring CRC. }
private void ReadExtendedHeader(BitSplitter bs) { // not yet implemented }
private void DecodeElementaryStream(BitSplitter bs) { int marker = (int)bs.GetInteger(2); if (marker != 2) { throw new DemuxException("Invalid PES: first marker missing"); } ReadFlags(bs); if (bs.BitOffset != 0) { throw new DemuxException("Alignment problem in PES (internal)"); } HeaderLength = (int)bs.GetInteger(8); int head_start = bs.ByteOffset; if (HasPTS && HasDTS) { ReadDTS_PTS(bs); } else if (HasPTS) { ReadPTS(bs); } if (HasESCR) { bs.SkipBytes(6); // not currently used. } if (HasEsRate) { bs.SkipBits(24); // not currently used. } if (UsesTrickMode) { bs.SkipBytes(1); // ignored } if (MoreCopyright) { bs.SkipBytes(1); // ignored } if (HasPesCRC) { bs.SkipBytes(2); // ignored } if (HasPesExtension) { ReadExtendedHeader(bs); } // skip anything that's left int head_end = bs.ByteOffset; int to_skip = HeaderLength - (head_end - head_start); if (to_skip < 0) { throw new DemuxException("Invalid PES: declared header length did not match measured length"); } bs.SkipBytes(to_skip); // Now, the remaining bytes are data and padding int data_length = PacketLength - (HeaderLength + to_skip); if (data_length > 3) { data_length -= 3; // no idea where the '3' is coming from... } byte[] data = bs.RemainingBytes(); if (PacketLength == 0) // video is allowed to not specify { data_length = data.Length; } #if DEBUG if (data.Length < data_length) { throw new DemuxException("Invalid PES: packet shorter than described"); } if (data_length < 0) { throw new DemuxException("Invalid PES: Negative packet length"); } #else if (data.Length < data_length || data_length < 0) { data_length = 0; } #endif MemoryStream ms = new MemoryStream(data, 0, data_length); FrameData = ms.ToArray(); }
/// <summary> /// Digest a packet payload into structured table. Payload should be from the pointer field onward. /// Does not yet handle multi-packet tables /// </summary> public PMT(byte[] RawPayload) { BitSplitter bs = new BitSplitter(RawPayload); Map = new Dictionary <int, int>(); ReverseMap = new Dictionary <StreamType, int>(); ValidateTable(bs); bs.SkipBits(2); SectionLength = (int)bs.GetInteger(12); // total length after this, in bytes; includes 4 byte CRC. ProgramNumber = (int)bs.GetInteger(16); bs.SkipBits(2); Version = (int)bs.GetInteger(5); IsCurrent = bs.GetFlag(); SectionNumber = (int)bs.GetInteger(8); LastSection = (int)bs.GetInteger(8); bs.SkipBits(3); PCR_PID = (int)bs.GetInteger(13); // Either the PID of a channel timecode stream, or 0x1FFF for none. bs.SkipBits(4); ProgInfoLength = (int)bs.GetInteger(12); // number of bytes of descriptors. if (bs.BitOffset != 0) { throw new DemuxException("Byte alignment error (internal)"); } bs.SkipBytes(ProgInfoLength); // ignore descriptors. int info_bytes = (SectionLength - ProgInfoLength) - 13; // bytes of descriptor. while (info_bytes > 0) // descriptions can be variable length { int stream_type = (int)bs.GetInteger(8); bs.SkipBits(3); int pid = (int)bs.GetInteger(13); bs.SkipBits(4); if (!Map.ContainsKey(pid)) { Map.Add(pid, stream_type); // more complete map of pid types } else { throw new DemuxException("Invalid PMT: PID specified more than once"); } StreamType st = DecodeStreamType(stream_type); if (!ReverseMap.ContainsKey(st)) { ReverseMap.Add(st, pid); // store first pid of each type } int es_info_length = (int)bs.GetInteger(12); bs.SkipBytes(es_info_length); info_bytes -= 5 + es_info_length; } if (bs.BitOffset != 0) { throw new DemuxException("Invalid PMT: program info length didn't match data"); } // ignoring CRC. }
/// <summary> /// Двойное шифрование по алгоритму Rijndael-256 /// </summary> /// <param name="inputStream">Входной поток.</param> /// <param name="key1">Ключ для первого прохода шифрования.</param> /// <param name="key2">Ключ для второго прохода шифрования.</param> /// <param name="outputStream">Выходной поток.</param> /// <param name="encryptionMode">Используется шифрование?</param> /// <param name="iterations">Количество итераций хеширования пароля.</param> public void DoubleRijndael(Stream inputStream, byte[] key1, byte[] key2, Stream outputStream, bool encryptionMode, int iterations = 1) { if(!inputStream.CanSeek) { throw new Exception("Cryforce::DoubleRijndael() ==> Input stream can't seek!"); } var streamCryptoWrappers = new[] {new StreamCryptoWrapper(), new StreamCryptoWrapper()}; streamCryptoWrappers[0].Initialize(encryptionMode ? key1 : key2, iterations); // При шифровании прямой порядок паролей... streamCryptoWrappers[1].Initialize(encryptionMode ? key2 : key1, iterations); // ...а при расшифровке - обратный // Генерируем 10 случайных имен файлов: два для целей временного хранения данных в пределах данного метода // и 8 штук для битсплиттера (генерируем их совместно, чтобы избежать конфликтов) string[] tempFilenamesAll = WorkInTempDir ? CryforceUtilities.GetTempFilenames(10) : CryforceUtilities.GetRandomFilenames(10, 8, RndSeed).Select(item => item + ".jpg").ToArray(); var tempFilenames = new string[2]; var tempFilenamesToBitSplitter = new string[8]; Array.Copy(tempFilenamesAll, 0, tempFilenames, 0, 2); Array.Copy(tempFilenamesAll, 2, tempFilenamesToBitSplitter, 0, 8); Stream[] randomFilenameStreams = tempFilenames.Select(item => CryforceUtilities.PrepareOutputStream(ProgressChanged, item, BufferSizePerStream, ZeroOut, WorkInMemory, RndSeed)).ToArray(); ////////////////////////////////////// // Шифрование первого уровня (Level0) ////////////////////////////////////// Stream inputStreamAtLevel0 = encryptionMode ? inputStream : streamCryptoWrappers[0].WrapStream(inputStream, false); Stream outputStreamAtLevel0 = encryptionMode ? streamCryptoWrappers[0].WrapStream(randomFilenameStreams[0], true) : randomFilenameStreams[0]; inputStreamAtLevel0.SafeSeekBegin(); outputStreamAtLevel0.SafeSeekBegin(); // Процесс шифрования/расшифровки происходит прозрачно, во время чтения из зашифрованного потока или записи в зашифрованный // Размер буфера при копировании выбираем таким, чтобы обеспечить вывод каждого процента long dataSize = inputStream.Length; var bufferSize = (int)(dataSize / 100); CryforceUtilities.StreamCopy(ProgressChanged, inputStreamAtLevel0, outputStreamAtLevel0, dataSize, bufferSize); if(outputStreamAtLevel0 is CryptoStream) { ((CryptoStream)outputStreamAtLevel0).FlushFinalBlock(); } outputStreamAtLevel0.Flush(); // Если выходной поток первого уровня является криптографической оберткой над другим потоком - // нужно получить базовый поток для дальнейшей работы if(outputStreamAtLevel0 is CryptoStream) { outputStreamAtLevel0 = randomFilenameStreams[0]; } inputStreamAtLevel0.SafeSeekBegin(); outputStreamAtLevel0.SafeSeekBegin(); if(ProgressChanged != null) { ProgressChanged(null, new EventArgsGeneric<ProgressChangedArg>(new ProgressChangedArg("Rijndael-256 (1/2)", 100))); } //////////////////////////////////////////////////////// // Перестановка битов посредством битсплиттера (Level1) //////////////////////////////////////////////////////// // Выходной поток первого уровня обработки является входным потоком для второго Stream inputStreamAtLevel1 = outputStreamAtLevel0; // Т.к. результат работы битсплиттера не является конечным - работаем с временным потоком Stream outputStreamAtLevel1 = randomFilenameStreams[1]; inputStreamAtLevel1.SafeSeekBegin(); outputStreamAtLevel1.SafeSeekBegin(); var bitSplitter = new BitSplitter(tempFilenamesToBitSplitter, key1, key2, WorkInMemory); bitSplitter.RndSeed = RndSeed; // Некритичный параметр, но проброска значения желательна bitSplitter.ProgressChanged += ProgressChanged; if(encryptionMode) { bitSplitter.SplitToBitstream(inputStreamAtLevel1, outputStreamAtLevel1); } else { bitSplitter.UnsplitFromBitstream(inputStreamAtLevel1, outputStreamAtLevel1); } if(ProgressChanged != null) { ProgressChanged(null, new EventArgsGeneric<ProgressChangedArg>(new ProgressChangedArg("BitSplitter", 100))); } bitSplitter.ClearAndClose(); inputStreamAtLevel1.SafeSeekBegin(); outputStreamAtLevel1.SafeSeekBegin(); ////////////////////////////////////// // Шифрование второго уровня (Level2) ////////////////////////////////////// Stream inputStreamAtLevel2 = encryptionMode ? outputStreamAtLevel1 : streamCryptoWrappers[1].WrapStream(outputStreamAtLevel1, false); Stream outputStreamAtLevel2 = encryptionMode ? streamCryptoWrappers[1].WrapStream(outputStream, true) : outputStream; inputStreamAtLevel2.SafeSeekBegin(); outputStreamAtLevel2.SafeSeekBegin(); // Процесс шифрования/расшифровки происходит прозрачно, во время чтения из зашифрованного потока или записи в зашифрованный // Размер буфера при копировании выбираем таким, чтобы обеспечить вывод каждого процента dataSize = outputStreamAtLevel1.Length; bufferSize = (int)(dataSize / 100); CryforceUtilities.StreamCopy(ProgressChanged, inputStreamAtLevel2, outputStreamAtLevel2, dataSize, bufferSize); if(outputStreamAtLevel2 is CryptoStream) { ((CryptoStream)outputStreamAtLevel2).FlushFinalBlock(); } outputStreamAtLevel2.Flush(); inputStreamAtLevel2.SafeSeekBegin(); outputStreamAtLevel2.SafeSeekBegin(); if(ProgressChanged != null) { ProgressChanged(null, new EventArgsGeneric<ProgressChangedArg>(new ProgressChangedArg("Rijndael-256 (2/2)", 100))); } // Уничтожаем данные временных потоков foreach(Stream randomFilenameStream in randomFilenameStreams) { CryforceUtilities.WipeStream(ProgressChanged, randomFilenameStream, BufferSizePerStream, 0, randomFilenameStream.Length, ZeroOut, RndSeed); randomFilenameStream.Flush(); randomFilenameStream.Close(); } // Производим удаление носителей foreach(string tempFilename in tempFilenames) { if(File.Exists(tempFilename)) { File.SetAttributes(tempFilename, FileAttributes.Normal); File.Delete(tempFilename); } } // Закрываем все потоки, которые не являлись синонимами входа или выхода inputStreamAtLevel1.Close(); inputStreamAtLevel2.Close(); outputStreamAtLevel0.Close(); outputStreamAtLevel1.Close(); }