// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : AACS.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes AACS structures. // // --[ License ] -------------------------------------------------------------- // // This library is free software; you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation; either version 2.1 of the // License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, see . // // ---------------------------------------------------------------------------- // Copyright © 2011-2019 Natalia Portillo // ****************************************************************************/ using System; using System.Diagnostics.CodeAnalysis; using System.Text; namespace DiscImageChef.Decoders.SCSI.MMC { /// /// Information from the following standards: /// ANSI X3.304-1997 /// T10/1048-D revision 9.0 /// T10/1048-D revision 10a /// T10/1228-D revision 7.0c /// T10/1228-D revision 11a /// T10/1363-D revision 10g /// T10/1545-D revision 1d /// T10/1545-D revision 5 /// T10/1545-D revision 5a /// T10/1675-D revision 2c /// T10/1675-D revision 4 /// T10/1836-D revision 2g /// [SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "MemberCanBeInternal")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] [SuppressMessage("ReSharper", "NotAccessedField.Global")] public static class AACS { public struct AACSVolumeIdentifier { /// /// Bytes 0 to 1 /// Data length /// public ushort DataLength; /// /// Byte 2 /// Reserved /// public byte Reserved1; /// /// Byte 3 /// Reserved /// public byte Reserved2; /// /// Bytes 4 to end /// AACS volume identifier data /// public byte[] VolumeIdentifier; } public struct AACSMediaSerialNumber { /// /// Bytes 0 to 1 /// Data length /// public ushort DataLength; /// /// Byte 2 /// Reserved /// public byte Reserved1; /// /// Byte 3 /// Reserved /// public byte Reserved2; /// /// Bytes 4 to end /// AACS media serial number /// public byte[] MediaSerialNumber; } public struct AACSMediaIdentifier { /// /// Bytes 0 to 1 /// Data length /// public ushort DataLength; /// /// Byte 2 /// Reserved /// public byte Reserved1; /// /// Byte 3 /// Reserved /// public byte Reserved2; /// /// Bytes 4 to end /// AACS media identifier data /// public byte[] MediaIdentifier; } public struct AACSMediaKeyBlock { /// /// Bytes 0 to 1 /// Data length /// public ushort DataLength; /// /// Byte 2 /// Reserved /// public byte Reserved; /// /// Byte 3 /// Number of MKB packs available to transfer /// public byte TotalPacks; /// /// Bytes 4 to end /// AACS media key block packs /// public byte[] MediaKeyBlockPacks; } public struct AACSDataKeys { /// /// Bytes 0 to 1 /// Data length /// public ushort DataLength; /// /// Byte 2 /// Reserved /// public byte Reserved1; /// /// Byte 3 /// Reserved /// public byte Reserved2; /// /// Bytes 4 to end /// AACS data keys /// public byte[] DataKeys; } public struct AACSLBAExtentsResponse { /// /// Bytes 0 to 1 /// Data Length /// public ushort DataLength; /// /// Byte 2 /// Reserved /// public byte Reserved; /// /// Byte 3 /// Number of LBA extents the drive can store. /// if(MaxLBAExtents == 0 && DataLength > 2), 256 extents can be stored /// public byte MaxLBAExtents; /// /// Bytes 4 to end /// LBA Extents /// public AACSLBAExtent[] Extents; } public struct AACSLBAExtent { /// /// Bytes 0 to 7 /// Reserved /// public byte[] Reserved; /// /// Bytes 8 to 11 /// Start LBA of extent /// public uint StartLBA; /// /// Bytes 12 to 15 /// Extent length /// public uint LBACount; } public static AACSVolumeIdentifier? DecodeAACSVolumeIdentifier(byte[] AACSVIResponse) { if(AACSVIResponse == null) return null; AACSVolumeIdentifier decoded = new AACSVolumeIdentifier(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; decoded.VolumeIdentifier = new byte[AACSVIResponse.Length - 4]; decoded.DataLength = BigEndianBitConverter.ToUInt16(AACSVIResponse, 0); decoded.Reserved1 = AACSVIResponse[2]; decoded.Reserved2 = AACSVIResponse[3]; Array.Copy(AACSVIResponse, 4, decoded.VolumeIdentifier, 0, AACSVIResponse.Length - 4); return decoded; } public static string PrettifyAACSVolumeIdentifier(AACSVolumeIdentifier? AACSVIResponse) { if(AACSVIResponse == null) return null; AACSVolumeIdentifier response = AACSVIResponse.Value; StringBuilder sb = new StringBuilder(); #if DEBUG if(response.Reserved1 != 0) sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); if(response.Reserved2 != 0) sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); #endif sb.AppendFormat("AACS Volume Identifier in hex follows:"); sb.AppendLine(PrintHex.ByteArrayToHexArrayString(response.VolumeIdentifier, 80)); return sb.ToString(); } public static string PrettifyAACSVolumeIdentifier(byte[] AACSVIResponse) { AACSVolumeIdentifier? decoded = DecodeAACSVolumeIdentifier(AACSVIResponse); return PrettifyAACSVolumeIdentifier(decoded); } public static AACSMediaSerialNumber? DecodeAACSMediaSerialNumber(byte[] AACSMSNResponse) { if(AACSMSNResponse == null) return null; AACSMediaSerialNumber decoded = new AACSMediaSerialNumber(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; decoded.MediaSerialNumber = new byte[AACSMSNResponse.Length - 4]; decoded.DataLength = BigEndianBitConverter.ToUInt16(AACSMSNResponse, 0); decoded.Reserved1 = AACSMSNResponse[2]; decoded.Reserved2 = AACSMSNResponse[3]; Array.Copy(AACSMSNResponse, 4, decoded.MediaSerialNumber, 0, AACSMSNResponse.Length - 4); return decoded; } public static string PrettifyAACSMediaSerialNumber(AACSMediaSerialNumber? AACSMSNResponse) { if(AACSMSNResponse == null) return null; AACSMediaSerialNumber response = AACSMSNResponse.Value; StringBuilder sb = new StringBuilder(); #if DEBUG if(response.Reserved1 != 0) sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); if(response.Reserved2 != 0) sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); #endif sb.AppendFormat("AACS Media Serial Number in hex follows:"); sb.AppendLine(PrintHex.ByteArrayToHexArrayString(response.MediaSerialNumber, 80)); return sb.ToString(); } public static string PrettifyAACSMediaSerialNumber(byte[] AACSMSNResponse) { AACSMediaSerialNumber? decoded = DecodeAACSMediaSerialNumber(AACSMSNResponse); return PrettifyAACSMediaSerialNumber(decoded); } public static AACSMediaIdentifier? DecodeAACSMediaIdentifier(byte[] AACSMIResponse) { if(AACSMIResponse == null) return null; AACSMediaIdentifier decoded = new AACSMediaIdentifier(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; decoded.MediaIdentifier = new byte[AACSMIResponse.Length - 4]; decoded.DataLength = BigEndianBitConverter.ToUInt16(AACSMIResponse, 0); decoded.Reserved1 = AACSMIResponse[2]; decoded.Reserved2 = AACSMIResponse[3]; Array.Copy(AACSMIResponse, 4, decoded.MediaIdentifier, 0, AACSMIResponse.Length - 4); return decoded; } public static string PrettifyAACSMediaIdentifier(AACSMediaIdentifier? AACSMIResponse) { if(AACSMIResponse == null) return null; AACSMediaIdentifier response = AACSMIResponse.Value; StringBuilder sb = new StringBuilder(); #if DEBUG if(response.Reserved1 != 0) sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); if(response.Reserved2 != 0) sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); #endif sb.AppendFormat("AACS Media Identifier in hex follows:"); sb.AppendLine(PrintHex.ByteArrayToHexArrayString(response.MediaIdentifier, 80)); return sb.ToString(); } public static string PrettifyAACSMediaIdentifier(byte[] AACSMIResponse) { AACSMediaIdentifier? decoded = DecodeAACSMediaIdentifier(AACSMIResponse); return PrettifyAACSMediaIdentifier(decoded); } public static AACSMediaKeyBlock? DecodeAACSMediaKeyBlock(byte[] AACSMKBResponse) { if(AACSMKBResponse == null) return null; AACSMediaKeyBlock decoded = new AACSMediaKeyBlock(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; decoded.MediaKeyBlockPacks = new byte[AACSMKBResponse.Length - 4]; decoded.DataLength = BigEndianBitConverter.ToUInt16(AACSMKBResponse, 0); decoded.Reserved = AACSMKBResponse[2]; decoded.TotalPacks = AACSMKBResponse[3]; Array.Copy(AACSMKBResponse, 4, decoded.MediaKeyBlockPacks, 0, AACSMKBResponse.Length - 4); return decoded; } public static string PrettifyAACSMediaKeyBlock(AACSMediaKeyBlock? AACSMKBResponse) { if(AACSMKBResponse == null) return null; AACSMediaKeyBlock response = AACSMKBResponse.Value; StringBuilder sb = new StringBuilder(); #if DEBUG if(response.Reserved != 0) sb.AppendFormat("Reserved = 0x{0:X2}", response.Reserved).AppendLine(); #endif sb.AppendFormat("Total number of media key blocks available to transfer {0}", response.TotalPacks) .AppendLine(); sb.AppendFormat("AACS Media Key Blocks in hex follows:"); sb.AppendLine(PrintHex.ByteArrayToHexArrayString(response.MediaKeyBlockPacks, 80)); return sb.ToString(); } public static string PrettifyAACSMediaKeyBlock(byte[] AACSMKBResponse) { AACSMediaKeyBlock? decoded = DecodeAACSMediaKeyBlock(AACSMKBResponse); return PrettifyAACSMediaKeyBlock(decoded); } public static AACSDataKeys? DecodeAACSDataKeys(byte[] AACSDKResponse) { if(AACSDKResponse == null) return null; AACSDataKeys decoded = new AACSDataKeys(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; decoded.DataKeys = new byte[AACSDKResponse.Length - 4]; decoded.DataLength = BigEndianBitConverter.ToUInt16(AACSDKResponse, 0); decoded.Reserved1 = AACSDKResponse[2]; decoded.Reserved2 = AACSDKResponse[3]; Array.Copy(AACSDKResponse, 4, decoded.DataKeys, 0, AACSDKResponse.Length - 4); return decoded; } public static string PrettifyAACSDataKeys(AACSDataKeys? AACSDKResponse) { if(AACSDKResponse == null) return null; AACSDataKeys response = AACSDKResponse.Value; StringBuilder sb = new StringBuilder(); #if DEBUG if(response.Reserved1 != 0) sb.AppendFormat("Reserved1 = 0x{0:X2}", response.Reserved1).AppendLine(); if(response.Reserved2 != 0) sb.AppendFormat("Reserved2 = 0x{0:X2}", response.Reserved2).AppendLine(); #endif sb.AppendFormat("AACS Data Keys in hex follows:"); sb.AppendLine(PrintHex.ByteArrayToHexArrayString(response.DataKeys, 80)); return sb.ToString(); } public static string PrettifyAACSDataKeys(byte[] AACSDKResponse) { AACSDataKeys? decoded = DecodeAACSDataKeys(AACSDKResponse); return PrettifyAACSDataKeys(decoded); } public static AACSLBAExtentsResponse? DecodeAACSLBAExtents(byte[] AACSLBAExtsResponse) { if(AACSLBAExtsResponse == null) return null; AACSLBAExtentsResponse decoded = new AACSLBAExtentsResponse(); BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; decoded.DataLength = BigEndianBitConverter.ToUInt16(AACSLBAExtsResponse, 0); decoded.Reserved = AACSLBAExtsResponse[2]; decoded.MaxLBAExtents = AACSLBAExtsResponse[3]; if((AACSLBAExtsResponse.Length - 4) % 16 != 0) return decoded; decoded.Extents = new AACSLBAExtent[(AACSLBAExtsResponse.Length - 4) / 16]; for(int i = 0; i < (AACSLBAExtsResponse.Length - 4) / 16; i++) { decoded.Extents[i].Reserved = new byte[8]; Array.Copy(AACSLBAExtsResponse, 0 + i * 16 + 4, decoded.Extents[i].Reserved, 0, 8); decoded.Extents[i].StartLBA = BigEndianBitConverter.ToUInt32(AACSLBAExtsResponse, 8 + i * 16 + 4); decoded.Extents[i].LBACount = BigEndianBitConverter.ToUInt32(AACSLBAExtsResponse, 12 + i * 16 + 4); } return decoded; } public static string PrettifyAACSLBAExtents(AACSLBAExtentsResponse? AACSLBAExtsResponse) { if(AACSLBAExtsResponse == null) return null; AACSLBAExtentsResponse response = AACSLBAExtsResponse.Value; StringBuilder sb = new StringBuilder(); if(response.MaxLBAExtents == 0) sb.AppendLine(response.DataLength > 2 ? "Drive can store 256 LBA Extents" : "Drive cannot store LBA Extents"); else sb.AppendFormat("Drive can store {0} LBA Extents", response.MaxLBAExtents).AppendLine(); for(int i = 0; i < response.Extents.Length; i++) sb.AppendFormat("LBA Extent {0} starts at LBA {1} and goes for {2} sectors", i, response.Extents[i].StartLBA, response.Extents[i].LBACount); return sb.ToString(); } public static string PrettifyAACSLBAExtents(byte[] AACSLBAExtsResponse) { AACSLBAExtentsResponse? decoded = DecodeAACSLBAExtents(AACSLBAExtsResponse); return PrettifyAACSLBAExtents(decoded); } } }