Skip to content

Commit

Permalink
Merge pull request #539 from hwupathum/pqc
Browse files Browse the repository at this point in the history
Update CryptoAPI
  • Loading branch information
Bhashinee authored Mar 27, 2024
2 parents 059e361 + 8afab76 commit 9063286
Show file tree
Hide file tree
Showing 32 changed files with 2,522 additions and 19 deletions.
14 changes: 7 additions & 7 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "crypto"
version = "2.6.2"
version = "2.7.0"
authors = ["Ballerina"]
keywords = ["security", "hash", "hmac", "sign", "encrypt", "decrypt", "private key", "public key"]
repository = "https://github.com/ballerina-platform/module-ballerina-crypto"
Expand All @@ -15,17 +15,17 @@ graalvmCompatible = true
[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "crypto-native"
version = "2.6.2"
path = "../native/build/libs/crypto-native-2.6.2.jar"
version = "2.7.0"
path = "../native/build/libs/crypto-native-2.7.0.jar"

[[platform.java17.dependency]]
groupId = "org.bouncycastle"
artifactId = "bcpkix-jdk18on"
version = "1.74"
path = "./lib/bcpkix-jdk18on-1.74.jar"
version = "1.77"
path = "./lib/bcpkix-jdk18on-1.77.jar"

[[platform.java17.dependency]]
groupId = "org.bouncycastle"
artifactId = "bcprov-jdk18on"
version = "1.74"
path = "./lib/bcprov-jdk18on-1.74.jar"
version = "1.77"
path = "./lib/bcprov-jdk18on-1.77.jar"
32 changes: 31 additions & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ distribution-version = "2201.8.0"
[[package]]
org = "ballerina"
name = "crypto"
version = "2.6.2"
version = "2.7.0"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.array"},
{org = "ballerina", name = "test"},
{org = "ballerina", name = "time"}
]
Expand All @@ -28,6 +29,29 @@ modules = [
{org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"}
]

[[package]]
org = "ballerina"
name = "lang.__internal"
version = "0.0.0"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.object"}
]

[[package]]
org = "ballerina"
name = "lang.array"
version = "0.0.0"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.__internal"}
]
modules = [
{org = "ballerina", packageName = "lang.array", moduleName = "lang.array"}
]

[[package]]
org = "ballerina"
name = "lang.error"
Expand All @@ -37,6 +61,12 @@ dependencies = [
{org = "ballerina", name = "jballerina.java"}
]

[[package]]
org = "ballerina"
name = "lang.object"
version = "0.0.0"
scope = "testOnly"

[[package]]
org = "ballerina"
name = "test"
Expand Down
15 changes: 14 additions & 1 deletion ballerina/Module.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,17 @@ The `crypto` module supports both symmetric key encryption/decryption and asymme

### Sign and verify

The `crypto` module supports signing data using the RSA private key and verification of the signature using the RSA public key. This supports MD5, SHA1, SHA256, SHA384, and SHA512 digesting algorithms as well.
The `crypto` module supports signing data using the RSA private key and verification of the signature using the RSA public key. This supports MD5, SHA1, SHA256, SHA384, and SHA512 digesting algorithms, and ML-DSA-65 post-quantum signature algorithm as well.

### Key Derivation Functions (KDF)

The `crypto` module supports HMAC-based Key Derivation Function (HKDF). HKDF is a key derivation function that uses a Hash-based Message Authentication Code (HMAC) to derive keys.

### Key Exchange Mechanisms (KEM)

The `crypto` module supports Key Exchange Mechanisms (KEM). It includes RSA-KEM and post-quantum ML-KEM-768 for both encapsulation and decapsulation.

### Hybrid Public Key Encryption (HPKE)

The `crypto` module supports Hybrid Public Key Encryption (HPKE). It supportspost-quantum ML-KEM-768-HPKE and RSA-KEM-ML-KEM-768-HPKE for encryption and decryption.

14 changes: 13 additions & 1 deletion ballerina/Package.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,19 @@ The `crypto` package supports both symmetric key encryption/decryption and asymm

### Sign and verify

The `crypto` package supports signing data using the RSA private key and verification of the signature using the RSA public key. This supports MD5, SHA1, SHA256, SHA384, and SHA512 digesting algorithms as well.
The `crypto` package supports signing data using the RSA private key and verification of the signature using the RSA public key. This supports MD5, SHA1, SHA256, SHA384, and SHA512 digesting algorithms, and ML-DSA-65 post-quantum signature algorithm as well.

### Key Derivation Functions (KDF)

