Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 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.9.1"
version = "2.10.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,8 +15,8 @@ graalvmCompatible = true
[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "crypto-native"
version = "2.9.1"
path = "../native/build/libs/crypto-native-2.9.1-SNAPSHOT.jar"
version = "2.10.0"
path = "../native/build/libs/crypto-native-2.10.0-SNAPSHOT.jar"

[[platform.java21.dependency]]
groupId = "org.bouncycastle"
Expand Down
2 changes: 1 addition & 1 deletion ballerina/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ id = "crypto-compiler-plugin"
class = "io.ballerina.stdlib.crypto.compiler.CryptoCompilerPlugin"

[[dependency]]
path = "../compiler-plugin/build/libs/crypto-compiler-plugin-2.9.1-SNAPSHOT.jar"
path = "../compiler-plugin/build/libs/crypto-compiler-plugin-2.10.0-SNAPSHOT.jar"
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ distribution-version = "2201.12.0"
[[package]]
org = "ballerina"
name = "crypto"
version = "2.9.1"
version = "2.10.0"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
Expand Down
56 changes: 49 additions & 7 deletions ballerina/sign_verify.bal
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,25 @@ public isolated function signRsaSha512(byte[] input, PrivateKey privateKey) retu
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;

# Returns the RSASSA-PSS with SHA-256 based signature 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:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword");
# byte[] signature = check crypto:signRsaSsaPss256(data, privateKey);
# ```
#
# + input - The content to be signed
# + privateKey - Private key used for signing
# + return - The generated signature or else a `crypto:Error` if the private key is invalid
public isolated function signRsaSsaPss256(byte[] input, PrivateKey privateKey) returns byte[]|Error = @java:Method {
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;

# Returns the SHA384withECDSA based signature value for the given data.
# ```ballerina
# string input = "Hello Ballerina";
Expand Down Expand Up @@ -195,7 +214,7 @@ public isolated function signSha256withEcdsa(byte[] input, PrivateKey privateKey
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifyRsaMd5Signature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifyRsaMd5Signature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;
Expand All @@ -219,7 +238,7 @@ public isolated function verifyRsaMd5Signature(byte[] data, byte[] signature, Pu
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifyMlDsa65Signature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifyMlDsa65Signature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;
Expand Down Expand Up @@ -267,7 +286,7 @@ public isolated function verifyRsaSha1Signature(byte[] data, byte[] signature, P
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifyRsaSha256Signature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifyRsaSha256Signature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;
Expand All @@ -291,7 +310,7 @@ public isolated function verifyRsaSha256Signature(byte[] data, byte[] signature,
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifyRsaSha384Signature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifyRsaSha384Signature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;
Expand All @@ -315,11 +334,34 @@ public isolated function verifyRsaSha384Signature(byte[] data, byte[] signature,
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifyRsaSha512Signature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifyRsaSha512Signature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;

# Verifies the RSASSA-PSS with SHA-256 based signature.
# ```ballerina
# string input = "Hello Ballerina";
# byte[] data = input.toBytes();
# crypto:KeyStore keyStore = {
# path: "/path/to/keyStore.p12",
# password: "keyStorePassword"
# };
# crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword");
# byte[] signature = check crypto:signRsaSsaPss256(data, privateKey);
# crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(keyStore, "keyAlias");
# boolean validity = check crypto:verifyRsaSsaPss256Signature(data, signature, publicKey);
# ```
#
# + data - The content to be verified
# + signature - Signature value
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifyRsaSsaPss256Signature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;

# Verifies the SHA384withECDSA based signature.
# ```ballerina
# string input = "Hello Ballerina";
Expand All @@ -339,7 +381,7 @@ public isolated function verifyRsaSha512Signature(byte[] data, byte[] signature,
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifySha384withEcdsaSignature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifySha384withEcdsaSignature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;
Expand All @@ -363,7 +405,7 @@ public isolated function verifySha384withEcdsaSignature(byte[] data, byte[] sign
# + publicKey - Public key used for verification
# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid
public isolated function verifySha256withEcdsaSignature(byte[] data, byte[] signature, PublicKey publicKey)
returns boolean|Error = @java:Method {
returns boolean|Error = @java:Method {
name: "verifySha256withEcdsaSignature",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign"
} external;
47 changes: 42 additions & 5 deletions ballerina/tests/sign_verify_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ isolated function testSignRsaSha512() returns Error? {
test:assertEquals(sha512Signature.toBase16(), expectedSha512Signature);
}

@test:Config {}
isolated function testSignRsaSsaPss256() returns Error? {
byte[] payload = "Ballerina test".toBytes();
KeyStore keyStore = {
path: KEYSTORE_PATH,
password: "ballerina"
};
PrivateKey privateKey = check decodeRsaPrivateKeyFromKeyStore(keyStore, "ballerina", "ballerina");
byte[] pssSignature = check signRsaSsaPss256(payload, privateKey);
test:assertTrue(pssSignature.length() > 0);
}

@test:Config {}
isolated function testSignMlDsa65() returns Error? {
byte[] payload = "Ballerina test".toBytes();
Expand Down Expand Up @@ -204,7 +216,7 @@ isolated function testSignMlDsa65() returns Error? {
@test:Config {}
isolated function testSignRsaMd5WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
PrivateKey privateKey = {algorithm:"RSA"};
PrivateKey privateKey = {algorithm: "RSA"};
byte[]|Error result = signRsaMd5(payload, privateKey);
if result is Error {
test:assertTrue(result.message().includes("Uninitialized private key:"));
Expand All @@ -216,7 +228,7 @@ isolated function testSignRsaMd5WithInvalidKey() {
@test:Config {}
isolated function testSignRsaSha1WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
PrivateKey privateKey = {algorithm:"RSA"};
PrivateKey privateKey = {algorithm: "RSA"};
byte[]|Error result = signRsaSha1(payload, privateKey);
if result is Error {
test:assertTrue(result.message().includes("Uninitialized private key:"));
Expand All @@ -228,7 +240,7 @@ isolated function testSignRsaSha1WithInvalidKey() {
@test:Config {}
isolated function testSignRsaSha256WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
PrivateKey privateKey = {algorithm:"RSA"};
PrivateKey privateKey = {algorithm: "RSA"};
byte[]|Error result = signRsaSha256(payload, privateKey);
if result is Error {
test:assertTrue(result.message().includes("Uninitialized private key:"));
Expand All @@ -240,7 +252,7 @@ isolated function testSignRsaSha256WithInvalidKey() {
@test:Config {}
isolated function testSignRsaSha384WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
PrivateKey privateKey = {algorithm:"RSA"};
PrivateKey privateKey = {algorithm: "RSA"};
byte[]|Error result = signRsaSha384(payload, privateKey);
if result is Error {
test:assertTrue(result.message().includes("Uninitialized private key:"));
Expand All @@ -252,7 +264,7 @@ isolated function testSignRsaSha384WithInvalidKey() {
@test:Config {}
isolated function testSignRsaSha512WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
PrivateKey privateKey = {algorithm:"RSA"};
PrivateKey privateKey = {algorithm: "RSA"};
byte[]|Error result = signRsaSha512(payload, privateKey);
if result is Error {
test:assertTrue(result.message().includes("Uninitialized private key:"));
Expand All @@ -261,6 +273,18 @@ isolated function testSignRsaSha512WithInvalidKey() {
}
}

@test:Config {}
isolated function testSignRsaSsaPss256WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
PrivateKey privateKey = {algorithm: "RSA"};
byte[]|Error result = signRsaSsaPss256(payload, privateKey);
if result is Error {
test:assertTrue(result.message().includes("Uninitialized private key:"));
} else {
test:assertFail("Expected error not found.");
}
}

@test:Config {}
isolated function testSignMlDsa65WithInvalidKey() {
byte[] payload = "Ballerina test".toBytes();
Expand Down Expand Up @@ -338,6 +362,19 @@ isolated function testVerifyRsaSha512() returns Error? {
test:assertTrue(check verifyRsaSha512Signature(payload, sha512Signature, publicKey));
}

@test:Config {}
isolated function testVerifyRsaSsaPss256() returns Error? {
byte[] payload = "Ballerina test".toBytes();
KeyStore keyStore = {
path: KEYSTORE_PATH,
password: "ballerina"
};
PrivateKey privateKey = check decodeRsaPrivateKeyFromKeyStore(keyStore, "ballerina", "ballerina");
PublicKey publicKey = check decodeRsaPublicKeyFromTrustStore(keyStore, "ballerina");
byte[] pssSignature = check signRsaSsaPss256(payload, privateKey);
test:assertTrue(check verifyRsaSsaPss256Signature(payload, pssSignature, publicKey));
}

@test:Config {}
isolated function testVerifySha384withEcdsa() returns Error? {
byte[] payload = "Ballerina test".toBytes();
Expand Down
14 changes: 11 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased]

### Added
- [Introduce support for RSASSA-PSS (PS256) signature algorithm](https://github.com/ballerina-platform/ballerina-library/issues/8292)
- [Introduce support for PBKDF2 password hashing and verification](https://github.com/ballerina-platform/ballerina-lang/issues/43926)
- [Add static analysis rule - Encryption algorithms should be used with secure mode and padding scheme](https://github.com/ballerina-platform/ballerina-library/issues/7940)
- [Add static analysis rule - Passwords should not be stored in plaintext or with a fast hashing algorithm](https://github.com/ballerina-platform/ballerina-library/issues/7950)
- [Add static analysis rule - Counter Mode initialization vectors should not be reused](https://github.com/ballerina-platform/ballerina-library/issues/8010)

### Changed
- [Update OIDS of NIST approved post quantum algorithms](https://github.com/ballerina-platform/ballerina-library/issues/7678)
- [Optimize hardcoded IV detection using semantic model reference counting](https://github.com/ballerina-platform/ballerina-library/issues/8257)
## [2.9.1] - 2025-09-29

### Fixed
- [Implement optional close method check for BStream](https://github.com/ballerina-platform/ballerina-library/issues/8288)

## [2.9.0] - 2025-03-12

### Changed
- [Update OIDS of NIST approved post quantum algorithms](https://github.com/ballerina-platform/ballerina-library/issues/7678)


## [2.8.0] - 2025-02-11

### Added
Expand Down
13 changes: 13 additions & 0 deletions compiler-plugin-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ plugins {
id 'java'
id 'checkstyle'
id 'com.github.spotbugs'
id 'jacoco'
}

description = 'Ballerina - Crypto Compiler Plugin Tests'
Expand Down Expand Up @@ -61,6 +62,18 @@ test {
}
}
}
jacoco {
destinationFile = file("$buildDir/jacoco/test.exec")
}
finalizedBy jacocoTestReport
}

jacocoTestReport {
dependsOn test
reports {
xml.required = true
}
sourceSets project(':crypto-compiler-plugin').sourceSets.main
}

spotbugsTest {
Expand Down
Loading