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

FROMintersystemsdc/iris-community

 

USERroot

 

ENVDEBIAN_FRONTENDnoninteractive

 

# install libraries required to pyDes to process 3DES
RUNapt-get-yupdate\
    &&apt-get-yinstallapt-utils\
    &&apt-getinstall-ybuild-essentialunzippkg-configwget\
    &&apt-getinstall-ypython3-pip  

 

# use pip3 (the python zpm) to install 3DES dependencies
RUNpip3install--upgradepipsetuptoolswheel
RUNpip3install--target/usr/irissys/mgr/pythonpyDes

 

USERroot  
WORKDIR/opt/irisbuild
RUNchown${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP}/opt/irisbuild
USER${ISC_PACKAGE_MGRUSER}

 

WORKDIR/opt/irisbuild
COPY  srcsrc
COPYInstaller.clsInstaller.cls
COPYmodule.xmlmodule.xml
COPYiris.scriptiris.script
ENVSECRETKEY=InterSystemsIRIS2022SupportToTripleDES

 

USER${ISC_PACKAGE_MGRUSER}

 

RUNirisstartIRIS\
    &&irissessionIRIS<iris.script\
    &&irisstopIRISquietly

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
Classdc.crypto.CryptoService
{

 

ClassMethodDo3DESEncryptation(MessageAs%String)As%Library.String
{
        SetKey=$system.Util.GetEnviron("SECRETKEY")
        Return..Encrypt3DES(Message,Key)
}

 

ClassMethodDo3DESDecryptation(MessageAs%String)As%Library.String
{
        SetKey=$system.Util.GetEnviron("SECRETKEY")
        Return..Decrypt3DES(Message,Key)
}

 

///encrypt messages
ClassMethodEncrypt3DES(msg,key)[Language=python]
{
        frompyDesimporttriple_des,CBC,PAD_PKCS5
        importbinascii

 

        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)
        returnbinascii.b2a_hex(en).decode(encoding='utf-8')
}

 

///decrypt messages
ClassMethodDecrypt3DES(msg,key)[Language=python]
{
        frompyDesimporttriple_des,CBC,PAD_PKCS5
        importbinascii

 

        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)
        returnde.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

Classdc.crypto.CryptoRESTAppExtends%CSP.REST
{

 

ParameterCHARSET="utf-8";

 

ParameterCONVERTINPUTSTREAM=1;

 

ParameterCONTENTTYPE="application/json";

 

ParameterVersion="1.0.0";

 

ParameterHandleCorsRequest=1;

 

XDataUrlMap[XMLNamespace="http://www.intersystems.com/urlmap"]
{
<Routes>
<!--ServerInfo-->
<RouteUrl="/"Method="GET"Call="GetInfo"Cors="true"/>
<!--Swaggerspecs-->
<RouteUrl="/_spec"Method="GET"Call="SwaggerSpec"/>

 

<!--encryptamessage-->
<RouteUrl="/encrypt"Method="POST"Call="EncryptMessage"/>

 

<!--decryptamessage-->
<RouteUrl="/decrypt"Method="POST"Call="DecryptMessage"/>

 

</Routes>
}

 

///Encrypt message
ClassMethodEncryptMessage()As%Status
{
    Try{
      SetMessage=$ZCONVERT(%request.Content.Read(),"I","UTF8")
      SetResponse=##class(dc.crypto.CryptoService).Do3DESEncryptation(Message)
      Set%response.Status=200
      Set%response.Headers("Access-Control-Allow-Origin")="*"
      WriteResponse
      Return$$$OK
    }Catcherr{
      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
ClassMethodDecryptMessage()As%Status
{
  Try{
    SetMessage=$ZCONVERT(%request.Content.Read(),"I","UTF8")
    SetResponse=##class(dc.crypto.CryptoService).Do3DESDecryptation(Message)
    Set%response.Status=200
    Set%response.Headers("Access-Control-Allow-Origin")="*"
    WriteResponse
    Return$$$OK
  }Catcherr{
    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
ClassMethodGetInfo()As%Status
{
  SETversion=..#Version
  SETfmt=##class(%SYS.NLS.Format).%New("ptbw")
 
  SETinfo={
    "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")="*"

 

  Writeinfo.%ToJSON()
  Quit$$$OK
}

 

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

 

ClassMethod%ProcessResult(pStatusAs%Status={$$$OK},pResultAs%DynamicObject="")As%Status[Internal]
{
  #dim%responseAs%CSP.Response
  SETtSC=$$$OK
  IF$$$ISERR(pStatus){
    SET%response.Status=500
    SETtSC=..StatusToJSON(pStatus,.tJSON)
    IF$isobject(tJSON){
      SETpResult=tJSON
    }ELSE{
      SETpResult={"errors":[{"error":"Unknown error parsing status code"}]}
    }
  }
  ELSEIFpStatus=1{
    IF'$isobject(pResult){
      SETpResult={
      }
    }
  }
  ELSE{
    SET%response.Status=pStatus
    SETerror=$PIECE(pStatus," ",2,*)
    SETpResult={
      "error":(error)
    }
  }
 
  IFpResult.%Extends("%Library.DynamicAbstractObject"){
    WRITEpResult.%ToJSON()
  }
  ELSEIFpResult.%Extends("%JSON.Adaptor"){
    DOpResult.%JSONExport()
  }
  ELSEIFpResult.%Extends("%Stream.Object"){
    DOpResult.OutputToDevice()
  }
 
  QUITtSC
}

 

}

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