private bool CryptXvcRegion(int regionIdx, bool encrypt) { if (XvcInfo.ContentID == null || !XvcInfo.IsAnyKeySet || regionIdx > XvcInfo.RegionCount || RegionHeaders == null || regionIdx >= RegionHeaders.Count) { return(false); } XvcRegionHeader header = RegionHeaders[regionIdx]; if (encrypt && header.KeyId == 0xFFFF) { header.KeyId = 0; } if (header.Length <= 0 || header.Offset <= 0 || header.KeyId == 0xFFFF || (header.KeyId + 1) > XvcInfo.KeyCount) { return(false); } byte[] key; GetXvcKey(header.KeyId, out key); if (key == null) { return(false); } return(CryptSection(encrypt, key, header.Id, header.Offset, header.Length)); }
private bool CryptXvcRegion(int regionIdx, bool encrypt) { if (XvcInfo.ContentID == null || !XvcInfo.IsAnyKeySet || regionIdx > XvcInfo.RegionCount || RegionHeaders == null || regionIdx >= RegionHeaders.Count) { return(false); } XvcRegionHeader header = RegionHeaders[regionIdx]; if (encrypt && header.KeyId == XvcConstants.XVC_KEY_NONE) { header.KeyId = 0; } if (header.Length <= 0 || header.Offset <= 0 || header.KeyId == XvcConstants.XVC_KEY_NONE || header.KeyId + 1 > XvcInfo.KeyCount) { return(false); } GetXvcKey(header.KeyId, out var key); if (key == null) { return(false); } return(CryptSectionXts(encrypt, key, (uint)header.Id, header.Offset, header.Length)); }
public bool RemoveHashTree() { if (!IsDataIntegrityEnabled) { return(true); } var hashTreeSize = (long)(HashTreeBlockCount * 0x1000); _io.Stream.Position = (long)HashTreeOffset; if (!_io.DeleteBytes(hashTreeSize)) { return(false); } Header.VolumeFlags = Header.VolumeFlags.ToggleFlag((uint)XvdVolumeFlags.DataIntegrityDisabled); for (int i = 0; i < Header.TopHashBlockHash.Length; i++) { Header.TopHashBlockHash[i] = 0; } if (IsXvcFile) { if (XvcInfo.InitialPlayOffset > HashTreeOffset) { XvcInfo.InitialPlayOffset -= (ulong)hashTreeSize; } if (XvcInfo.PreviewOffset > HashTreeOffset) { XvcInfo.PreviewOffset -= (ulong)hashTreeSize; } for (int i = 0; i < RegionHeaders.Count; i++) { var newHdr = new XvcRegionHeader(RegionHeaders[i]); ulong regionEnd = newHdr.Offset + newHdr.Length; if ((newHdr.Offset == HashTreeOffset || newHdr.Offset < HashTreeOffset) && HashTreeOffset < regionEnd) { newHdr.Length -= (ulong)hashTreeSize; } else if (newHdr.Offset > HashTreeOffset) { newHdr.Offset -= (ulong)hashTreeSize; } newHdr.RegionPDUID = 0; RegionHeaders[i] = newHdr; } } // todo: figure out update segments and fix them return(true); }
public bool AddHashTree() { if (IsDataIntegrityEnabled) { return(true); } var hashTreeSize = (long)(HashTreeBlockCount * 0x1000); _io.Stream.Position = (long)HashTreeOffset; if (!_io.AddBytes(hashTreeSize)) { return(false); } Header.VolumeFlags = Header.VolumeFlags.ToggleFlag((uint)XvdVolumeFlags.DataIntegrityDisabled); if (IsXvcFile) { if (XvcInfo.InitialPlayOffset > 0) { XvcInfo.InitialPlayOffset += (ulong)hashTreeSize; } if (XvcInfo.PreviewOffset > 0) { XvcInfo.PreviewOffset += (ulong)hashTreeSize; } foreach (XvcRegionHeader t in RegionHeaders) { XvcRegionHeader header = t; // have to make a new object pointer otherwise c# complains ulong regionEnd = header.Offset + header.Length; if ((header.Offset == HashTreeOffset || header.Offset < HashTreeOffset) && HashTreeOffset < regionEnd) { header.Length += (ulong)hashTreeSize; } else if (header.Offset > HashTreeOffset) { header.Offset += (ulong)hashTreeSize; } header.RegionPDUID = 0; } } CalculateDataOffsets(); return(Save()); // todo: figure out update segments and fix them //VerifyDataHashTree(true); //return CalculateHashTree(); }
public bool Decrypt() { if (!IsEncrypted) { return(true); } CryptHeaderCik(false); if (!CikIsDecrypted) { return(false); } bool success; if (IsXvcFile) { for (int i = 0; i < RegionHeaders.Count; i++) { XvcRegionHeader header = RegionHeaders[i]; if (header.Length <= 0 || header.Offset <= 0 || header.KeyId == XvcConstants.XVC_KEY_NONE || header.KeyId + 1 > XvcInfo.KeyCount) { continue; } if (!CryptXvcRegion(i, false)) { return(false); } } success = true; } else { // todo: check with more non-xvc xvds and see if they use any other headerId besides 0x1 success = CryptSectionXts(false, Header.KeyMaterial, 0x1, UserDataOffset, (ulong)_io.Stream.Length - UserDataOffset); } if (!success) { return(false); } Header.VolumeFlags ^= XvdVolumeFlags.EncryptionDisabled; Save(); return(true); }
/* 0x80 = END */ public XvcRegionHeader(XvcRegionHeader src) { Id = src.Id; KeyId = src.KeyId; Unknown1 = src.Unknown1; Flags = src.Flags; Unknown2 = src.Unknown2; Description = src.Description; Offset = src.Offset; Length = src.Length; RegionPDUID = src.RegionPDUID; Unknown3 = src.Unknown3; Unknown4 = src.Unknown4; Unknown5 = src.Unknown5; }
public int[] VerifyDataHashTree(bool rehash = false) { int dataBlockCount = (int)((ulong)_io.Stream.Length - UserDataOffset)/0x1000; var invalidBlocks = new List<int>(); for (int i = 0; i < dataBlockCount; i++) { ulong stackNum; var blockNum = CalculateHashBlockNumForBlockNum(Header.Unknown1_HashTableRelated, (ulong)i, 0, out stackNum); var hashEntryOffset = (blockNum*0x1000) + HashTreeOffset; hashEntryOffset += stackNum*0x18; _io.Stream.Position = (long)hashEntryOffset; byte[] oldhash = _io.Reader.ReadBytes(0x18); var dataToHashOffset = (((uint)i*0x1000) + UserDataOffset); _io.Stream.Position = (long)dataToHashOffset; byte[] data = _io.Reader.ReadBytes(0x1000); byte[] hash = SHA256.Create().ComputeHash(data); Array.Resize(ref hash, 0x18); bool writeIdx = false; // encrypted data uses 0x14 hashes with a block IDX added to the end to make the 0x18 hash var idxToWrite = (uint)i; if (IsEncrypted) { if (IsXvcFile) { var hdr = new XvcRegionHeader(); foreach (var region in RegionHeaders) { if (region.KeyId == 0xFFFF) continue; // skip unencrypted regions if (dataToHashOffset >= region.Offset && dataToHashOffset < (region.Offset + region.Length)) { writeIdx = true; hdr = region; break; } } if (hdr.Id != 0) { var regionOffset = dataToHashOffset - hdr.Offset; var regionBlockNo = (regionOffset + 0xFFF)/0x1000; idxToWrite = (uint) regionBlockNo; } } else { writeIdx = true; idxToWrite = (uint) i; } } if (writeIdx) { byte[] idxBytes = BitConverter.GetBytes(idxToWrite); Array.Copy(idxBytes, 0, hash, 0x14, 4); } if (hash.IsEqualTo(oldhash)) continue; invalidBlocks.Add(i); if (!rehash) continue; _io.Stream.Position = (long)hashEntryOffset; _io.Writer.Write(hash); } return invalidBlocks.ToArray(); }
public bool RemoveHashTree() { if (!IsDataIntegrityEnabled) return true; var hashTreeSize = (long)(HashTreeBlockCount * 0x1000); _io.Stream.Position = (long)HashTreeOffset; if (!_io.DeleteBytes(hashTreeSize)) return false; Header.VolumeFlags = Header.VolumeFlags.ToggleFlag((uint)XvdVolumeFlags.DataIntegrityDisabled); for (int i = 0; i < Header.TopHashBlockHash.Length; i++) Header.TopHashBlockHash[i] = 0; if (IsXvcFile) { if (XvcInfo.InitialPlayOffset > HashTreeOffset) XvcInfo.InitialPlayOffset -= (ulong) hashTreeSize; if (XvcInfo.PreviewOffset > HashTreeOffset) XvcInfo.PreviewOffset -= (ulong) hashTreeSize; for(int i = 0; i < RegionHeaders.Count; i++) { var newHdr = new XvcRegionHeader(RegionHeaders[i]); ulong regionEnd = newHdr.Offset + newHdr.Length; if ((newHdr.Offset == HashTreeOffset || newHdr.Offset < HashTreeOffset) && HashTreeOffset < regionEnd) newHdr.Length -= (ulong)hashTreeSize; else if (newHdr.Offset > HashTreeOffset) newHdr.Offset -= (ulong)hashTreeSize; newHdr.RegionPDUID = 0; RegionHeaders[i] = newHdr; } } // todo: figure out update segments and fix them return true; }
public int[] VerifyDataHashTree(bool rehash = false) { int dataBlockCount = (int)((ulong)_io.Stream.Length - UserDataOffset) / 0x1000; var invalidBlocks = new List <int>(); for (int i = 0; i < dataBlockCount; i++) { ulong stackNum; var blockNum = CalculateHashBlockNumForBlockNum(Header.Unknown1_HashTableRelated, (ulong)i, 0, out stackNum); var hashEntryOffset = (blockNum * 0x1000) + HashTreeOffset; hashEntryOffset += stackNum * 0x18; _io.Stream.Position = (long)hashEntryOffset; byte[] oldhash = _io.Reader.ReadBytes(0x18); var dataToHashOffset = (((uint)i * 0x1000) + UserDataOffset); _io.Stream.Position = (long)dataToHashOffset; byte[] data = _io.Reader.ReadBytes(0x1000); byte[] hash = SHA256.Create().ComputeHash(data); Array.Resize(ref hash, 0x18); bool writeIdx = false; // encrypted data uses 0x14 hashes with a block IDX added to the end to make the 0x18 hash var idxToWrite = (uint)i; if (IsEncrypted) { if (IsXvcFile) { var hdr = new XvcRegionHeader(); foreach (var region in RegionHeaders) { if (region.KeyId == 0xFFFF) { continue; // skip unencrypted regions } if (dataToHashOffset >= region.Offset && dataToHashOffset < (region.Offset + region.Length)) { writeIdx = true; hdr = region; break; } } if (hdr.Id != 0) { var regionOffset = dataToHashOffset - hdr.Offset; var regionBlockNo = (regionOffset + 0xFFF) / 0x1000; idxToWrite = (uint)regionBlockNo; } } else { writeIdx = true; idxToWrite = (uint)i; } } if (writeIdx) { byte[] idxBytes = BitConverter.GetBytes(idxToWrite); Array.Copy(idxBytes, 0, hash, 0x14, 4); } if (hash.IsEqualTo(oldhash)) { continue; } invalidBlocks.Add(i); if (!rehash) { continue; } _io.Stream.Position = (long)hashEntryOffset; _io.Writer.Write(hash); } return(invalidBlocks.ToArray()); }
public bool Encrypt(int cikKeyId = 0) { if (IsEncrypted) { return(true); } bool success; if (!IsXvcFile) { if (Header.EncryptedCIK.IsArrayEmpty()) { // generate a new CIK if there's none specified var rng = new Random(); Header.EncryptedCIK = new byte[0x20]; rng.NextBytes(Header.EncryptedCIK); } // todo: check with more non-xvc xvds and see if they use any other headerId besides 0x1 success = CryptSection(true, Header.EncryptedCIK, 0x1, UserDataOffset, (ulong)_io.Stream.Length - UserDataOffset); } else { if (cikKeyId >= 0) // if cikKeyId isn't -1 set the XvcInfo key GUID to one we know { var keyGuids = CikKeys.Keys.ToList(); if (cikKeyId < 0 || cikKeyId >= keyGuids.Count) { return(false); } XvcInfo.EncryptionKeyIds[0].KeyId = keyGuids[cikKeyId].ToByteArray(); } for (int i = 0; i < RegionHeaders.Count; i++) { XvcRegionHeader header = RegionHeaders[i]; if (header.Length <= 0 || header.Offset <= 0 || ((header.KeyId + 1) > XvcInfo.KeyCount && header.KeyId != 0xFFFF)) { continue; } if (header.Id == 0x40000005 || header.Id == 0x40000004 || header.Id == 0x40000001) { continue; // skip XVD header / EXVD / XVC info } if (!CryptXvcRegion(i, true)) { return(false); } } success = true; } if (!success) { return(false); } CryptHeaderCik(true); Header.VolumeFlags = Header.VolumeFlags.ToggleFlag((uint)XvdVolumeFlags.EncryptionDisabled); // seems the readonly flag gets set when encrypting if (!Header.VolumeFlags.IsFlagSet((uint)XvdVolumeFlags.ReadOnly)) { Header.VolumeFlags = Header.VolumeFlags.ToggleFlag((uint)XvdVolumeFlags.ReadOnly); } Save(); return(true); }