There are four steps when implementing a host command:
  • Create the host command XML definition.
  • Implement the command code.
  • Build the simulator.
  • Add unit tests.

Create the host command XML definition

The most important thing to understand when implementing a host command is how the simulator's host XML definition works.

The simulator receives messages as TCP messages. The first thing the simulator must do is try to parse the message in order to understand:
  1. what host command the request message refers to, and
  2. the contents of the message.
To that end, the simulator uses a list of XML files, each describing a host command. The first thing, therefore, to create when implementing a host command is the XML host definition for that command. To understand how an XML host definition file is formed take a look at this file which is an XSD for the XML host definition.

For example, consider the HA command that generates a new TAK and returns it encrypted under a TMK and the LMK. The XML host definition file for the HA command is the following:
<?xml version="1.0" encoding="utf-8" ?>
<CommandConfiguration xmlns="http://tempuri.org/HostCommandDefinitions.xsd">
  <CommandName>Generates a TAK.</CommandName>
  <Request>HA</Request>
  <Response>HB</Response>

  <Field>
    <Name>TMK</Name>
    <IncludeFile>MultiFormatKey.xml</IncludeFile>
  </Field>

  <Field>
    <Name>Delimiter</Name>
    <Length>1</Length>
    <Type>Character</Type>
    <ValidValue>;</ValidValue>
  </Field>

  <Field>
    <Name>Key Scheme TMK</Name>
    <Length>1</Length>
    <Type>Character</Type>
    <DependentField>Delimiter</DependentField>
    <DependentValue>;</DependentValue>
    <ExclusiveDependency>false</ExclusiveDependency>
  </Field>

  <Field>
    <Name>Key Scheme LMK</Name>
    <Length>1</Length>
    <Type>Character</Type>
    <DependentField>Delimiter</DependentField>
    <DependentValue>;</DependentValue>
    <ExclusiveDependency>false</ExclusiveDependency>
  </Field>

  <Field>
    <Name>Reserved</Name>
    <Length>1</Length>
    <Type>Character</Type>
    <DependentField>Delimiter</DependentField>
    <DependentValue>;</DependentValue>
    <ExclusiveDependency>false</ExclusiveDependency>
  </Field>

</CommandConfiguration>
The XML starts by defining the command name, command request and response codes. This is general information used to identify the command. After that, a series of <Field> elements define the layout of the host command contents excluding the message header and command code.

The first field in the command is the TMK under which the TAK is encrypted. A TMK can be expressed in a number of ways: as a series of 16 hex characters (single-length key), as a variant double or triple length key or as an ANSI double or triple length key. Parsing keys is a common task shared by most host commands, so there already are XML files that include the parsing logic. The first field, therefore, includes the multi-format key definition XML file. You can use this and other common files (such as this, this, or this) in your host command XML definitions.

After the TMK, a delimiter field is declared. Note that we've specified the valid value for a delimiter. If something else is encountered, the simulator will throw an exception and log an error.

After the delimiter there may be three additional fields but they may also be omitted. However, if they are included in the message, they must be placed after the delimiter. That is why the next three fields indicate that they depend on the delimiter field being present.

Once your host command XML definition is completed, save it in the ThalesCore/XMLDefs/HostCommands folder. Use a descriptive name like <CommandDescription>_<CommandCode>.xml. The HA command is saved as GenerateTAK_HA.xml.

There are other tricks that can be used with XML definitions. For example, it's possible to not indicate a field's length and have the simulator parse the field until a specific value is found. Or, instead of throwing an exception, a specific rejection code can be used if a field doesn't have a valid value. Browse the existing XML definitions to see more.

Implement the command code

Create a new class in the ThalesCore/HostCommands/BuiltIn directory and use the same naming convention as before. The HA command code is saved as GenerateTAK_HA.vb. Your class:
  • should be in the HostCommands.BuiltIn namespace.
  • should inherit from AHostCommand.
  • should be decorated with the ThalesCommandCode attribute.
  • should include a parameter-less constructor.
  • should override AcceptMessage and ConstructResponse.
Here's the code for the HA command:
Imports ThalesSim.Core.Message
Imports ThalesSim.Core.Cryptography
Imports ThalesSim.Core

