public static ReadOnlyMemory <byte> DecryptSegmentData(byte[] encryptedSegmentData, HlsMediaSegment segment, byte[] key, byte[] iv) { AesEngine engine = new AesEngine(); // TODO: See if this +2 can be made static. CbcBlockCipher blockCipher = new CbcBlockCipher(engine); PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding()); KeyParameter keyParam = new KeyParameter(key); ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParam, iv, 0, 16); cipher.Init(false, keyParamWithIv); byte[] decryptedSegmentData = new byte[cipher.GetOutputSize(encryptedSegmentData.Length)]; int length = cipher.ProcessBytes(encryptedSegmentData, decryptedSegmentData, 0); length += cipher.DoFinal(decryptedSegmentData, length); return(new ReadOnlyMemory <byte>(decryptedSegmentData, 0, length)); }
public async Task <ReadOnlyMemory <byte> > GetNextSegmentAsync() { if (_nextSegment >= _mediaPlaylist.MediaSegments.Count) { throw new Exception(); } HlsMediaSegment segment = _mediaPlaylist.MediaSegments[_nextSegment]; if (segment.Key != null && segment.Key.Method != HlsKeyMethod.None && !_keyCache.ContainsKey(segment.Key.Uri)) { byte[] keyData; if (OnKeyFetch != null) { keyData = await OnKeyFetch(segment.Key); } else { keyData = await HttpClient.GetByteArrayAsync(segment.Key.Uri); } if (keyData.Length != 16) { throw new Exception($"Key length not 16 ({keyData.Length})."); } _keyCache[segment.Key.Uri] = keyData; } byte[] segmentBytes; if (OnSegmentFetch != null) { segmentBytes = await OnSegmentFetch(segment); } else { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, segment.Uri.ToString()); if (segment.ByteRange != null) { string[] rangeParts = segment.ByteRange.Split('@'); int len = Int32.Parse(rangeParts[0]), start = Int32.Parse(rangeParts[1]); request.Headers.Range = new RangeHeaderValue(start, start + len - 1); } HttpResponseMessage response = await HttpClient.SendAsync(request); segmentBytes = await response.Content.ReadAsByteArrayAsync(); } ReadOnlyMemory <byte> segmentData = segmentBytes; if (segment.Key != null && segment.Key.Method == HlsKeyMethod.Aes128) { byte[] iv = new byte[16]; if (segment.Key.KeyFormat == "identity") { Buffer.BlockCopy(BitConverter.GetBytes(segment.SequenceNumber).Reverse().ToArray(), 0, iv, 12, 4); } segmentData = DecryptSegmentData(segmentBytes, segment, _keyCache[segment.Key.Uri], iv); } _nextSegment++; return(segmentData); }
private void HandleMediaPlaylistLine(string line) { if (_isMasterPlaylist.HasValue && _isMasterPlaylist.Value) { throw new HlsException("Unexpected tag in media playlist."); } if (!_isMasterPlaylist.HasValue) { _isMasterPlaylist = false; _mediaPlaylist = new HlsMediaPlaylist { Version = _version, IsIndependentSegments = _isIndependentSegments, Start = _start }; } if (line[0] != '#') { // A URI line for a media segment should be the only case when this happens. _mediaSegment.Uri = new Uri(_baseUri, line); _mediaSegment = null; return; } int firstColonIndex = line.IndexOf(':'); string tag = (firstColonIndex > -1 ? line.Substring(1, firstColonIndex - 1) : line.Substring(1)); string value = (firstColonIndex > -1 ? line.Substring(firstColonIndex + 1, line.Length - firstColonIndex - 1) : null); switch (tag) { // Media segment tags. case "EXTINF": int commaIndex = value.IndexOf(','); _mediaSegment = new HlsMediaSegment { SequenceNumber = _mediaPlaylist.MediaSequence + _mediaPlaylist._mediaSegments.Count, Duration = Convert.ToSingle((commaIndex != -1 ? value.Substring(0, commaIndex) : value)), Title = (commaIndex != -1 ? value.Substring(commaIndex + 1) : null), Key = _mediaSegmentKey }; _mediaPlaylist._mediaSegments.Add(_mediaSegment); break; case "EXT-X-BYTERANGE": _mediaSegment.ByteRange = value; break; case "EXT-X-DISCONTINUITY": _mediaSegment.HasDiscontinuityAfter = true; break; case "EXT-X-KEY": IReadOnlyDictionary <string, string> keyAttributes = AttributeList.Parse(value); _mediaSegmentKey = new HlsMediaSegmentKey { Method = AttributeValueUtils.ParseEnum <HlsKeyMethod>(keyAttributes["METHOD"]), Uri = AttributeValueUtils.GetUriOrNull(keyAttributes, _baseUri, "URI"), KeyFormat = (AttributeValueUtils.GetStringOrNull(keyAttributes, "KEYFORMAT") ?? "identity"), }; break; case "EXT-X-MAP": break; case "EXT-X-PROGRAM-DATE-TIME": _mediaSegment.ProgramDateTime = value; break; case "EXT-X-ALLOW-CACHE": break; case "EXT-X-DATERANGE": break; // Media playlist tags. case "EXT-X-TARGETDURATION": _mediaPlaylist.TargetDuration = Convert.ToInt32(value); break; case "EXT-X-MEDIA-SEQUENCE": _mediaPlaylist.MediaSequence = Convert.ToInt32(value); break; case "EXT-X-DISCONTINUITY-SEQUENCE": _mediaPlaylist.DiscontinuitySequence = Convert.ToInt32(value); break; case "EXT-X-ENDLIST": _mediaSegment = null; HasEndlist = true; break; case "EXT-X-PLAYLIST-TYPE": _mediaPlaylist.PlaylistType = value; break; case "EXT-X-I-FRAMES-ONLY": _mediaPlaylist.IsIFramesOnly = true; break; } }