Quantum safe cryptography and IBM Confidential Computing

Quantum safe cryptography and IBM Confidential Computing

Quantum safe cryptography is a current hot topic. IBM already provides cryptographic product implementing quantum resistant schemes and I wanted to provide coding examples for developers interested to leverage them. In this article, we will create an "hello world" application in Go programming language to leverage quantum safe Dilithium digital signature scheme within the IBM confidential computing solution: IBM Hyper Protect software appliance.

The complete source of this sample is available at this link.

IBM Trusted Execution Environment

Confidential Computing provides protection against internal attacks by running application within an enclave called a Trusted Execution environment (TEE). The TEE uses hardware protected access for the application memory pages that forbids access to the operating system and to the virtualization hypervisor.

IBM Hyper Protect software appliance is an all in one solution to deploy and manage such applications using TEE protection on IBM LinuxONE or IBM Z servers. The appliance starts up in Secure Boot mode in to order prevent any tampering.

As illustrated below, using TEE is then made easy: application developers generate encrypted registration files that specify in particular OCI container urls and Docker Content Trust signing keys. The files can only be decrypted by the Hyper Protect software appliance that runs in a TEE: it retrieves container images from the registry and checks for their signature before instantiating them as part of the TEE enclave.

No alt text provided for this image

Online documentation for creating TEE registration file is available at this link.

Protected data at rest - TEE and Hardware Security Module access

TEE can have storage attached. IBM appliance uses LUKS filesytem. AES ciphering keys are protected within the appliance TEE.

For cryptographic keys processed by banking applications and digital asset management applications, Hardware Security Module is the defacto standard to protect them and to comply with regulations. An Hardware Security Module can be attached to a TEE but the direct access to the adapter is prevented.

No alt text provided for this image

For this purpose, IBM provides a stateless gateway server (called GREP11) running in the TEE as part of the IBM Hyper Protect appliance.

The application connects the grep11 server using mTLS authentication that will remotely invoke the PKCS11 operations on the HSM.

The following link details how to setup and deploy a GREP11 server using the IBM Hyper Protect appliance.

But for the need of our sample, we can enable an external connection to the grep11 server using NAT at the IBM Hyper Protect appliance level. The following section just needs to be added to the grep11 server deployment file.

? ports
??- hostport: 9876
????protocol: tcp
????containerport: 9876
        


No alt text provided for this image

Using the previous definition for your grep11 deployment file, the IBM Hyper Protect appliance will allow connection on port 9786 and will route the traffic onto the grep11 server listening port (9876).


Also illustrated in the above picture, a wrapping key (in purple) is stored inside the IBM Crypto Express adapter and protects cryptographic material: our program will actually create a pair of dilithium secure keys. A secure key is type of key protected by another key (called the wrapping key) using AES encryption. The wrapping key is loaded in the IBM Crypto Express adapter via a ceremony process and is unextractable. The IBM Crypto adapters process cryptographic operations using these secure keys.

PKCS11 over gRPCs (Google Remote Procedure Call)

PKCS 11 API defines a platform-independent API to work with Hardware Security Module. The EP11 library provides a stateless interface similar to the PKCS 11 API and satisfies several FIPS and NIST standards. ?

The IBM GREP11 server provides remote capability for the EP11 API over gRPC (GREP11). GREP11 API documentation can be found here.

Our client will connect the GREP11 server using mTLS via the following credentials.yaml configuration file:

url: "grep11.myca.com:9876
cert_path: "/home/girardjy/Digital Asset/client.pem"
key_path: "/home/girardjy/Digital Asset/client-key.pem"
cacert_path: "/home/girardjy/Digital Asset/ca.pem"        

Do not forget to add an entry in your /etc/hosts files for grep11.myca.com using your IBM Hyper Protect appliance IP address. Creating certificates for the CA, the grep11 server and your program are documented here.

CRYSTALS-Dilithium

What is it ?

Quantum safe or post-quantum cryptography denotes cryptographic algorithms that resist attacks from classical as well as from quantum computers. The CRYSTALS-Dilithium Digital Signature Algorithm is one of the shortlisted candidate in the NIST Post-Quantum Cryptography Standardization Process.

Within EP11, the CRYSTALS-Dilithium algorithm currently provides security category SHA384/ SHA3-384 and performance category Dilithium-1536x1280 (also called Dilithium-6-5).?It has the oid 1.3.6.1.4.1.2.267.1.6.5.

Currently, EP11 provides one single mechanism CKM_IBM_DILITHIUM?with three operations: key generation, sign, and verify that I will illustrate in the next code snippets.