The `crypto` package supports HMAC-based Key Derivation Function (HKDF). HKDF is a key derivation function that uses a Hash-based Message Authentication Code (HMAC) to derive keys.

### Key Exchange Mechanisms (KEM)

The `crypto` package supports Key Exchange Mechanisms (KEM). It includes RSA-KEM and post-quantum ML-KEM-768 for both encapsulation and decapsulation.

### Hybrid Public Key Encryption (HPKE)

The `crypto` package supports Hybrid Public Key Encryption (HPKE). It supportspost-quantum ML-KEM-768-HPKE and RSA-KEM-ML-KEM-768-HPKE for encryption and decryption.

## Report issues

Expand Down
143 changes: 143 additions & 0 deletions ballerina/hpke.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) 2024 WSO2 LLC. (https://www.wso2.com).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

# Represent the supported symmetric key sizes for AES algorithm.
public type AesKeySize 16|24|32;

# Represents the encapsulated secret and the ciphertext used in Hybrid Public Key Encryption (HPKE).
#
# + encapsulatedSecret - The encapsulated secret
# + cipherText - The encrypted data
public type HybridEncryptionResult record {|
byte[] encapsulatedSecret;
byte[] cipherText;
|};

# Returns the ML-KEM-768-AES-hybrid-encrypted value for the given data.
# ```ballerina
# string input = "Hello Ballerina";
# byte[] data = input.toBytes();
# crypto:KeyStore keyStore = {
# path: "/path/to/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:PublicKey publicKey = check crypto:decodeMlKem768PublicKeyFromTrustStore(keyStore, "keyAlias");
# crypto:HybridEncryptionResult encryptionResult = crypto:encryptMlKem768Hpke(data, publicKey);
# ```
# + input - The content to be encrypted
# + publicKey - Public key used for encryption
# + symmetricKeySize - The length of the symmetric key (in bytes)
# + return - Encrypted data or else a `crypto:Error` if an error occurs
public isolated function encryptMlKem768Hpke(byte[] input, PublicKey publicKey, AesKeySize symmetricKeySize = 32) returns HybridEncryptionResult|Error {
EncapsulationResult encapsulationResult = check encapsulateMlKem768(publicKey);
byte[] sharedSecret = check hkdfSha256(encapsulationResult.sharedSecret, symmetricKeySize);
byte[] encapsulatedSecret = encapsulationResult.encapsulatedSecret;
byte[] ciphertext = check encryptAesEcb(input, sharedSecret);
return {
encapsulatedSecret: encapsulatedSecret,
cipherText: ciphertext
};
}

# Returns the ML-KEM-768-AES-hybrid-encrypted value for the given encrypted data.
# ```ballerina
# string input = "Hello Ballerina";
# byte[] data = input.toBytes();
# crypto:KeyStore keyStore = {
# path: "/path/to/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:PublicKey publicKey = check crypto:decodeMlKem768PublicKeyFromTrustStore(keyStore, "keyAlias");
# crypto:HybridEncryptionResult encryptionResult = crypto:encryptMlKem768Hpke(data, publicKey);
# byte[] cipherText = encryptionResult.cipherText;
# byte[] encapsulatedKey = encryptionResult.encapsulatedSecret;
# crypto:PrivateKey privateKey = check crypto:decodeMlKem768PrivateKeyFromKeyStore(keyStore, "keyAlias");
# byte[] decryptedData = check crypto:decryptMlKem768Hpke(cipherText, encapsulatedKey, privateKey);
# ```
# + input - The content to be decrypted
# + encapsulatedKey - The encapsulated secret
# + privateKey - The MlKem private key used for decryption
# + symmetricKeySize - The length of the symmetric key (in bytes)
# + return - Decrypted data or else a `crypto:Error` if error occurs
public isolated function decryptMlKem768Hpke(byte[] input, byte[] encapsulatedKey, PrivateKey privateKey, AesKeySize symmetricKeySize = 32) returns byte[]|Error {
byte[] key = check decapsulateMlKem768(encapsulatedKey, privateKey);
key = check hkdfSha256(key, symmetricKeySize);
return check decryptAesEcb(input, key);
}

# Returns the RSA-KEM-ML-KEM-768-AES-hybrid-encrypted value for the given data.
# ```ballerina
# string input = "Hello Ballerina";
# byte[] data = input.toBytes();
# crypto:KeyStore mlkemKeyStore = {
# path: "/path/to/mlkem/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:KeyStore rsaKeyStore = {
# path: "/path/to/rsa/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:PublicKey mlkemPublicKey = check crypto:decodeMlKem768PublicKeyFromTrustStore(mlkemKeyStore, "keyAlias");
# crypto:PublicKey rsaPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(rsaKeyStore, "keyAlias");
# crypto:HybridEncryptionResult encryptionResult = crypto:encryptRsaKemMlKem768Hpke(data, rsaPublicKey, mlkemPublicKey);
# ```
# + input - The content to be encrypted
# + rsaPublicKey - The RSA public key used for encryption
# + mlkemPublicKey - The MlKem public key used for encryption
# + symmetricKeySize - The length of the symmetric key (in bytes)
# + return - Encrypted data or else a `crypto:Error` if an error occurs
public isolated function encryptRsaKemMlKem768Hpke(byte[] input, PublicKey rsaPublicKey, PublicKey mlkemPublicKey, AesKeySize symmetricKeySize = 32) returns HybridEncryptionResult|Error {
EncapsulationResult hybridEncapsulationResult = check encapsulateRsaKemMlKem768(rsaPublicKey, mlkemPublicKey);
byte[] sharedSecret = check hkdfSha256(hybridEncapsulationResult.sharedSecret, symmetricKeySize);
byte[] ciphertext = check encryptAesEcb(input, sharedSecret);
return {
encapsulatedSecret: hybridEncapsulationResult.encapsulatedSecret,
cipherText: ciphertext
};
}

# Returns the RSA-KEM-ML-KEM-768-AES-hybrid-encrypted value for the given encrypted data.
# ```ballerina
# string input = "Hello Ballerina";
# byte[] data = input.toBytes();
# crypto:KeyStore mlkemKeyStore = {
# path: "/path/to/mlkem/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:KeyStore rsaKeyStore = {
# path: "/path/to/rsa/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:PublicKey mlkemPublicKey = check crypto:decodeMlKem768PublicKeyFromTrustStore(mlkemKeyStore, "keyAlias");
# crypto:PublicKey rsaPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(rsaKeyStore, "keyAlias");
# crypto:HybridEncryptionResult encryptionResult = crypto:encryptRsaKemMlKem768Hpke(data, rsaPublicKey, mlkemPublicKey);
# byte[] cipherText = encryptionResult.cipherText;
# byte[] encapsulatedKey = encryptionResult.encapsulatedSecret;
# crypto:PrivateKey mlkemPrivateKey = check crypto:decodeMlKem768PrivateKeyFromKeyStore(mlkemKeyStore, "keyAlias");
# crypto:PrivateKey rsaPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(rsaKeyStore, "keyAlias");
# byte[] decryptedData = check crypto:decryptRsaKemMlKem768Hpke(cipherText, encapsulatedKey, rsaPrivateKey, mlkemPrivateKey);
# ```
# + input - The content to be decrypted
# + encapsulatedKey - The encapsulated secret
# + rsaPrivateKey - The RSA private key used for decryption
# + mlkemPrivateKey - The MlKem private key used for decryption
# + symmetricKeySize - The length of the symmetric key (in bytes)
# + return - Decrypted data or else a `crypto:Error` if error occurs
public isolated function decryptRsaKemMlKem768Hpke(byte[] input, byte[] encapsulatedKey, PrivateKey rsaPrivateKey, PrivateKey mlkemPrivateKey, AesKeySize symmetricKeySize = 32) returns byte[]|Error {
byte[] key = check decapsulateRsaKemMlKem768(encapsulatedKey, rsaPrivateKey, mlkemPrivateKey);
key = check hkdfSha256(key, symmetricKeySize);
return check decryptAesEcb(input, key);
}
33 changes: 33 additions & 0 deletions ballerina/kdf.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2024 WSO2 LLC. (https://www.wso2.com).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/jballerina.java;

# Returns HKDF (HMAC-based Key Derivation Function) using SHA-256 as the hash function.
# ```ballerina
# string secret = "some-secret";
# byte[] key = secret.toBytes();
# byte[] hash = crypto:hkdfSha256(key, 32);
# ```
# + input - The input key material to derive the key from
# + length - The length of the output keying material (OKM) in bytes
# + salt - Optional salt value, a non-secret random value
# + info - Optional context and application-specific information
# + return - The derived keying material (OKM) of the specified length
public isolated function hkdfSha256(byte[] input, int length, byte[] salt = [], byte[] info = []) returns byte[]|Error = @java:Method {
name: "hkdfSha256",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Kdf"
} external;
Loading

0 comments on commit 9063286

Please sign in to comment.