Written by

Software Architect at Visum
Article Yuri Marx · Mar 30, 2022 9m read

3DES support

There are several ways of classifying cryptographic algorithms: 1) Secret Key Cryptography (SKC) - Uses a single key for both encryption and decryption. It is also called symmetric encryption. Primarily, it was used for privacy and confidentiality; 2) Public Key Cryptography (PKC) - Uses one key for encryption and another one for decryption. It is also called asymmetric encryption. Initially, it was utilised for authentication, non-repudiation, and key exchange; 3) Hash Functions - Uses a mathematical transformation to irreversibly "encrypt" information, providing a digital fingerprint. Originally, it was employed for message integrity. The InterSystems IRIS supports encryption algorithms in all the above-mentioned categories. However, the 3DES (Triple DES) algorithm, many popular and based on SKC, is not supported by the %SYSTEM.Encryption class. The IRIS support for Embedded Python allows using the Python language to support 3DES through the Python package pyDes (https://pypi.org/project/pyDes/). In this article, we will demonstrate to you how it works.

Get the 3DES sample application

To get the sample and run it, follow these steps:

  1. Go to the https://openexchange.intersystems.com/package/crypto-iris and click Download to go to the git repository.
  2. Clone the project: git clone https://github.com/yurimarx/crypto-iris.git.  
  3. Go to the project folder crypto-iris.
  4. Do the build: docker-compose build.
  5. Execute the containers: docker-compose up -d.
  6. Check in your docker desktop with the instances if everything is ok:


Interface gráfica do usuário, Texto, Aplicativo, Email  Descrição gerada automaticamente

Try to encrypt some texts using IRIS Terminal

1. Attach shell to crypto-iris docker instance:


2. In terminal execute iris session iris:

3. Change to IRISAPP namespace:


4. Encrypt and Decrypt a message:


Try to encrypt some texts using ObjectScript CryptoService class

1. From any ObjectScript class call ##class(dc.crypto.CryptoService).Do3DESEncryptation("YOURMESSAGE") to encrypt.
2. From any ObjectScript class call ##class(dc.crypto.CryptoService).Do3DESDecryptation("YOURMESSAGE") to decrypt.

 

Try to encrypt and decrypt some texts using Postman

1. Go to your Postman (or another similar REST client) and configure the request as shown in this image to encrypt:

Request Encrypt messages
 
 

2. Click send and get a text encrypted as a binary hex message
3. Go to your Postman (or another similar REST client) and configure the request as indicated in this image to decrypt:


Request Decrypt messages
 

4. Click send and get a text decrypted as a string message

Python and ObjectScript code supporting 3DES

Docker File
The Dockerfile installs python and pyDes package, copies the source file, creates the SECRETKEY which will be used as the private key on encryption and runs IRIS.

 

Dockerfile

FROM intersystemsdc/iris-community

 

USER root

 

ENV DEBIAN_FRONTEND noninteractive

 

# install libraries required to pyDes to process 3DES
RUN apt-get -y update \
    && apt-get -y install apt-utils \
    && apt-get install -y build-essential unzip pkg-config wget \
    && apt-get install -y python3-pip  

 

# use pip3 (the python zpm) to install 3DES dependencies
RUN pip3 install --upgrade pip setuptools wheel
RUN pip3 install --target /usr/irissys/mgr/python pyDes

 

USER root  
WORKDIR /opt/irisbuild
RUN chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/irisbuild
USER ${ISC_PACKAGE_MGRUSER}

 

WORKDIR /opt/irisbuild
COPY  src src
COPY Installer.cls Installer.cls
COPY module.xml module.xml
COPY iris.script iris.script
ENV SECRETKEY=InterSystemsIRIS2022SupportToTripleDES

 

USER ${ISC_PACKAGE_MGRUSER}

 

RUN iris start IRIS \
    && iris session IRIS < iris.script \
    && iris stop IRIS quietly

Class dc.crypto.CryptoService
This class implements Encrypt3DES and Decrypt3DES python methods to encrypt and decrypt strings. To do that, it imports triple_des, CBC and PAD_PKCS5 to do string encryption and decryption. The binaascii package is imported to return the encrypted string as ASCII hexadecimal string, resolving possible Unicode problems.

 

CrytoService class

/// Cryptography services
Class dc.crypto.CryptoService
{

 

ClassMethod Do3DESEncryptation(Message As %String) As %Library.String
{
        Set Key = $system.Util.GetEnviron("SECRETKEY")
        Return ..Encrypt3DES(Message, Key)
}

 

ClassMethod Do3DESDecryptation(Message As %String) As %Library.String
{
        Set Key = $system.Util.GetEnviron("SECRETKEY")
        Return ..Decrypt3DES(Message, Key)
}

 

/// encrypt messages
ClassMethod Encrypt3DES(msg, key) [ Language = python ]
{
        from pyDes import triple_des, CBC, PAD_PKCS5
        import binascii

 

        secret_key = key[:24]
        iv = secret_key[-8:]
        k = triple_des(secret_key, mode=CBC, IV=iv, padmode=PAD_PKCS5)
        en = k.encrypt(msg, padmode=PAD_PKCS5)
        return binascii.b2a_hex(en).decode(encoding='utf-8')
}

 

/// decrypt messages
ClassMethod Decrypt3DES(msg, key) [ Language = python ]
{
        from pyDes import triple_des, CBC, PAD_PKCS5
        import binascii

 

        secret_key = key[:24]
        iv = secret_key[-8:]
        k = triple_des(secret_key, mode=CBC, IV=iv, padmode=PAD_PKCS5)
        de = k.decrypt(binascii.a2b_hex(msg), padmode=PAD_PKCS5)
        return de.decode(encoding='utf-8')
}

 

}

The triple_des python method returns the class to encrypt and decrypt texts. Note that the size of the key was limited to 24 positions and IV to 8.

Class dc.crypto.CryptoRESTApp
This class exposes encryption and decryption services as REST services.

 

CryptoRESTApp

Class dc.crypto.CryptoRESTApp Extends %CSP.REST
{

 

Parameter CHARSET = "utf-8";

 

Parameter CONVERTINPUTSTREAM = 1;

 

Parameter CONTENTTYPE = "application/json";

 

Parameter Version = "1.0.0";

 

Parameter HandleCorsRequest = 1;

 

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<!-- Server Info -->
<Route Url="/" Method="GET" Call="GetInfo" Cors="true"/>
<!-- Swagger specs -->
<Route Url="/_spec" Method="GET" Call="SwaggerSpec" />

 

<!-- encrypt a message -->
<Route Url="/encrypt" Method="POST" Call="EncryptMessage" />

 

<!-- decrypt a message -->
<Route Url="/decrypt" Method="POST" Call="DecryptMessage" />

 

</Routes>
}

 

/// Encrypt message
ClassMethod EncryptMessage() As %Status
{
    Try {
      Set Message = $ZCONVERT(%request.Content.Read(),"I","UTF8")
      Set Response = ##class(dc.crypto.CryptoService).Do3DESEncryptation(Message)
      Set %response.Status = 200
      Set %response.Headers("Access-Control-Allow-Origin")="*"
      Write Response
      Return $$$OK
    } Catch err {
      write !, "Error name: ", ?20, err.Name,
          !, "Error code: ", ?20, err.Code,
          !, "Error location: ", ?20, err.Location,
          !, "Additional data: ", ?20, err.Data, !
      Return $$$NOTOK
  }
}

 

/// Decrypt messages
ClassMethod DecryptMessage() As %Status
{
  Try {
    Set Message = $ZCONVERT(%request.Content.Read(),"I","UTF8")
    Set Response = ##class(dc.crypto.CryptoService).Do3DESDecryptation(Message)
    Set %response.Status = 200
    Set %response.Headers("Access-Control-Allow-Origin")="*"
    Write Response
    Return $$$OK
  } Catch err {
    write !, "Error name: ", ?20, err.Name,
          !, "Error code: ", ?20, err.Code,
          !, "Error location: ", ?20, err.Location,
          !, "Additional data: ", ?20, err.Data, !
    Return $$$NOTOK
  }
}

 

/// General information
ClassMethod GetInfo() As %Status
{
  SET version = ..#Version
  SET fmt=##class(%SYS.NLS.Format).%New("ptbw")
 
  SET info = {
    "Service": "3DES Service API",
    "version": (version),
    "Developer": "Yuri Gomes",
    "Status": "Ok",
    "Date": ($ZDATETIME($HOROLOG))
  }
  Set %response.ContentType = ..#CONTENTTYPEJSON
  Set %response.Headers("Access-Control-Allow-Origin")="*"

 

  Write info.%ToJSON()
  Quit $$$OK
}

 

ClassMethod SwaggerSpec() As %Status
{
  Set tSC = ##class(%REST.API).GetWebRESTApplication($NAMESPACE, %request.Application, .swagger)
  Do swagger.info.%Remove("x-ISC_Namespace")
  Set swagger.basePath = "/iris-tts"
  Set swagger.info.title = "TTS Service API"
  Set swagger.info.version = "1.0"
  Set swagger.host = "localhost:52773"
  Return ..%ProcessResult($$$OK, swagger)
}

 

ClassMethod %ProcessResult(pStatus As %Status = {$$$OK}, pResult As %DynamicObject = "") As %Status [ Internal ]
{
  #dim %response As %CSP.Response
  SET tSC = $$$OK
  IF $$$ISERR(pStatus) {
    SET %response.Status = 500
    SET tSC = ..StatusToJSON(pStatus, .tJSON)
    IF $isobject(tJSON) {
      SET pResult = tJSON
    } ELSE {
      SET pResult = { "errors": [ { "error": "Unknown error parsing status code" } ] }
    }
  }
  ELSEIF pStatus=1 {
    IF '$isobject(pResult){
      SET pResult = {
      }
    }
  }
  ELSE {
    SET %response.Status = pStatus
    SET error = $PIECE(pStatus, " ", 2, *)
    SET pResult = {
      "error": (error)
    }
  }
 
  IF pResult.%Extends("%Library.DynamicAbstractObject") {
    WRITE pResult.%ToJSON()
  }
  ELSEIF pResult.%Extends("%JSON.Adaptor") {
    DO pResult.%JSONExport()
  }
  ELSEIF pResult.%Extends("%Stream.Object") {
    DO pResult.OutputToDevice()
  }
 
  QUIT tSC
}

 

}