The three examples are to be compiled and run on your laptop where the Go programming language has been properly setup. The three programs connect the IBM Hyper Protect appliance where a grep11 server has been instantiated.

Generating public and private keys

The following example shows how to create a key pair using GREP11 API. Obviously, make sure you allow sign and verify operations in your key templates with CKA_VERIFY and CKA_SIGN attributes set as true. The CKA_IBM_PGC_PARAMS parameter specifies the Dilithium scheme where PGC stands for Post Quantum Cryptography.

func main()
	cryptoClient := getGrep11Server()
	defer disconnectGrep11Server()?

	dilithiumStrengthParam, err := asn1.Marshal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 2, 267, 1, 6, 5}) // Round 2 strength)
	if err != nil {
		panic(fmt.Errorf("Unable to encode parameter OID: %s", err))
	}

	publicKeyTemplate := ep11.EP11Attributes{
		ep11.CKA_IBM_PQC_PARAMS: dilithiumStrengthParam,
		ep11.CKA_VERIFY:? ? ? ? ?true,
		ep11.CKA_EXTRACTABLE:? ? false,
	}
	privateKeyTemplate := ep11.EP11Attributes{
		ep11.CKA_SIGN:? ? ? ? true,
		ep11.CKA_EXTRACTABLE: false,
	}

	generateDilKeyPairRequest := &pb.GenerateKeyPairRequest{
		Mech:? ? ? ? ? ? &pb.Mechanism{Mechanism: ep11.CKM_IBM_DILITHIUM},
		PubKeyTemplate:? AttributeMap(publicKeyTemplate),
		PrivKeyTemplate: AttributeMap(privateKeyTemplate),
	}

	// Dilithium Key Pair generation
	generateDilKeyPairResponse, err := cryptoClient.GenerateKeyPair(context.Background(), generateDilKeyPairRequest)
	if ok, ep11Status := Convert(err); !ok {
		if ep11Status.Code == ep11.CKR_MECHANISM_INVALID {
			fmt.Println("Dilithium mechanism is not supported on the remote HSM")
			return
		} else {
			panic(fmt.Errorf("Generate Dilithium key pair error: %s", err))
		}
	}

? ? pubkeyhex := make([]byte, hex.EncodedLen(len(generateDilKeyPairResponse.PubKeyBytes)))
? ? hex.Encode(pubkeyhex, generateDilKeyPairResponse.PubKeyBytes)
? ? fmt.Println("Public Dilithium key: " +string(pubkeyhex)+"\n")

? ? privkeyhex := make([]byte, hex.EncodedLen(len(generateDilKeyPairResponse.PrivKeyBytes)))
? ? hex.Encode(privkeyhex, generateDilKeyPairResponse.PrivKeyBytes)
? ? fmt.Println("Private Dilithium key: " +string(privkeyhex))
}        

Running this program outputs two long (secret) keys encoded in hexadecimal. They cannot be used outside the HSM.

$ ./dilithium
Public Dilithium key: 30820702300f060b2b060104010.......

Private Dilithium key: 0000000000000000000000000000000000...
        

We will export the two hexadecimal strings as environment variables so that we can easily use it to sign and verify the message string in the next examples.

$ export PK=30820702300f060b2b060104010... (3 854 characters)

$ export SK=0000000000000000000000000000000000.... (11 617 characters)        

Signing a message

The following sample illustrates how to sign a message using Dilithium. I use the SK environment variable to pass the Dilithium private key. The message to sign is passed as the command parameter.

func main()( ) 

	cryptoClient := getGrep11Server()
	defer disconnectGrep11Server()?

? ? sk := make([]byte, hex.DecodedLen(len(os.Getenv("SK"))))
? ? hex.Decode(sk, []byte(os.Getenv("SK")))

? ? //*****************************************************************
	signInitRequest := &pb.SignInitRequest{
		Mech:? ? &pb.Mechanism{Mechanism: ep11.CKM_IBM_DILITHIUM},
		PrivKey:? sk,
	}
	signInitResponse, err := cryptoClient.SignInit(context.Background(), signInitRequest)
	if err != nil {
		panic(fmt.Errorf("SignInit error: %s", err))
	}

	signData := sha256.Sum256([]byte(os.Args[1]))
	signRequest := &pb.SignRequest{
		State: signInitResponse.State,
		Data:? signData[:],
	}

	SignResponse, err := cryptoClient.Sign(context.Background(), signRequest)
	if err != nil {
		panic(fmt.Errorf("Sign error: %s", err))
	}
?	//*****************************************************************
	
