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 11 Feature] Add support for PGP encryption/decryption with streams #567

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6a7cb9b
Migrate to Java 21
ravinperera00 Sep 30, 2024
5ecc9ab
Merge pull request #566 from ravinperera00/java21
warunalakshitha Sep 30, 2024
0c230e2
[Automated] Update the native jar versions
TharmiganK Oct 2, 2024
85c3173
[Automated] Update the native jar versions
TharmiganK Oct 2, 2024
3ac65fd
Add PGP encrypt/decrypt functions with files
TharmiganK Oct 2, 2024
0fbae6b
Apply suggestions from code review
TharmiganK Oct 2, 2024
ae7af8c
[Automated] Update the native jar versions
TharmiganK Oct 2, 2024
3deeba8
Add test cases
TharmiganK Oct 2, 2024
c930edd
Update change log
TharmiganK Oct 2, 2024
04ba4ca
Update spec
TharmiganK Oct 2, 2024
3654143
Update to next minor version
TharmiganK Oct 2, 2024
1fa0552
[Automated] Update the native jar versions
TharmiganK Oct 2, 2024
95f34c1
[Automated] Update the native jar versions
TharmiganK Oct 2, 2024
957f623
Add IO test dependency
TharmiganK Oct 2, 2024
5ba88d7
Add suggestions from review
TharmiganK Oct 2, 2024
e207f95
Change error message
TharmiganK Oct 2, 2024
18ff4cc
Update time version
TharmiganK Oct 2, 2024
95c6beb
Add stream pgp decrypt support
TharmiganK Oct 7, 2024
7c584bb
Fix casting issue
TharmiganK Oct 7, 2024
d09e235
[Automated] Update the native jar versions
TharmiganK Oct 8, 2024
1126113
[Automated] Update the native jar versions
TharmiganK Oct 8, 2024
ea708af
Add stream pgp encrypt support
TharmiganK Oct 9, 2024
7094e49
Replace PipedStreams with a custom implementation
TharmiganK Oct 9, 2024
b635c45
Remove PGP file APIs
TharmiganK Oct 9, 2024
9cdd933
Fix test cases
TharmiganK Oct 9, 2024
29333dc
Update change log
TharmiganK Oct 9, 2024
f71ebb7
Update spec
TharmiganK Oct 9, 2024
9169721
Update ballerina invoke call
TharmiganK Oct 9, 2024
be0f1cb
Merge remote-tracking branch 'origin/java21' into pgp-files
TharmiganK Oct 9, 2024
1ba983d
[Automated] Update the native jar versions
TharmiganK Oct 9, 2024
132cd31
[Automated] Update the native jar versions
TharmiganK Oct 9, 2024
a51aa97
[Automated] Update the native jar versions
TharmiganK Oct 9, 2024
7139227
Update dependency versions supported with Java 21
TharmiganK Oct 9, 2024
43b3903
Rename test functions
TharmiganK Oct 9, 2024
7c429ca
Add license header
TharmiganK Oct 9, 2024
9d529a4
Add module prefix
TharmiganK Oct 9, 2024
6902dbe
Address sonar cloud issues
TharmiganK Oct 9, 2024
534c5a0
Fix integrity check
TharmiganK Oct 9, 2024
a443409
Apply suggestions from code review
TharmiganK Oct 10, 2024
c792890
Remove unused constants
TharmiganK Oct 10, 2024
0f85fef
Rename PGP stream APIs
TharmiganK Oct 11, 2024
c9aca36
Merge remote-tracking branch 'origin/master' into pgp-files
TharmiganK Oct 11, 2024
03c6d8f
Apply suggestions from code review
TharmiganK Oct 11, 2024
f019b54
Add missing doc for options
TharmiganK Oct 14, 2024
db345a0
Replace experimental graalvm options
TharmiganK Oct 14, 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
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.7.2"
version = "2.7.3"
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
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.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "crypto-native"
version = "2.7.2"
path = "../native/build/libs/crypto-native-2.7.2.jar"
version = "2.7.3"
path = "../native/build/libs/crypto-native-2.7.3-SNAPSHOT.jar"

[[platform.java17.dependency]]
groupId = "org.bouncycastle"
Expand Down
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.9.0"
[[package]]
org = "ballerina"
name = "crypto"
version = "2.7.2"
version = "2.7.3"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.array"},
Expand Down
31 changes: 31 additions & 0 deletions ballerina/encrypt_decrypt.bal
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,21 @@
'class: "io.ballerina.stdlib.crypto.nativeimpl.Encrypt"
} external;

