Exemplo n.º 1
0
        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));
        }
Exemplo n.º 2
0
        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));
        }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
        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();
        }
Exemplo n.º 5
0
        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);
        }
Exemplo n.º 6
0
        /* 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;
        }
Exemplo n.º 7
0
        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();
        }
Exemplo n.º 8
0
        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;
        }
Exemplo n.º 9
0
        /* 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;
        }
Exemplo n.º 10
0
        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());
        }
Exemplo n.º 11
0
        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);
        }