?	sign := make([]byte, hex.EncodedLen(len(SignResponse.Signature)))
? ? hex.Encode(sign, SignResponse.Signature)
? ? fmt.Println("signature: " +string(sign))
}        

You can generate the signature for a message string this way:

$ ./sign "Hello World"
signature: 9857d3880a4e5d9bad51cdceba3b99dc5e53bf41f39429963491031728b39d438e578fd0bb50c7fe938fbcf4283843a4dc3efaefc19ad7460c34cfa4bd2fcbd33676fb211bec07c352f94c2b563182924ca14584e1d499281bbaf3f6bd8f881bb09929c74b7839f0889905542b45a6daa83462573a97fc5c779fe16e565f15b83cfb725d0159abe39a591ed44d860cff7c6e7af61647922c154e4ece661e38a3b6847d4be009cde6fb4827ce7139dd4a63c6e47c5decfd784df2cf775dc334f44d82eb2b31564ef828beca5662215d38acd3104b1ea7f7cdb96c17b8dc5dd015b3eeda7ac9c5a30c224e74dfe9f2ec85910cd6a7ffe244d17f93ec30cab2d823bb0838b515acb8e661251f5bc6bb8542d038eeecd55036de06828e3562bcc93ca17959ed37f602b4d2bf745e63dec7a7c7f5228333da1f07ddc5ce59353f...
        

Be sure to export this signature as SIGN environment variable to pass it to the verify program.

$ export SIGN=9857d3880a4e5d9bad...(6 733 characters)        

Verifying the message signature

The following sample illustrates how to verify a message using Dilithium. I use environment variables PK to pass the Dilithium public key and SIGN for the previous (long) message signature.

func main()( ) 

? ? cryptoClient := getGrep11Server()
? ?	defer disconnectGrep11Server()?

? ? pk := make([]byte, hex.DecodedLen(len(os.Getenv("PK"))))
? ? hex.Decode(pk, []byte(os.Getenv("PK")))


? ? sign := make([]byte, hex.DecodedLen(len(os.Getenv("SIGN"))))
? ? hex.Decode(sign, []byte(os.Getenv("SIGN")))


	signData := sha256.Sum256([]byte(os.Args[1]))
?
? ? //*****************************************************************
?	verifyInitRequest := &pb.VerifyInitRequest{
		Mech:? ?&pb.Mechanism{Mechanism: ep11.CKM_IBM_DILITHIUM},
		PubKey: pk,
	}
	verifyInitResponse, err := cryptoClient.VerifyInit(context.Background(), verifyInitRequest)
	if err != nil {
		panic(fmt.Errorf("VerifyInit error: %s", err))
	}

	verifyRequest := &pb.VerifyRequest{
		State:? ? ?verifyInitResponse.State,
		Data:? ? ? []byte(signData[:]),
		Signature: sign,
	}

	_, err = cryptoClient.Verify(context.Background(), verifyRequest)
	if ok, ep11Status := Convert(err); !ok {
		if ep11Status.Code == ep11.CKR_SIGNATURE_INVALID {
			panic(fmt.Errorf("Invalid signature"))
		} else {
			panic(fmt.Errorf("Verify error: [%d]: %s", ep11Status.Code, ep11Status.Detail))
		}
	}
	fmt.Println("Verified")
	//*****************************************************************
	
}        

You can verify the signature for a message the following way:

$ ./verify "Hello World"

Verified 

$ ./verify "Hellow World" 

rpc error: code = Unknown desc = CKR_SIGNATURE_INVALID 
panic: Verify error: [5]: Error [*errors.prefixError]: [rpc error: code = Unknown desc = CKR_SIGNATURE_INVALID] 
        

Conclusion

The complete source of this sample is available at this link. Install the source code in your GO source director and use the make all command to compile the three program in your directory:

  • dilithium to generate the dilithium key pair.
  • sign to create a message string signature using a dilithium private key.
  • verify to verify a dilithium signature using the dilithium public key.

IBM provides GREP11 programming sample for Go programming language here.

If you want to know more on IBM Crypto adapter go here. And if you are interested by the IBM Hyper Protect appliance to run your appliance within TEE enclave, you can have a look at the RedBook I coauthored with my colleagues last year here.

An alternative is obviously to contact me.


Jean-Yves Girard - IBM Digital Asset architect

IBM ClientEngineering

Patrick Joussen

La Bastide de Sigalas

2 年

top ! tx jean-Yves

回复
Marc Bouzigues

IT Architect, Client Engineering EMEA, Hybrid Cloud - AI - Power

2 年

very good example ! Thanks JY ...

要查看或添加评论,请登录

Jean-Yves Girard的更多文章

社区洞察

其他会员也浏览了