# Writes the PGP-encrypted value of the given data to a file specified by the output file path.
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
# ```ballerina
# check crypto:encryptPgpAsFile("input.txt", "public_key.asc", "output.txt");
# ```
#
# + inputFilePath - Path to the input file
# + publicKeyPath - Path to the public key
# + outputFilePath - Path to the output file
# + options - PGP encryption options
# + return - A `crypto:Error` will be returned if the process fails
public isolated function encryptPgpAsFile(string inputFilePath, string publicKeyPath, string outputFilePath,
*Options options) returns Error? = @java:Method {
'class: "io.ballerina.stdlib.crypto.nativeimpl.Encrypt"
} external;

Check warning on line 276 in ballerina/encrypt_decrypt.bal

View check run for this annotation

Codecov / codecov/patch

ballerina/encrypt_decrypt.bal#L276

Added line #L276 was not covered by tests

# Returns the PGP-decrypted value of the given PGP-encrypted data.
# ```ballerina
# byte[] message = "Hello Ballerina!".toBytes();
Expand All @@ -278,3 +293,19 @@
name: "decryptPgp",
'class: "io.ballerina.stdlib.crypto.nativeimpl.Decrypt"
} external;

# Writes the PGP-decrypted value of the given data to a file specified by the output file path.
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
# ```ballerina
# byte[] passphrase = check io:fileReadBytes("pass_phrase.txt");
# check crypto:decryptPgpAsFile("input.txt", "private_key.asc", passphrase, "output.txt");
# ```
#
# + inputFilePath - Path to the input file
# + privateKeyPath - Path to the private key
# + passphrase - passphrase of the private key
# + outputFilePath - Path to the output file
# + return - A `crypto:Error` will be returned if the process fails
public isolated function decryptPgpAsFile(string inputFilePath, string privateKeyPath, byte[] passphrase,
string outputFilePath) returns Error? = @java:Method {
'class: "io.ballerina.stdlib.crypto.nativeimpl.Decrypt"
} external;

Check warning on line 311 in ballerina/encrypt_decrypt.bal

View check run for this annotation

Codecov / codecov/patch

ballerina/encrypt_decrypt.bal#L311

Added line #L311 was not covered by tests
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Security;
import java.util.Iterator;
import java.util.Objects;
Expand Down Expand Up @@ -123,6 +125,12 @@
}
}

public void decrypt(InputStream encryptedIn, String outputPath) throws PGPException, IOException {
try (OutputStream outputStream = Files.newOutputStream(Path.of(outputPath))) {
decryptStream(encryptedIn, outputStream);

Check warning on line 130 in native/src/main/java/io/ballerina/stdlib/crypto/PgpDecryptionGenerator.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/PgpDecryptionGenerator.java#L129-L130

Added lines #L129 - L130 were not covered by tests
}
}

Check warning on line 132 in native/src/main/java/io/ballerina/stdlib/crypto/PgpDecryptionGenerator.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/PgpDecryptionGenerator.java#L132

Added line #L132 was not covered by tests

private static void decrypt(OutputStream clearOut, Optional<PGPPrivateKey> pgpPrivateKey,
PGPPublicKeyEncryptedData publicKeyEncryptedData) throws IOException, PGPException {
if (pgpPrivateKey.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.security.Security;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -76,7 +78,7 @@
this.withIntegrityCheck = withIntegrityCheck;
}

private void encryptStream(OutputStream encryptOut, InputStream clearIn, long length, InputStream publicKeyIn)
private void encryptStream(OutputStream encryptOut, InputStream clearIn, InputStream publicKeyIn)
throws IOException, PGPException {
PGPCompressedDataGenerator compressedDataGenerator =
new PGPCompressedDataGenerator(compressionAlgorithm);
Expand All @@ -95,7 +97,7 @@
}

try (OutputStream cipherOutStream = pgpEncryptedDataGenerator.open(encryptOut, new byte[BUFFER_SIZE])) {
copyAsLiteralData(compressedDataGenerator.open(cipherOutStream), clearIn, length);
copyAsLiteralData(compressedDataGenerator.open(cipherOutStream), clearIn);
compressedDataGenerator.close();
}
encryptOut.close();
Expand All @@ -105,11 +107,18 @@
public Object encrypt(byte[] clearData, InputStream publicKeyIn) throws PGPException, IOException {
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(clearData);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
encryptStream(outputStream, inputStream, clearData.length, publicKeyIn);
encryptStream(outputStream, inputStream, publicKeyIn);
return ValueCreator.createArrayValue(outputStream.toByteArray());
}
}

