• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pgpainless / sop-java / #53

17 Jun 2025 11:45AM UTC coverage: 58.411% (-2.4%) from 60.782%
#53

push

other

vanitasvitae
SOP-Java 14.0.1-SNAPSHOT

2073 of 3549 relevant lines covered (58.41%)

0.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

27.12
/sop-java-testfixtures/src/main/java/sop/testsuite/operation/EncryptDecryptTest.java
1
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
2
//
3
// SPDX-License-Identifier: Apache-2.0
4

5
package sop.testsuite.operation;
6

7
import org.junit.jupiter.api.condition.EnabledIf;
8
import org.junit.jupiter.params.ParameterizedTest;
9
import org.junit.jupiter.params.provider.Arguments;
10
import org.junit.jupiter.params.provider.MethodSource;
11
import sop.ByteArrayAndResult;
12
import sop.DecryptionResult;
13
import sop.EncryptionResult;
14
import sop.Profile;
15
import sop.SOP;
16
import sop.SessionKey;
17
import sop.Verification;
18
import sop.enums.EncryptAs;
19
import sop.enums.SignatureMode;
20
import sop.exception.SOPGPException;
21
import sop.operation.Decrypt;
22
import sop.operation.Encrypt;
23
import sop.testsuite.TestData;
24
import sop.testsuite.assertions.VerificationListAssert;
25
import sop.util.Optional;
26
import sop.util.UTCUtil;
27

28
import java.io.IOException;
29
import java.nio.charset.StandardCharsets;
30
import java.text.ParseException;
31
import java.util.ArrayList;
32
import java.util.Date;
33
import java.util.List;
34
import java.util.stream.Stream;
35

36
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
37
import static org.junit.jupiter.api.Assertions.assertEquals;
38
import static org.junit.jupiter.api.Assertions.assertNotNull;
39
import static org.junit.jupiter.api.Assertions.assertThrows;
40

