From 0c7445be6711f19a2af32499497881db0f99e498 Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Tue, 3 Dec 2024 16:08:48 +0000 Subject: [PATCH 1/5] Remove variable collision in NewCore() --- pkg/core/dev.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/core/dev.go b/pkg/core/dev.go index 7fcec3d5..cf6a3d1e 100644 --- a/pkg/core/dev.go +++ b/pkg/core/dev.go @@ -19,16 +19,16 @@ type Core struct { } func NewCore(device string) (*Core, error) { - drive, err := drive.Open(device) + d, err := drive.Open(device) if err != nil { return nil, fmt.Errorf("open device %s failed: %v", device, err) } - ident, err := drive.Identify() + ident, err := d.Identify() if err != nil { return nil, fmt.Errorf("identify device %s failed: %v", device, err) } c := &Core{ - DriveIntf: drive, + DriveIntf: d, DiskInfo: DiskInfo{ Identity: ident, Level0Discovery: &Level0Discovery{}, From 7b40bb597dc42a16dbdb99c1a93ac09f8b4a3f54 Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Tue, 3 Dec 2024 16:08:48 +0000 Subject: [PATCH 2/5] Add support for parsing Geometry,SingleUser,NamespaceLocking,ShadowMBRForMultipleNamespaces features --- pkg/core/dev.go | 49 +++++++------- pkg/core/feature/feature.go | 127 ++++++++++++++++++++++++++++-------- 2 files changed, 125 insertions(+), 51 deletions(-) diff --git a/pkg/core/dev.go b/pkg/core/dev.go index cf6a3d1e..f3053543 100644 --- a/pkg/core/dev.go +++ b/pkg/core/dev.go @@ -49,29 +49,30 @@ type DiskInfo struct { // Level0Discovery structure as described in TCG Storage Architecture Core Spec v2.01 rev 1.00 // (missing data length field, which is only required for parsing) type Level0Discovery struct { - MajorVersion int - MinorVersion int - Vendor [32]byte - TPer *feature.TPer - Locking *feature.Locking - Geometry *feature.Geometry - SecureMsg *feature.SecureMsg - Enterprise *feature.Enterprise - OpalV1 *feature.OpalV1 - SingleUser *feature.SingleUser - DataStore *feature.DataStore - OpalV2 *feature.OpalV2 - Opalite *feature.Opalite - PyriteV1 *feature.PyriteV1 - PyriteV2 *feature.PyriteV2 - RubyV1 *feature.RubyV1 - LockingLBA *feature.LockingLBA - BlockSID *feature.BlockSID - NamespaceLocking *feature.NamespaceLocking - DataRemoval *feature.DataRemoval - NamespaceGeometry *feature.NamespaceGeometry - SeagatePorts *feature.SeagatePorts - UnknownFeatures []uint16 + MajorVersion int + MinorVersion int + Vendor [32]byte + TPer *feature.TPer + Locking *feature.Locking + Geometry *feature.Geometry + SecureMsg *feature.SecureMsg + Enterprise *feature.Enterprise + OpalV1 *feature.OpalV1 + SingleUser *feature.SingleUser + DataStore *feature.DataStore + OpalV2 *feature.OpalV2 + Opalite *feature.Opalite + PyriteV1 *feature.PyriteV1 + PyriteV2 *feature.PyriteV2 + RubyV1 *feature.RubyV1 + LockingLBA *feature.LockingLBA + BlockSID *feature.BlockSID + NamespaceLocking *feature.NamespaceLocking + DataRemoval *feature.DataRemoval + NamespaceGeometry *feature.NamespaceGeometry + ShadowMBRForMultipleNamespaces *feature.ShadowMBRForMultipleNamespaces + SeagatePorts *feature.SeagatePorts + UnknownFeatures []uint16 } // Perform a Level 0 SSC Discovery. @@ -151,6 +152,8 @@ func (d *Core) Discovery0() error { d0.DataRemoval, err = feature.ReadDataRemovalFeature(frdr) case feature.CodeNamespaceGeometry: d0.NamespaceGeometry, err = feature.ReadNamespaceGeometryFeature(frdr) + case feature.CodeShadowMBRForMultipleNamespaces: + d0.ShadowMBRForMultipleNamespaces, err = feature.ReadShadowMBRForMultipleNamespacesFeature(frdr) case feature.CodeSeagatePorts: d0.SeagatePorts, err = feature.ReadSeagatePorts(frdr) default: diff --git a/pkg/core/feature/feature.go b/pkg/core/feature/feature.go index 21b40d26..1c3e76b9 100644 --- a/pkg/core/feature/feature.go +++ b/pkg/core/feature/feature.go @@ -14,25 +14,26 @@ import ( type FeatureCode uint16 const ( - CodeTPer FeatureCode = 0x0001 - CodeLocking FeatureCode = 0x0002 - CodeGeometry FeatureCode = 0x0003 - CodeSecureMsg FeatureCode = 0x0004 - CodeEnterprise FeatureCode = 0x0100 - CodeOpalV1 FeatureCode = 0x0200 - CodeSingleUser FeatureCode = 0x0201 - CodeDataStore FeatureCode = 0x0202 - CodeOpalV2 FeatureCode = 0x0203 - CodeOpalite FeatureCode = 0x0301 - CodePyriteV1 FeatureCode = 0x0302 - CodePyriteV2 FeatureCode = 0x0303 - CodeRubyV1 FeatureCode = 0x0304 - CodeLockingLBA FeatureCode = 0x0401 - CodeBlockSID FeatureCode = 0x0402 - CodeNamespaceLocking FeatureCode = 0x0403 - CodeDataRemoval FeatureCode = 0x0404 - CodeNamespaceGeometry FeatureCode = 0x0405 - CodeSeagatePorts FeatureCode = 0xC001 + CodeTPer FeatureCode = 0x0001 + CodeLocking FeatureCode = 0x0002 + CodeGeometry FeatureCode = 0x0003 + CodeSecureMsg FeatureCode = 0x0004 + CodeEnterprise FeatureCode = 0x0100 + CodeOpalV1 FeatureCode = 0x0200 + CodeSingleUser FeatureCode = 0x0201 + CodeDataStore FeatureCode = 0x0202 + CodeOpalV2 FeatureCode = 0x0203 + CodeOpalite FeatureCode = 0x0301 + CodePyriteV1 FeatureCode = 0x0302 + CodePyriteV2 FeatureCode = 0x0303 + CodeRubyV1 FeatureCode = 0x0304 + CodeLockingLBA FeatureCode = 0x0401 + CodeBlockSID FeatureCode = 0x0402 + CodeNamespaceLocking FeatureCode = 0x0403 + CodeDataRemoval FeatureCode = 0x0404 + CodeNamespaceGeometry FeatureCode = 0x0405 + CodeShadowMBRForMultipleNamespaces FeatureCode = 0x0407 + CodeSeagatePorts FeatureCode = 0xC001 ) type TPer struct { @@ -60,8 +61,12 @@ type CommonSSC struct { } type Geometry struct { - // TODO + Align bool + LogicalBlockSize uint32 + AlignmentGranularity uint64 + LowestAlignedLBA uint64 } + type SecureMsg struct { // TODO } @@ -75,7 +80,10 @@ type OpalV1 struct { // TODO } type SingleUser struct { - // TODO + NumberLockingObjectsSupported uint32 + Policy bool + Any bool + All bool } type DataStore struct { // TODO @@ -130,7 +138,12 @@ type BlockSID struct { } type NamespaceLocking struct { - // TODO + Range_C bool + Range_P bool + SUM_C bool + MaximumKeyCount uint32 + UnusedKeyCount uint32 + MaximumRangesPerNamespace uint32 } type DataRemoval struct { // TODO @@ -144,6 +157,10 @@ type SeagatePort struct { PortLocked uint8 } +type ShadowMBRForMultipleNamespaces struct { + ANS_C bool +} + type SeagatePorts struct { Ports []SeagatePort } @@ -181,8 +198,22 @@ func ReadLockingFeature(rdr io.Reader) (*Locking, error) { } func ReadGeometryFeature(rdr io.Reader) (*Geometry, error) { - f := &Geometry{} - return f, nil + d := struct { + Align uint8 + _ [7]byte + LogicalBlockSize uint32 + AlignmentGranularity uint64 + LowestAlignedLBA uint64 + }{} + if err := binary.Read(rdr, binary.BigEndian, &d); err != nil { + return nil, err + } + return &Geometry{ + Align: d.Align&0x1 > 0, + LogicalBlockSize: d.LogicalBlockSize, + AlignmentGranularity: d.AlignmentGranularity, + LowestAlignedLBA: d.LowestAlignedLBA, + }, nil } func ReadSecureMsgFeature(rdr io.Reader) (*SecureMsg, error) { @@ -204,8 +235,20 @@ func ReadOpalV1Feature(rdr io.Reader) (*OpalV1, error) { } func ReadSingleUserFeature(rdr io.Reader) (*SingleUser, error) { - f := &SingleUser{} - return f, nil + d := struct { + NumberOfLockingObjectsSupported uint32 + Policy uint8 + _ [7]byte + }{} + if err := binary.Read(rdr, binary.BigEndian, &d); err != nil { + return nil, err + } + return &SingleUser{ + NumberLockingObjectsSupported: d.NumberOfLockingObjectsSupported, + Policy: d.Policy&0x4 > 0, + All: d.Policy&0x2 > 0, + Any: d.Policy&0x1 > 0, + }, nil } func ReadDataStoreFeature(rdr io.Reader) (*DataStore, error) { @@ -273,8 +316,25 @@ func ReadBlockSIDFeature(rdr io.Reader) (*BlockSID, error) { } func ReadNamespaceLockingFeature(rdr io.Reader) (*NamespaceLocking, error) { - f := &NamespaceLocking{} - return f, nil + d := struct { + Range uint8 + _ [3]byte + MaximumKeyCount uint32 + UnusedKeyCount uint32 + MaximumRangesPerNamespace uint32 + }{} + if err := binary.Read(rdr, binary.BigEndian, &d); err != nil { + return nil, err + } + + return &NamespaceLocking{ + Range_C: d.Range&0x80 > 0, + Range_P: d.Range&0x40 > 0, + SUM_C: d.Range&0x20 > 0, + MaximumKeyCount: d.MaximumKeyCount, + UnusedKeyCount: d.UnusedKeyCount, + MaximumRangesPerNamespace: d.MaximumRangesPerNamespace, + }, nil } func ReadDataRemovalFeature(rdr io.Reader) (*DataRemoval, error) { @@ -287,6 +347,17 @@ func ReadNamespaceGeometryFeature(rdr io.Reader) (*NamespaceGeometry, error) { return f, nil } +func ReadShadowMBRForMultipleNamespacesFeature(rdr io.Reader) (*ShadowMBRForMultipleNamespaces, error) { + var raw uint8 + if err := binary.Read(rdr, binary.BigEndian, &raw); err != nil { + return nil, err + } + + return &ShadowMBRForMultipleNamespaces{ + ANS_C: raw&0x1 > 0, + }, nil +} + func ReadSeagatePorts(rdr io.Reader) (*SeagatePorts, error) { f := &SeagatePorts{} for { From edddefe9cc14e23b60ca89d3718ab9566f5a31b0 Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Tue, 3 Dec 2024 16:08:48 +0000 Subject: [PATCH 3/5] Read SecretProtect table --- cmd/tcgsdiag/main.go | 3 ++ pkg/core/table/locking.go | 68 +++++++++++++++++++++++++++++++++++++++ pkg/core/uid/tables.go | 1 + 3 files changed, 72 insertions(+) diff --git a/cmd/tcgsdiag/main.go b/cmd/tcgsdiag/main.go index 4025f608..d1c573b8 100644 --- a/cmd/tcgsdiag/main.go +++ b/cmd/tcgsdiag/main.go @@ -273,6 +273,9 @@ func main() { log.Printf("Locking SP LockingInfo:") spew.Dump(table.LockingInfo(s)) + log.Printf("Locking SP LockingSecretProtect:") + spew.Dump(table.LockingSecretProtect(s)) + log.Printf("Locking SP MBRTableInfo:") mbi, err := table.MBR_TableInfo(s) if err != nil { diff --git a/pkg/core/table/locking.go b/pkg/core/table/locking.go index 5d8740e9..2ac85160 100644 --- a/pkg/core/table/locking.go +++ b/pkg/core/table/locking.go @@ -34,6 +34,22 @@ const ( ResetHotPlug ResetType = 2 ) +type ProtectMechanism uint + +const ( + VendorUnique ProtectMechanism = 0 + AuthenticationDataRequired ProtectMechanism = 1 +) + +type SecretProtect struct { + UID uid.UID + Table uid.RowUID + Column uint + ProtectMechanism []ProtectMechanism +} + +const ProtectMechanismColumn uint = 3 + type LockingInfoRow struct { UID uid.RowUID Name *string @@ -59,6 +75,58 @@ func LockingSPActivate(s *core.Session) error { return nil } +func LockingSecretProtect(s *core.Session) ([]SecretProtect, error) { + if uids, err := Enumerate(s, uid.Locking_SecretProtect); err != nil { + return nil, err + } else { + result := make([]SecretProtect, len(uids)) + for i, rowUid := range uids { + val, err := GetFullRow(s, rowUid) + if err != nil { + return nil, err + } + + for col, val := range val { + switch col { + case "0", "UID": + v, ok := val.([]byte) + if !ok { + return nil, method.ErrMalformedMethodResponse + } + copy(result[i].UID[:], v[:8]) + case "1", "Table": + v, ok := val.([]byte) + if !ok { + return nil, method.ErrMalformedMethodResponse + } + copy(result[i].Table[:], v[:8]) + case "2", "Column": + v, ok := val.(uint) + if !ok { + return nil, method.ErrMalformedMethodResponse + } + result[i].Column = v + case "3", "ProtectMechanisms": + v, ok := val.(stream.List) + if !ok { + return nil, method.ErrMalformedMethodResponse + } + mechanisms := make([]ProtectMechanism, len(v)) + for n, val := range v { + mechanism, ok := val.(uint) + if !ok { + return nil, method.ErrMalformedMethodResponse + } + mechanisms[n] = ProtectMechanism(mechanism) + } + result[i].ProtectMechanism = mechanisms + } + } + } + return result, nil + } +} + func LockingInfo(s *core.Session) (*LockingInfoRow, error) { rowUID := uid.RowUID{} if s.ProtocolLevel == core.ProtocolLevelEnterprise { diff --git a/pkg/core/uid/tables.go b/pkg/core/uid/tables.go index 6de85058..ddf62e05 100644 --- a/pkg/core/uid/tables.go +++ b/pkg/core/uid/tables.go @@ -17,6 +17,7 @@ var ( Locking_LockingTable = TableUID{0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00} LockingGlobalRange = TableUID{0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x01} Locking_MBRTable = TableUID{0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00} + Locking_SecretProtect = TableUID{0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00} ) func (t *TableUID) Row(uid [4]byte) RowUID { From 5deb002ba730e4eb59ae280049e27d296d4d802a Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Tue, 3 Dec 2024 16:08:48 +0000 Subject: [PATCH 4/5] Use standard uint encoding for KeepGlobalRangeKey parameter --- pkg/core/table/locking.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/core/table/locking.go b/pkg/core/table/locking.go index 2ac85160..ba15c884 100644 --- a/pkg/core/table/locking.go +++ b/pkg/core/table/locking.go @@ -32,6 +32,9 @@ const ( ResetPowerOff ResetType = 0 ResetHardware ResetType = 1 ResetHotPlug ResetType = 2 + // The parameter number for KeepGlobalRangeKey SHALL be 0x060000 + // TCG Storage Security Subsystem Class: Opal | Version 2.02 | Revision 1.0 | Page 86 + KeepGlobalRangeKey uint = 0x060000 ) type ProtectMechanism uint @@ -620,9 +623,7 @@ func RevertLockingSP(s *core.Session, keep bool, pwhash []byte) error { mc := method.NewMethodCall(uid.InvokeIDThisSP, uid.OpalRevertSP, s.MethodFlags) if keep { mc.Token(stream.StartName) - // KeepGlobalRangeKey, TCG Storage Security Subsystem Class: Opal | Version 2.02 | Revision 1.0 | Page 85 - // sedutil-cli looks like a Short-Atom without byte or integer indicator - mc.RawByte([]byte{0x83, 0x06, 0x00, 0x00}) + mc.UInt(KeepGlobalRangeKey) mc.Token(stream.OpalTrue) mc.Token(stream.EndName) } From 0d7ed10c5c82bd2cf464b18b4ca21c1d0fe6864e Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Tue, 3 Dec 2024 16:08:48 +0000 Subject: [PATCH 5/5] Remove superfluous param from RevertLockingSP() --- cmd/gosedctl/cmd.go | 2 +- pkg/core/table/locking.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/gosedctl/cmd.go b/cmd/gosedctl/cmd.go index 7664623e..eb66b358 100644 --- a/cmd/gosedctl/cmd.go +++ b/cmd/gosedctl/cmd.go @@ -255,7 +255,7 @@ func (r *revertNoeraseCmd) Run(ctx *context) error { return fmt.Errorf("authenticating as Admin1 failed: %v", err) } - if err := table.RevertLockingSP(lockingSession, true, pwhash); err != nil { + if err := table.RevertLockingSP(lockingSession, true); err != nil { return fmt.Errorf("RevertLockingSP() failed: %v", err) } return nil diff --git a/pkg/core/table/locking.go b/pkg/core/table/locking.go index ba15c884..0942e180 100644 --- a/pkg/core/table/locking.go +++ b/pkg/core/table/locking.go @@ -619,7 +619,7 @@ func LoadPBAImage(s *core.Session, image []byte) error { return nil } -func RevertLockingSP(s *core.Session, keep bool, pwhash []byte) error { +func RevertLockingSP(s *core.Session, keep bool) error { mc := method.NewMethodCall(uid.InvokeIDThisSP, uid.OpalRevertSP, s.MethodFlags) if keep { mc.Token(stream.StartName)