public void encrypt(InputStream inputStream, InputStream publicKeyIn, String outputPath)
throws PGPException, IOException {
try (OutputStream outputStream = Files.newOutputStream(Path.of(outputPath))) {
encryptStream(outputStream, inputStream, publicKeyIn);

Check warning on line 118 in native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java#L117-L118

Added lines #L117 - L118 were not covered by tests
}
}

Check warning on line 120 in native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java#L120

Added line #L120 was not covered by tests

private static PGPPublicKey getPublicKey(InputStream keyInputStream) throws IOException, PGPException {
PGPPublicKeyRingCollection pgpPublicKeyRings = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(keyInputStream), new JcaKeyFingerprintCalculator());
Expand All @@ -124,7 +133,7 @@
throw new PGPException("Invalid public key");
}

private static void copyAsLiteralData(OutputStream outputStream, InputStream in, long length)
private static void copyAsLiteralData(OutputStream outputStream, InputStream in)
throws IOException {
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
byte[] buff = new byte[PgpEncryptionGenerator.BUFFER_SIZE];
Expand All @@ -133,10 +142,8 @@
InputStream inputStream = in) {

int len;
long totalBytesWritten = 0L;
while (totalBytesWritten <= length && (len = inputStream.read(buff)) > 0) {
while ((len = inputStream.read(buff)) > 0) {
pOut.write(buff, 0, len);
totalBytesWritten += len;
}
} finally {
Arrays.fill(buff, (byte) 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,24 @@
return CryptoUtils.createError("Error occurred while PGP decrypt: " + e.getMessage());
}
}

public static Object decryptPgpAsFile(BString inputFilePath, BString privateKeyPath, BArray passphrase,
BString outputFilePath) {
byte[] passphraseInBytes = passphrase.getBytes();

Check warning on line 106 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decrypt.java#L106

Added line #L106 was not covered by tests
byte[] privateKey;
try {
privateKey = Files.readAllBytes(Path.of(privateKeyPath.toString()));
} catch (IOException e) {
return CryptoUtils.createError("Error occurred while reading public key: " + e.getMessage());
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 112 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decrypt.java#L109-L112

Added lines #L109 - L112 were not covered by tests

try (InputStream keyStream = new ByteArrayInputStream(privateKey);
InputStream cipherTextStream = Files.newInputStream(Path.of(inputFilePath.toString()))) {
PgpDecryptionGenerator pgpDecryptionGenerator = new PgpDecryptionGenerator(keyStream, passphraseInBytes);
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
pgpDecryptionGenerator.decrypt(cipherTextStream, outputFilePath.getValue());
return null;
} catch (IOException | PGPException e) {
return CryptoUtils.createError("Error occurred while PGP decrypt: " + e.getMessage());

Check warning on line 120 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decrypt.java#L114-L120

Added lines #L114 - L120 were not covered by tests
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,29 @@
return CryptoUtils.createError("Error occurred while PGP encrypt: " + e.getMessage());
}
}

public static Object encryptPgpAsFile(BString inputFilePath, BString publicKeyPath, BString outputFilePath,
BMap options) {
byte[] publicKey;
try {
publicKey = Files.readAllBytes(Path.of(publicKeyPath.toString()));
} catch (IOException e) {
return CryptoUtils.createError("Error occurred while reading public key: " + e.getMessage());
}

Check warning on line 127 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java#L124-L127

Added lines #L124 - L127 were not covered by tests

try (InputStream publicKeyStream = new ByteArrayInputStream(publicKey);
InputStream inputStream = Files.newInputStream(Path.of(inputFilePath.toString()))

Check warning on line 130 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java#L129-L130

Added lines #L129 - L130 were not covered by tests
) {
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
PgpEncryptionGenerator pgpEncryptionGenerator = new PgpEncryptionGenerator(
Integer.parseInt(options.get(COMPRESSION_ALGORITHM).toString()),
Integer.parseInt(options.get(SYMMETRIC_KEY_ALGORITHM).toString()),
Boolean.parseBoolean(options.get(ARMOR).toString()),
Boolean.parseBoolean(options.get(WITH_INTEGRITY_CHECK).toString())

Check warning on line 136 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java#L132-L136

Added lines #L132 - L136 were not covered by tests
);
pgpEncryptionGenerator.encrypt(inputStream, publicKeyStream, outputFilePath.getValue());
return null;
} catch (IOException | PGPException e) {
return CryptoUtils.createError("Error occurred while PGP encrypt: " + e.getMessage());

Check warning on line 141 in native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Encrypt.java#L138-L141

Added lines #L138 - L141 were not covered by tests
}
}
}
Loading