Namespace HostCommands.BuildIn

    ''' <summary>
    ''' Generates a TAK.
    ''' </summary>
    ''' <remarks>
    ''' This class implements the HA Racal command.
    ''' </remarks>
    <ThalesCommandCode("HA", "HB", "", "Generates a TAK")> _
    Public Class GenerateTAK_HA
        Inherits AHostCommand

        Private _sourceTmk As String
        Private _del As String
        Private _keySchemeZMK As String
        Private _keySchemeLMK As String

        ''' <summary>
        ''' Constructor.
        ''' </summary>
        ''' <remarks>
        ''' The constructor sets up the HA message parsing fields.
        ''' </remarks>
        Public Sub New()
            ReadXMLDefinitions()
        End Sub

        ''' <summary>
        ''' Parses the request message.
        ''' </summary>
        ''' <remarks>
        ''' This method parses the command message. The message header and message command
        ''' code are <b>not</b> part of the message.
        ''' </remarks>
        Public Overrides Sub AcceptMessage(ByVal msg As Message.Message)
            XML.MessageParser.Parse(msg, XMLMessageFields, kvp, XMLParseResult)
            If XMLParseResult = ErrorCodes.ER_00_NO_ERROR Then
                _sourceTmk = kvp.ItemCombination("TMK Scheme", "TMK")
                _del = kvp.ItemOptional("Delimiter")
                _keySchemeZMK = kvp.ItemOptional("Key Scheme TMK")
                _keySchemeLMK = kvp.ItemOptional("Key Scheme LMK")
            End If
        End Sub

        ''' <summary>
        ''' Creates the response message.
        ''' </summary>
        ''' <remarks>
        ''' This method creates the response message. The message header and message reply code
        ''' are <b>not</b> part of the message.
        ''' </remarks>
        Public Overrides Function ConstructResponse() As Message.MessageResponse
            Dim mr As New MessageResponse

            Dim ks As KeySchemeTable.KeyScheme, tmkKs As KeySchemeTable.KeyScheme

            If _del = DELIMITER_VALUE Then
                If ValidateKeySchemeCode(_keySchemeLMK, ks, mr) = False Then Return mr
                If ValidateKeySchemeCode(_keySchemeZMK, tmkKs, mr) = False Then Return mr
            Else
                ks = KeySchemeTable.KeyScheme.SingleDESKey
                tmkKs = KeySchemeTable.KeyScheme.SingleDESKey
            End If

            Dim clearSource As String

            Dim cryptSource As New HexKey(_sourceTmk)
            clearSource = Utility.DecryptUnderLMK(cryptSource.ToString, cryptSource.Scheme, LMKPairs.LMKPair.Pair14_15, "0")
            If Utility.IsParityOK(clearSource, Utility.ParityCheck.OddParity) = False Then
                mr.AddElement(ErrorCodes.ER_10_SOURCE_KEY_PARITY_ERROR)
                Return mr
            End If

            Dim clearKey As String = Utility.CreateRandomKey(tmkKs)

            Dim cryptKeyTMK As String = Utility.EncryptUnderZMK(clearSource, clearKey, tmkKs)
            Dim cryptKeyLMK As String = Utility.EncryptUnderLMK(clearKey, ks, LMKPairs.LMKPair.Pair16_17, "0")

            Log.Logger.MinorInfo("TMK (clear): " + clearSource)
            Log.Logger.MinorInfo("TAK (clear): " + clearKey)
            Log.Logger.MinorInfo("TAK (TMK): " + cryptKeyTMK)
            Log.Logger.MinorInfo("TAK (LMK): " + cryptKeyLMK)

            mr.AddElement(ErrorCodes.ER_00_NO_ERROR)

            mr.AddElement(cryptKeyTMK)
            mr.AddElement(cryptKeyLMK)

            Return mr

        End Function

    End Class

End Namespace

The ThalesCommandCode is used by the simulator to understand which class implements which command and it is imperative that it contains correct information. If it does not, the simulator will not be able to locate the class implementing the command or may respond with an incorrect command code.

The parameter-less constructor contains only a single line of code that is used to read the command code XML definition. If the naming convention has been followed, no other information will be necessary.

The AcceptMessage method is called by the simulator when a host command message arrives. The code included in AcceptMessage typically calls the XML parser's Parse method and then stores field values to class variables.

After AcceptMessage, the simulator calls the ConstructResponse method. The code in this method implements the core command logic. It uses the field data parsed and locally stored by the AcceptMessage method. The ConstructResponse method must return an instance of MessageResponse, which is simply a list of elements to return to the caller. For example, a successful execution of the HA command returns 00 to indicate success, the generated TAK encrypted under the TMK and the generated TAK encrypted under the LMK.

ConstructResponse can use any method implemented by the simulator. Look up the simulator's compiled CHM file for more detailed information on those. Typical tasks of any ConstructResponse implementation are:
  • Validate parameters, such as key schemes.
  • Decrypt key parameters that are encrypted under the LMK to get the clear values.
  • Check that key parameters have correct parity.

Build the simulator

Once the command is implemented, compile the ThalesCore and ThalesWinSimulator projects, then run the ThalesWinSimulator project.

Add unit tests

The ThalesCore.Tests project contains the HostCommandTests.vb file that includes unit tests to verify that commands behave as expected. Although optional, it's good practice to construct a series of expected responses to known inputs for a newly implemented command and add them as a unit test for that command.

Last edited Jul 7, 2012 at 10:51 PM by nickntg, version 1

Comments

No comments yet.