41
@EnabledIf("sop.testsuite.operation.AbstractSOPTest#hasBackends")
42
public class EncryptDecryptTest extends AbstractSOPTest {
1✔
43

44
    static Stream<Arguments> provideInstances() {
45
        return provideBackends();
1✔
46
    }
47

48
    @ParameterizedTest
49
    @MethodSource("provideInstances")
50
    public void encryptDecryptRoundTripPasswordTest(SOP sop) throws IOException {
51
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
52
        ByteArrayAndResult<EncryptionResult> encResult = sop.encrypt()
1✔
53
                .withPassword("sw0rdf1sh")
1✔
54
                .plaintext(message)
1✔
55
                .toByteArrayAndResult();
×
56

57
        byte[] ciphertext = encResult.getBytes();
×
58
        Optional<SessionKey> encSessionKey = encResult.getResult().getSessionKey();
×
59

60
        ByteArrayAndResult<DecryptionResult> decResult = sop.decrypt()
×
61
                .withPassword("sw0rdf1sh")
×
62
                .ciphertext(ciphertext)
×
63
                .toByteArrayAndResult();
×
64

65
        byte[] plaintext = decResult.getBytes();
×
66
        Optional<SessionKey> decSessionKey = decResult.getResult().getSessionKey();
×
67

68
        assertArrayEquals(message, plaintext);
×
69
        if (encSessionKey.isPresent() && decSessionKey.isPresent()) {
×
70
            assertEquals(encSessionKey.get(), decSessionKey.get());
×
71
        }
72
    }
×
73

74
    @ParameterizedTest
75
    @MethodSource("provideInstances")
76
    public void encryptDecryptRoundTripAliceTest(SOP sop) throws IOException {
77
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
78
        byte[] ciphertext = sop.encrypt()
1✔
79
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
80
                .plaintext(message)
1✔
81
                .toByteArrayAndResult()
×
82
                .getBytes();
×
83

84
        ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
×
85
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
86
                .ciphertext(ciphertext)
×
87
                .toByteArrayAndResult();
×
88

89
        byte[] plaintext = bytesAndResult.getBytes();
×
90
        assertArrayEquals(message, plaintext);
×
91

92
        DecryptionResult result = bytesAndResult.getResult();
×
93
        assertNotNull(result.getSessionKey().get());
×
94
    }
×
95

96
    @ParameterizedTest
97
    @MethodSource("provideInstances")
98
    public void encryptDecryptRoundTripBobTest(SOP sop) throws IOException {
99
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
100
        byte[] ciphertext = sop.encrypt()
1✔
101
                .withCert(TestData.BOB_CERT.getBytes(StandardCharsets.UTF_8))
1✔
102
                .plaintext(message)
1✔
103
                .toByteArrayAndResult()
×
104
                .getBytes();
×
105

106
        byte[] plaintext = sop.decrypt()
×
107
                .withKey(TestData.BOB_KEY.getBytes(StandardCharsets.UTF_8))
×
108
                .ciphertext(ciphertext)
×
109
                .toByteArrayAndResult()
×
110
                .getBytes();
×
111

112
        assertArrayEquals(message, plaintext);
×
113
    }
×
114

115
    @ParameterizedTest
116
    @MethodSource("provideInstances")
117
    public void encryptDecryptRoundTripCarolTest(SOP sop) throws IOException {
118
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
119
        byte[] ciphertext = sop.encrypt()
1✔
120
                .withCert(TestData.CAROL_CERT.getBytes(StandardCharsets.UTF_8))
1✔
121
                .plaintext(message)
1✔
122
                .toByteArrayAndResult()
×
123
                .getBytes();
×
124

125
        byte[] plaintext = sop.decrypt()
×
126
                .withKey(TestData.CAROL_KEY.getBytes(StandardCharsets.UTF_8))
×
127
                .ciphertext(ciphertext)
×
128
                .toByteArrayAndResult()
×
129
                .getBytes();
×
130

131
        assertArrayEquals(message, plaintext);
×
132
    }
×
133

134
    @ParameterizedTest
135
    @MethodSource("provideInstances")
136
    public void encryptNoArmorThenArmorThenDecryptRoundTrip(SOP sop) throws IOException {
137
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
138
        byte[] ciphertext = sop.encrypt()
1✔
139
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
140
                .noArmor()
1✔
141
                .plaintext(message)
1✔
142
                .toByteArrayAndResult()
×
143
                .getBytes();
×
144

145
        byte[] armored = sop.armor()
×
146
                .data(ciphertext)
×
147
                .getBytes();
×
148

149
        ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
×
150
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
151
                .ciphertext(armored)
×
152
                .toByteArrayAndResult();
×
153

154
        byte[] plaintext = bytesAndResult.getBytes();
×
155
        assertArrayEquals(message, plaintext);
×
156
    }
×
157

158
    @ParameterizedTest
159
    @MethodSource("provideInstances")
160
    public void encryptSignDecryptVerifyRoundTripAliceTest(SOP sop) throws IOException {
161
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
162
        byte[] ciphertext = sop.encrypt()
1✔
163
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
164
                .signWith(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
165
                .mode(EncryptAs.binary)
1✔
166
                .plaintext(message)
1✔
167
                .toByteArrayAndResult()
×
168
                .getBytes();
×
169

170
        ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
×
171
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
172
                .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
×
173
                .ciphertext(ciphertext)
×
174
                .toByteArrayAndResult();
×
175

176
        byte[] plaintext = bytesAndResult.getBytes();
×
177
        assertArrayEquals(message, plaintext);
×
178

179
        DecryptionResult result = bytesAndResult.getResult();
×
180
        assertNotNull(result.getSessionKey().get());
×
181

182
        List<Verification> verificationList = result.getVerifications();
×
183
        VerificationListAssert.assertThatVerificationList(verificationList)
×
184
                .isNotEmpty()
×
185
                .hasSingleItem()
×
186
                .issuedBy(TestData.ALICE_SIGNING_FINGERPRINT, TestData.ALICE_PRIMARY_FINGERPRINT)
×
187
                .hasModeOrNull(SignatureMode.binary);
×
188
    }
×
189

190
    @ParameterizedTest
191
    @MethodSource("provideInstances")
192
    public void encryptSignAsTextDecryptVerifyRoundTripAliceTest(SOP sop) throws IOException {
193
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
194
        byte[] ciphertext = sop.encrypt()
1✔
195
                .withCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
196
                .signWith(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
197
                .mode(EncryptAs.text)
1✔
198
                .plaintext(message)
1✔
199
                .toByteArrayAndResult()
×
200
                .getBytes();
×
201

202
        ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
×
203
                .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
×
204
                .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
×
205
                .ciphertext(ciphertext)
×
206
                .toByteArrayAndResult();
×
207

208
        byte[] plaintext = bytesAndResult.getBytes();
×
209
        assertArrayEquals(message, plaintext);
×
210

211
        DecryptionResult result = bytesAndResult.getResult();
×
212
        assertNotNull(result.getSessionKey().get());
×
213

214
        List<Verification> verificationList = result.getVerifications();
×
215
        VerificationListAssert.assertThatVerificationList(verificationList)
×
216
                .hasSingleItem()
×
217
                .issuedBy(TestData.ALICE_SIGNING_FINGERPRINT, TestData.ALICE_PRIMARY_FINGERPRINT)
×
218
                .hasModeOrNull(SignatureMode.text);
×
219
    }
×
220

221
    @ParameterizedTest
222
    @MethodSource("provideInstances")
223
    public void encryptSignDecryptVerifyRoundTripWithFreshEncryptedKeyTest(SOP sop) throws IOException {
224
        byte[] keyPassword = "sw0rdf1sh".getBytes(StandardCharsets.UTF_8);
1✔
225
        byte[] key = sop.generateKey()
1✔
226
                .withKeyPassword(keyPassword)
1✔
227
                .userId("Alice <alice@openpgp.org>")
1✔
228
                .generate()
1✔
229
                .getBytes();
×
230
        byte[] cert = sop.extractCert()
×
231
                .key(key)
×
232
                .getBytes();
×
233

234
        byte[] message = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
×
235
        byte[] ciphertext = sop.encrypt()
×
236
                .withCert(cert)
×
237
                .signWith(key)
×
238
                .withKeyPassword(keyPassword)
×
239
                .plaintext(message)
×
240
                .toByteArrayAndResult()
×
241
                .getBytes();
×
242

243
        ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
×
244
                .withKey(key)
×
245
                .withKeyPassword(keyPassword)
×
246
                .verifyWithCert(cert)
×
247
                .ciphertext(ciphertext)
×
248
                .toByteArrayAndResult();
×
249

250
        List<Verification> verifications = bytesAndResult.getResult().getVerifications();
×
251
        VerificationListAssert.assertThatVerificationList(verifications)
×
252
                .isNotEmpty()
×
253
                .hasSingleItem();
×
254
    }
×
255

256
    @ParameterizedTest
257
    @MethodSource("provideInstances")
258
    public void decryptVerifyNotAfterTest(SOP sop) throws ParseException {
259
        byte[] message = ("-----BEGIN PGP MESSAGE-----\n" +
1✔
260
                "\n" +
261
                "wV4DR2b2udXyHrYSAQdAwlOwwyxFDJta5+H9abgSj8jum9v7etUc9usdrElESmow\n" +
262
                "2Hka48AFVfOezYh0OFn9R8+DMcpuE+e4nw3XnnX5nKs/j3AC2IW6zRHUkRcF3ZCq\n" +
263
                "0sBNAfjnTYCMjuBmqdcCLzaZT4Hadnpg6neP1UecT/jP14maGfv8nwt0IDGR0Bik\n" +
264
                "0WC/UJLpWyJ/6TgRrA5hNfANVnfiFBzIiThiVBRWPT2StHr2cOAvFxQK4Uk07rK9\n" +
265
                "9aTUak8FpML+QA83U8I3qOk4QbzGVBP+IDJ+AKmvDz+0V+9kUhKp+8vyXsBmo9c3\n" +
266
                "SAXjhFSiPQkU7ORsc6gQHL9+KPOU+W2poPK87H3cmaGiusnXMeLXLIUbkBUJTswd\n" +
267
                "JNrA2yAkTTFP9QabsdcdTGoeYamq1c29kHF3GOTTcEqXw4WWXngcF7Kbcf435kkL\n" +
268
                "4iSJnCaxTPftKUxmiGqMqLef7ICVnq/lz3HrH1VD54s=\n" +
269
                "=Ebi3\n" +
270
                "-----END PGP MESSAGE-----").getBytes(StandardCharsets.UTF_8);
1✔
271
        Date signatureDate = UTCUtil.parseUTCDate("2023-01-13T16:09:32Z");
1✔
272

273
        Date beforeSignature = new Date(signatureDate.getTime() - 1000); // 1 sec before signing date
1✔
274

275
        assertThrows(SOPGPException.NoSignature.class, () -> {
×
276
            ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
1✔
277
                    .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
278
                    .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
279
                    .verifyNotAfter(beforeSignature)
1✔
280
                    .ciphertext(message)
1✔
281
                    .toByteArrayAndResult();
×
282

283
            // Some implementations do not throw NoSignature and instead return an empty list.
284
            if (bytesAndResult.getResult().getVerifications().isEmpty()) {
×
285
                throw new SOPGPException.NoSignature("No verifiable signature found.");
×
286
            }
287
        });
×
288
    }
×
289

290
    @ParameterizedTest
291
    @MethodSource("provideInstances")
292
    public void decryptVerifyNotBeforeTest(SOP sop) throws ParseException {
293
        byte[] message = ("-----BEGIN PGP MESSAGE-----\n" +
1✔
294
                "\n" +
295
                "wV4DR2b2udXyHrYSAQdAwlOwwyxFDJta5+H9abgSj8jum9v7etUc9usdrElESmow\n" +
296
                "2Hka48AFVfOezYh0OFn9R8+DMcpuE+e4nw3XnnX5nKs/j3AC2IW6zRHUkRcF3ZCq\n" +
297
                "0sBNAfjnTYCMjuBmqdcCLzaZT4Hadnpg6neP1UecT/jP14maGfv8nwt0IDGR0Bik\n" +
298
                "0WC/UJLpWyJ/6TgRrA5hNfANVnfiFBzIiThiVBRWPT2StHr2cOAvFxQK4Uk07rK9\n" +
299
                "9aTUak8FpML+QA83U8I3qOk4QbzGVBP+IDJ+AKmvDz+0V+9kUhKp+8vyXsBmo9c3\n" +
300
                "SAXjhFSiPQkU7ORsc6gQHL9+KPOU+W2poPK87H3cmaGiusnXMeLXLIUbkBUJTswd\n" +
301
                "JNrA2yAkTTFP9QabsdcdTGoeYamq1c29kHF3GOTTcEqXw4WWXngcF7Kbcf435kkL\n" +
302
                "4iSJnCaxTPftKUxmiGqMqLef7ICVnq/lz3HrH1VD54s=\n" +
303
                "=Ebi3\n" +
304
                "-----END PGP MESSAGE-----").getBytes(StandardCharsets.UTF_8);
1✔
305
        Date signatureDate = UTCUtil.parseUTCDate("2023-01-13T16:09:32Z");
1✔
306

307
        Date afterSignature = new Date(signatureDate.getTime() + 1000); // 1 sec after signing date
1✔
308

309
        assertThrows(SOPGPException.NoSignature.class, () -> {
×
310
            ByteArrayAndResult<DecryptionResult> bytesAndResult = sop.decrypt()
1✔
311
                    .withKey(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
312
                    .verifyWithCert(TestData.ALICE_CERT.getBytes(StandardCharsets.UTF_8))
1✔
313
                    .verifyNotBefore(afterSignature)
1✔
314
                    .ciphertext(message)
1✔
315
                    .toByteArrayAndResult();
×
316

317
            // Some implementations do not throw NoSignature and instead return an empty list.
318
            if (bytesAndResult.getResult().getVerifications().isEmpty()) {
×
319
                throw new SOPGPException.NoSignature("No verifiable signature found.");
×
320
            }
321
        });
×
322
    }
×
323

324
    @ParameterizedTest
325
    @MethodSource("provideInstances")
326
    public void missingArgsTest(SOP sop) {
327
        byte[] message = TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8);
1✔
328

329
        assertThrows(SOPGPException.MissingArg.class, () -> sop.encrypt()
1✔
330
                .plaintext(message)
1✔
331
                .toByteArrayAndResult()
×
332
                .getBytes());
×
333
    }
×
334

335
    @ParameterizedTest
336
    @MethodSource("provideInstances")
337
    public void passingSecretKeysForPublicKeysFails(SOP sop) {
338
        assertThrows(SOPGPException.BadData.class, () ->
×
339
                sop.encrypt()
1✔
340
                        .withCert(TestData.ALICE_KEY.getBytes(StandardCharsets.UTF_8))
1✔
341
                        .plaintext(TestData.PLAINTEXT.getBytes(StandardCharsets.UTF_8))
1✔
342
                        .toByteArrayAndResult()
×
343
                        .getBytes());
×
344
    }
×
345

346
    @ParameterizedTest
347
    @MethodSource("provideInstances")
348
    public void encryptDecryptWithAllSupportedKeyGenerationProfiles(SOP sop) throws IOException {
349
        List<Profile> profiles = sop.listProfiles().generateKey();
×
350

351
        List<byte[]> keys = new ArrayList<>();
×
352
        List<byte[]> certs = new ArrayList<>();
×
353
        for (Profile p : profiles) {
×
354
            byte[] k = sop.generateKey()
×
355
                    .profile(p)
×
356
                    .userId(p.getName())
×
357
                    .generate()
×
358
                    .getBytes();
×
359
            keys.add(k);
×
360

361
            byte[] c = sop.extractCert()
×
362
                    .key(k)
×
363
                    .getBytes();
×
364
            certs.add(c);
×
365
        }
×
366

367
        byte[] plaintext = "Hello, World!\n".getBytes();
×
368

369
        Encrypt encrypt = sop.encrypt();
×
370
        for (byte[] c : certs) {
×
371
            encrypt.withCert(c);
×
372
        }
×
373
        for (byte[] k : keys) {
×
374
            encrypt.signWith(k);
×
375
        }
×
376

377
        ByteArrayAndResult<EncryptionResult> encRes = encrypt.plaintext(plaintext)
×
378
                .toByteArrayAndResult();
×
379
        EncryptionResult eResult = encRes.getResult();
×
380
        byte[] ciphertext = encRes.getBytes();
×
381

382
        for (byte[] k : keys) {
×
383
            Decrypt decrypt = sop.decrypt()
×
384
                    .withKey(k);
×
385
            for (byte[] c : certs) {
×
386
                decrypt.verifyWithCert(c);
×
387
            }
×
388
            ByteArrayAndResult<DecryptionResult> decRes = decrypt.ciphertext(ciphertext)
×
389
                    .toByteArrayAndResult();
×
390
            DecryptionResult dResult = decRes.getResult();
×
391
            byte[] decPlaintext = decRes.getBytes();
×
392
            assertArrayEquals(plaintext, decPlaintext);
×
393
            assertEquals(certs.size(), dResult.getVerifications().size());
×
394
        }
×
395
    }
×
396
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc