-
Notifications
You must be signed in to change notification settings - Fork 35
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
Add support for PGP Encryption/Decryption #555
Add support for PGP Encryption/Decryption #555
Conversation
ballerina/pgp_utils.bal
Outdated
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
public type PgpEncryptionOptions record {| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add API doc comments to all public types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we just rename this to Options
?
public isolated function encryptPgp(byte[] plainText, byte[] publicKey, *Options options)
returns byte[]|Error = @java:Method {
IMO, the words pgp
and encrypt
are redundant as this API name itself provides an understanding that we are going to do a PGP encryption. So it is obvious that these options
are related to PGP encryption.
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we can reduce the redundancy by renaming it to Options
. I have addressed it here 557f72b
@@ -34,6 +40,11 @@ | |||
*/ | |||
public class Encrypt { | |||
|
|||
public static final String COMPRESSION_ALGORITHM = "compressionAlgorithm"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these constants are anyway converted to BString
its better to use following approach. Same for other constants as well.
public static final String COMPRESSION_ALGORITHM = "compressionAlgorithm"; | |
public static final BString COMPRESSION_ALGORITHM = StringUtils.fromString("compressionAlgorithm"); |
And if there constants are not use anywhere other than here, its better to mark them as private
as well.
public static Object encryptPgp(BArray plainTextValue, BArray publicKeyValue, BMap options) { | ||
byte[] plainText = plainTextValue.getBytes(); | ||
byte[] publicKey = publicKeyValue.getBytes(); | ||
InputStream publicKeyStream = new ByteArrayInputStream(publicKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we create InputStream
the best practice is to close them once the required work is done. And I think it is better to do that in the same place where we create the stream using a try-with-resources
block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have addressed this in 78e3fb4
byte[] cipherText = cipherTextValue.getBytes(); | ||
byte[] privateKey = privateKeyValue.getBytes(); | ||
byte[] passphraseInBytes = passphrase.getBytes(); | ||
InputStream keyStream = new ByteArrayInputStream(privateKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check whether this stream is properly closed. This comment [1] is valid here as well.
[1] - https://github.com/ballerina-platform/module-ballerina-crypto/pull/555/files#r1596194670
compressedDataGenerator.close(); | ||
cipherOutStream.close(); | ||
encryptOut.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than manually doing this it is better to use try-with-resources
block to close the created IO streams.
PGPPublicKeyEncryptedData publicKeyEncryptedData = null; | ||
|
||
Iterator<PGPEncryptedData> encryptedDataItr = pgpEncryptedDataList.getEncryptedDataObjects(); | ||
while (pgpPrivateKey == null && encryptedDataItr.hasNext()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while (pgpPrivateKey == null && encryptedDataItr.hasNext()) { | |
while (Objects.isNull(pgpPrivateKey) && encryptedDataItr.hasNext()) { |
throw new PGPException("Could not generate PGPPublicKeyEncryptedData object"); | ||
} | ||
|
||
if (pgpPrivateKey == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (pgpPrivateKey == null) { | |
if (Objects.isNull(pgpPrivateKey)) { |
|
||
private PGPPrivateKey findSecretKey(long keyID) throws PGPException { | ||
PGPSecretKey pgpSecretKey = pgpSecretKeyRingCollection.getSecretKey(keyID); | ||
return pgpSecretKey == null ? null : pgpSecretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than returning null
here can't we use Optionals [1] ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we can, and I have addressed this in 78e3fb4
PgpDecryptionGenerator pgpDecryptionGenerator = new PgpDecryptionGenerator( | ||
keyStream, | ||
passphraseInBytes | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PgpDecryptionGenerator pgpDecryptionGenerator = new PgpDecryptionGenerator( | |
keyStream, | |
passphraseInBytes | |
); | |
PgpDecryptionGenerator pgpDecryptionGenerator = new PgpDecryptionGenerator( | |
keyStream, passphraseInBytes); |
} | ||
} | ||
|
||
private static Optional<PGPPublicKey> extractPGPKeyFromRing(PGPPublicKeyRing pgpPublicKeyRing) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we rename this function to extractPgpKeyFromRing
ballerina/encrypt_decrypt.bal
Outdated
# + publicKey - Public key | ||
# + options - PGP encryption options | ||
# + return - Encrypted data or else a `crypto:Error` if the key is invalid | ||
public isolated function encryptPgp(byte[] plainText, byte[] publicKey, *PgpEncryptionOptions options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's take in the publicKey
and privateKey
as file paths for the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same should applied for the params privateKey
and passphrase
in decryptPgp
as well right? Should we keep the return type to byte []
as it is for those two methods encryptPgp
and decryptPgp
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have privateKey
as a string file path in decryptPgp
function as well. The passphrase should be a byte[] and what we return from the APIs also should be byte[] s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have addressed this in 78e3fb4
ballerina/pgp_utils.bal
Outdated
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
# Represents the PGP encryption options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Represents the PGP encryption options | |
# Represents the PGP encryption options. |
ballerina/pgp_utils.bal
Outdated
boolean withIntegrityCheck = true; | ||
|}; | ||
|
||
# Represents the compressions algorithms available in PGP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Represents the compressions algorithms available in PGP | |
# Represents the compression algorithms available in PGP. |
ballerina/pgp_utils.bal
Outdated
BZIP2= "3" | ||
} | ||
|
||
# Represent the symmetric key algorithms available in PGP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Represent the symmetric key algorithms available in PGP | |
# Represent the symmetric key algorithms available in PGP. |
native/src/main/java/io/ballerina/stdlib/crypto/PgpDecryptionGenerator.java
Show resolved
Hide resolved
native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java
Show resolved
Hide resolved
native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java
Outdated
Show resolved
Hide resolved
native/src/main/java/io/ballerina/stdlib/crypto/PgpEncryptionGenerator.java
Outdated
Show resolved
Hide resolved
byte[] cipherText = check encryptPgp(message, publicKey, symmetricKeyAlgorithm = AES_128, armor = false); | ||
byte[] plainText = check decryptPgp(cipherText, privateKey, passphrase); | ||
test:assertEquals(plainText.toBase16(), message.toBase16()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we also add a negative test case by creating another PGP key pair, like encrypting with a public key and trying to decrypt using a wrong private key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added two more test cases for following in here 78e3fb4
- Invalid Private key for decryption with it's passphrase
- Valid Private key for decryption with invalid passphrase
if plainText is Error { | ||
test:assertEquals(plainText.message(), "Error occurred while PGP decrypt: Could Not Extract private key"); | ||
} else { | ||
test:assertTrue(false, "Should return a crypto Error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test:assertTrue(false, "Should return a crypto Error"); | |
test:assertFail("Should return a crypto Error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have addressed this in f24bd9a
test:assertEquals(plainText.message(), | ||
"Error occurred while PGP decrypt: checksum mismatch at in checksum of 20 bytes"); | ||
} else { | ||
test:assertTrue(false, "Should return a crypto Error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been addressed in f24bd9a
Quality Gate passedIssues Measures |
Purpose
To Fix : ballerina-platform/ballerina-library#6462
Examples
Checklist