Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CryptoAPI #539

Merged
merged 38 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9350394
Add Dilithium3 Java methods
hwupathum Feb 10, 2024
f484085
Add Dilithium3 bal methods
hwupathum Feb 10, 2024
890eff3
Add Kyber from Keystore methods
hwupathum Feb 13, 2024
0ef71fc
Add Kyber KEM methods
hwupathum Feb 13, 2024
22c33f2
Add Hybrid KEM methods
hwupathum Feb 14, 2024
71b50a6
Add hkdf Sha256
hwupathum Feb 29, 2024
df13979
Add kyber rsa hpke
hwupathum Mar 4, 2024
32ce9ff
Refactor code
hwupathum Mar 5, 2024
433718b
Add licence
hwupathum Mar 5, 2024
cde030f
Apply suggestions from code review
hwupathum Mar 19, 2024
934432b
Fix build failures
hwupathum Mar 19, 2024
0d981e6
Fix revirew comments
hwupathum Mar 20, 2024
07395b2
Change Dilithium name to ML-DSA
hwupathum Mar 20, 2024
e6e7032
Change Kyber name to ML-KEM
hwupathum Mar 20, 2024
87667d7
Add ML-DSA unit tests
hwupathum Mar 20, 2024
6d0980a
Add ML-KEM unit tests
hwupathum Mar 20, 2024
798af72
Improve code coverage
hwupathum Mar 21, 2024
a96c026
[Automated] Update the native jar versions
hwupathum Mar 21, 2024
194fa5a
Apply suggestions from code review
hwupathum Mar 21, 2024
53d18df
Update reflect-config.json for BouncyCastle version bump
hwupathum Mar 21, 2024
abaafca
Refactor getPrivateKey method
hwupathum Mar 22, 2024
bb6d435
Add encrypted PrivateKey for Kyber
hwupathum Mar 22, 2024
84e8a9f
Rename hpke method names
hwupathum Mar 22, 2024
14ad720
[Automated] Update the native jar versions
ashendes Mar 22, 2024
a9714a4
PQC crypto unit tests added.
ashendes Mar 25, 2024
c443312
Tests updated.
ashendes Mar 25, 2024
26ae1af
Merge pull request #1 from ashendes/pqc
hwupathum Mar 25, 2024
c62d3df
Improve code coverage
hwupathum Mar 25, 2024
eccb010
Merge branch 'master' into pqc
hwupathum Mar 26, 2024
d16bf78
Update GraalVM configurations
hwupathum Mar 26, 2024
c1e59b8
Merge remote-tracking branch 'origin/pqc' into pqc
hwupathum Mar 26, 2024
8a78ddb
[Automated] Update the native jar versions
hwupathum Mar 26, 2024
de42504
Bump version to 2.7.0
hwupathum Mar 26, 2024
244e228
Update CryptoAPI specs
hwupathum Mar 26, 2024
c9b48af
Update method descriptions
hwupathum Mar 26, 2024
340b8af
Updated changelog
hwupathum Mar 26, 2024
4d7d924
Add newline
hwupathum Mar 26, 2024
8afab76
Add separate functions to add crypto providers
hwupathum Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
hwupathum marked this conversation as resolved.
Show resolved Hide resolved

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
Loading