Modes of operation supported with 3DES

Our sample used CBC mode, but the 3DES also operates the following modes:

ECB:

Electronic Code Book (ECB)

The most basic but also the weakest mode of operation. Each block of plaintext is encrypted independently of any other block.

CBC:

Cipher-Block Chaining (CBC)

It is a mode of operation where each plaintext block gets XOR-ed with the previous ciphertext block prior to encryption.

CFB:

Cipher FeedBack (CFB)

It is a mode of operation which turns the block cipher into a stream cipher. Each byte of plaintext is XOR-ed with a byte taken from a keystream: the result is the ciphertext.

OFB:

Output FeedBack (OFB)

It is another mode that leads to a stream cipher. Each byte of plaintext is XOR-ed with a byte taken from a keystream: the result is the ciphertext. The keystream is obtained by recursively encrypting the Initialization Vector.

CTR:

CounTer Mode (CTR)

This mode turns the block cipher into a stream cipher. Each byte of plaintext is XOR-ed with a byte taken from a keystream: the result is the ciphertext. The keystream is generated by encrypting a sequence of counter blocks with ECB.

More details on: https://pycryptodome.readthedocs.io/en/latest/src/cipher/des3.html
To get more information about Triple DES visit: https://en.wikipedia.org/wiki/Triple_DES

Comments

Yuri Marx  Mar 30, 2022 to Julius Kavay

I agree with you, but many legacy systems are using 3DES, so it is important have this option available.

0
Julius Kavay  Mar 30, 2022 to Yuri Marx

However, those legacy systems are already in operation, therefore they neither need python nor 3DES, at most, an upgrade to an current system. Hence, I don't understand your argumentation.

0
Yuri Marx  Mar 30, 2022 to Julius Kavay

A system built with IRIS and interoperating with a legacy system (using 3DES) needs to decrypt data sent from legacy (using 3DES). So it will decrypt using 3DES, because the parts must share same key and same way to encrypt and decrypt.

0
Yuri Marx  Jul 25, 2023 to Vadim Aniskin

Thanks!

0