1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.transport;
12
13 import static java.nio.charset.StandardCharsets.UTF_8;
14 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListPBE;
15 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListTrans;
16 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.folderDelete;
17 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.permitLongTests;
18 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.policySetup;
19 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.product;
20 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.proxySetup;
21 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.publicAddress;
22 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.reportPolicy;
23 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.securityProviderName;
24 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.textWrite;
25 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.transferStream;
26 import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.verifyFileContent;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertTrue;
31 import static org.junit.Assume.assumeTrue;
32
33 import java.io.BufferedReader;
34 import java.io.ByteArrayInputStream;
35 import java.io.ByteArrayOutputStream;
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.InputStreamReader;
41 import java.io.OutputStream;
42 import java.io.PrintWriter;
43 import java.net.SocketTimeoutException;
44 import java.net.URL;
45 import java.net.URLConnection;
46 import java.net.UnknownHostException;
47 import java.nio.file.Files;
48 import java.security.GeneralSecurityException;
49 import java.security.Provider;
50 import java.security.Security;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.List;
54 import java.util.Locale;
55 import java.util.Properties;
56 import java.util.Set;
57 import java.util.TreeSet;
58 import java.util.UUID;
59
60 import javax.crypto.SecretKeyFactory;
61
62 import org.eclipse.jgit.api.Git;
63 import org.eclipse.jgit.lib.StoredConfig;
64 import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
65 import org.eclipse.jgit.util.FileUtils;
66 import org.junit.After;
67 import org.junit.AfterClass;
68 import org.junit.Before;
69 import org.junit.BeforeClass;
70 import org.junit.FixMethodOrder;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.junit.runners.MethodSorters;
74 import org.junit.runners.Parameterized;
75 import org.junit.runners.Parameterized.Parameters;
76 import org.junit.runners.Suite;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80
81
82
83
84
85
86
87
88
89
90
91
92 @RunWith(Suite.class)
93 @Suite.SuiteClasses({
94 WalkEncryptionTest.Required.class,
95 WalkEncryptionTest.MinimalSet.class,
96 WalkEncryptionTest.TestablePBE.class,
97 WalkEncryptionTest.TestableTransformation.class,
98 })
99 public class WalkEncryptionTest {
100
101
102
103
104 static final Logger logger = LoggerFactory.getLogger(WalkEncryptionTest.class);
105
106
107
108
109 interface Names {
110
111
112
113 String TEST_BUCKET = "test.bucket";
114
115
116
117 String ENV_ACCESS_KEY = "JGIT_S3_ACCESS_KEY";
118
119 String ENV_SECRET_KEY = "JGIT_S3_SECRET_KEY";
120
121 String ENV_BUCKET_NAME = "JGIT_S3_BUCKET_NAME";
122
123
124
125 String ENV_CONFIG_FILE = "JGIT_S3_CONFIG_FILE";
126
127
128
129 String SYS_ACCESS_KEY = "jgit.s3.access.key";
130
131 String SYS_SECRET_KEY = "jgit.s3.secret.key";
132
133 String SYS_BUCKET_NAME = "jgit.s3.bucket.name";
134
135
136 String SYS_CONFIG_FILE = "jgit.s3.config.file";
137
138
139
140
141
142
143
144
145
146 String CONFIG_FILE = "jgit-s3-config.properties";
147
148
149 String HOME_CONFIG_FILE = System.getProperty("user.home")
150 + File.separator + CONFIG_FILE;
151
152
153 String WORK_CONFIG_FILE = System.getProperty("user.dir")
154 + File.separator + CONFIG_FILE;
155
156
157 String TEST_CONFIG_FILE = System.getProperty("user.dir")
158 + File.separator + "tst-rsrc" + File.separator + CONFIG_FILE;
159
160 }
161
162
163
164
165 static class Props implements WalkEncryptionTest.Names, AmazonS3.Keys {
166
167 static boolean haveEnvVar(String name) {
168 return System.getenv(name) != null;
169 }
170
171 static boolean haveEnvVarFile(String name) {
172 return haveEnvVar(name) && new File(name).exists();
173 }
174
175 static boolean haveSysProp(String name) {
176 return System.getProperty(name) != null;
177 }
178
179 static boolean haveSysPropFile(String name) {
180 return haveSysProp(name) && new File(name).exists();
181 }
182
183 static void loadEnvVar(String source, String target, Properties props) {
184 props.put(target, System.getenv(source));
185 }
186
187 static void loadSysProp(String source, String target,
188 Properties props) {
189 props.put(target, System.getProperty(source));
190 }
191
192 static boolean haveProp(String name, Properties props) {
193 return props.containsKey(name);
194 }
195
196 static boolean checkTestProps(Properties props) {
197 return haveProp(ACCESS_KEY, props) && haveProp(SECRET_KEY, props)
198 && haveProp(TEST_BUCKET, props);
199 }
200
201 static Properties fromEnvVars() {
202 if (haveEnvVar(ENV_ACCESS_KEY) && haveEnvVar(ENV_SECRET_KEY)
203 && haveEnvVar(ENV_BUCKET_NAME)) {
204 Properties props = new Properties();
205 loadEnvVar(ENV_ACCESS_KEY, ACCESS_KEY, props);
206 loadEnvVar(ENV_SECRET_KEY, SECRET_KEY, props);
207 loadEnvVar(ENV_BUCKET_NAME, TEST_BUCKET, props);
208 return props;
209 }
210 return null;
211 }
212
213 static Properties fromEnvFile() throws Exception {
214 if (haveEnvVarFile(ENV_CONFIG_FILE)) {
215 Properties props = new Properties();
216 props.load(new FileInputStream(ENV_CONFIG_FILE));
217 if (checkTestProps(props)) {
218 return props;
219 }
220 throw new Error("Environment config file is incomplete.");
221 }
222 return null;
223 }
224
225 static Properties fromSysProps() {
226 if (haveSysProp(SYS_ACCESS_KEY) && haveSysProp(SYS_SECRET_KEY)
227 && haveSysProp(SYS_BUCKET_NAME)) {
228 Properties props = new Properties();
229 loadSysProp(SYS_ACCESS_KEY, ACCESS_KEY, props);
230 loadSysProp(SYS_SECRET_KEY, SECRET_KEY, props);
231 loadSysProp(SYS_BUCKET_NAME, TEST_BUCKET, props);
232 return props;
233 }
234 return null;
235 }
236
237 static Properties fromSysFile() throws Exception {
238 if (haveSysPropFile(SYS_CONFIG_FILE)) {
239 Properties props = new Properties();
240 props.load(new FileInputStream(SYS_CONFIG_FILE));
241 if (checkTestProps(props)) {
242 return props;
243 }
244 throw new Error("System props config file is incomplete.");
245 }
246 return null;
247 }
248
249 static Properties fromConfigFile(String path) throws Exception {
250 File file = new File(path);
251 if (file.exists()) {
252 Properties props = new Properties();
253 props.load(new FileInputStream(file));
254 if (checkTestProps(props)) {
255 return props;
256 }
257 throw new Error("Props config file is incomplete: " + path);
258 }
259 return null;
260 }
261
262
263
264
265
266
267
268 static Properties discover() throws Exception {
269 Properties props;
270 if ((props = fromEnvVars()) != null) {
271 logger.debug(
272 "Using test properties from environment variables.");
273 return props;
274 }
275 if ((props = fromEnvFile()) != null) {
276 logger.debug(
277 "Using test properties from environment variable config file.");
278 return props;
279 }
280 if ((props = fromSysProps()) != null) {
281 logger.debug("Using test properties from system properties.");
282 return props;
283 }
284 if ((props = fromSysFile()) != null) {
285 logger.debug(
286 "Using test properties from system property config file.");
287 return props;
288 }
289 if ((props = fromConfigFile(HOME_CONFIG_FILE)) != null) {
290 logger.debug(
291 "Using test properties from hard coded ${user.home} file.");
292 return props;
293 }
294 if ((props = fromConfigFile(WORK_CONFIG_FILE)) != null) {
295 logger.debug(
296 "Using test properties from hard coded ${user.dir} file.");
297 return props;
298 }
299 if ((props = fromConfigFile(TEST_CONFIG_FILE)) != null) {
300 logger.debug(
301 "Using test properties from hard coded ${project.source} file.");
302 return props;
303 }
304 throw new Error("Can not load test properties form any source.");
305 }
306
307 }
308
309
310
311
312 static class Util {
313
314
315
316
317
318
319
320
321 static String textRead(File file) throws Exception {
322 return new String(Files.readAllBytes(file.toPath()), UTF_8);
323 }
324
325
326
327
328
329
330
331
332 static void textWrite(File file, String text) throws Exception {
333 Files.write(file.toPath(), text.getBytes(UTF_8));
334 }
335
336 static void verifyFileContent(File fileOne, File fileTwo)
337 throws Exception {
338 assertTrue(fileOne.length() > 0);
339 assertTrue(fileTwo.length() > 0);
340 String textOne = textRead(fileOne);
341 String textTwo = textRead(fileTwo);
342 assertEquals(textOne, textTwo);
343 }
344
345
346
347
348
349
350
351 static void folderCreate(String folder) throws Exception {
352 File path = new File(folder);
353 assertTrue(path.mkdirs());
354 }
355
356
357
358
359
360
361
362 static void folderDelete(String folder) throws Exception {
363 File path = new File(folder);
364 FileUtils.delete(path,
365 FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
366 }
367
368
369
370
371
372
373
374 static String publicAddress() throws Exception {
375 try {
376 String service = "http://checkip.amazonaws.com";
377 URL url = new URL(service);
378 URLConnection c = url.openConnection();
379 c.setConnectTimeout(500);
380 c.setReadTimeout(500);
381 try (BufferedReader reader = new BufferedReader(
382 new InputStreamReader(c.getInputStream(), UTF_8))) {
383 return reader.readLine();
384 }
385 } catch (UnknownHostException | SocketTimeoutException e) {
386 return "Can't reach http://checkip.amazonaws.com to"
387 + " determine public address";
388 }
389 }
390
391
392
393
394
395
396
397
398
399 static List<String> cryptoCipherListPBE() {
400 return cryptoCipherList(WalkEncryption.Vals.REGEX_PBE);
401 }
402
403
404 static List<String> cryptoCipherListTrans() {
405 return cryptoCipherList(WalkEncryption.Vals.REGEX_TRANS);
406 }
407
408 static String securityProviderName(String algorithm) throws Exception {
409 return SecretKeyFactory.getInstance(algorithm).getProvider()
410 .getName();
411 }
412
413 static List<String> cryptoCipherList(String regex) {
414 Set<String> source = Security.getAlgorithms("Cipher");
415 Set<String> target = new TreeSet<>();
416 for (String algo : source) {
417 algo = algo.toUpperCase(Locale.ROOT);
418 if (algo.matches(regex)) {
419 target.add(algo);
420 }
421 }
422 return new ArrayList<>(target);
423 }
424
425
426
427
428
429
430
431
432
433 static long transferStream(InputStream from, OutputStream into)
434 throws IOException {
435 byte[] array = new byte[1 * 1024];
436 long total = 0;
437 while (true) {
438 int count = from.read(array);
439 if (count == -1) {
440 break;
441 }
442 into.write(array, 0, count);
443 total += count;
444 }
445 return total;
446 }
447
448
449
450
451
452
453
454
455 static void proxySetup() throws Exception {
456 String keyNoProxy = "no_proxy";
457 String keyHttpProxy = "http_proxy";
458 String keyHttpsProxy = "https_proxy";
459
460 String no_proxy = System.getProperty(keyNoProxy,
461 System.getenv(keyNoProxy));
462 if (no_proxy != null) {
463 System.setProperty("http.nonProxyHosts", no_proxy);
464 logger.info("Proxy NOT: " + no_proxy);
465 }
466
467 String http_proxy = System.getProperty(keyHttpProxy,
468 System.getenv(keyHttpProxy));
469 if (http_proxy != null) {
470 URL url = new URL(http_proxy);
471 System.setProperty("http.proxyHost", url.getHost());
472 System.setProperty("http.proxyPort", "" + url.getPort());
473 logger.info("Proxy HTTP: " + http_proxy);
474 }
475
476 String https_proxy = System.getProperty(keyHttpsProxy,
477 System.getenv(keyHttpsProxy));
478 if (https_proxy != null) {
479 URL url = new URL(https_proxy);
480 System.setProperty("https.proxyHost", url.getHost());
481 System.setProperty("https.proxyPort", "" + url.getPort());
482 logger.info("Proxy HTTPS: " + https_proxy);
483 }
484
485 if (no_proxy == null && http_proxy == null && https_proxy == null) {
486 logger.info("Proxy not used.");
487 }
488
489 }
490
491
492
493
494
495
496 static boolean permitLongTests() {
497 return isBuildCI() || isProfileActive();
498 }
499
500
501
502
503
504
505 static boolean isProfileActive() {
506 return Boolean.parseBoolean(System.getProperty("jgit.test.long"));
507 }
508
509
510
511
512
513
514 static boolean isBuildCI() {
515 return System.getenv("HUDSON_HOME") != null;
516 }
517
518
519
520
521
522
523
524
525
526 static void policySetup(boolean restrictedOn) {
527 try {
528 java.lang.reflect.Field isRestricted = Class
529 .forName("javax.crypto.JceSecurity")
530 .getDeclaredField("isRestricted");
531 isRestricted.setAccessible(true);
532 isRestricted.set(null, Boolean.valueOf(restrictedOn));
533 } catch (Throwable e) {
534 logger.info(
535 "Could not setup JCE security policy restrictions.");
536 }
537 }
538
539 static void reportPolicy() {
540 try {
541 java.lang.reflect.Field isRestricted = Class
542 .forName("javax.crypto.JceSecurity")
543 .getDeclaredField("isRestricted");
544 isRestricted.setAccessible(true);
545 logger.info("JCE security policy restricted="
546 + isRestricted.get(null));
547 } catch (Throwable e) {
548 logger.info(
549 "Could not report JCE security policy restrictions.");
550 }
551 }
552
553 static List<Object[]> product(List<String> one, List<String> two) {
554 List<Object[]> result = new ArrayList<>();
555 for (String s1 : one) {
556 for (String s2 : two) {
557 result.add(new Object[] { s1, s2 });
558 }
559 }
560 return result;
561 }
562
563 }
564
565
566
567
568 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
569 public abstract static class Base extends SampleDataRepositoryTestCase {
570
571
572
573
574 static final String JGIT_USER = "tester-" + System.currentTimeMillis();
575
576
577
578
579 static final String JGIT_PASS = "secret-" + System.currentTimeMillis();
580
581
582
583
584 static final String JGIT_CONF_FILE = System.getProperty("user.home")
585 + "/" + JGIT_USER;
586
587
588
589
590 static final String JGIT_REPO_DIR = JGIT_USER + ".jgit";
591
592
593
594
595 static final String JGIT_LOCAL_DIR = System.getProperty("user.dir")
596 + "/target/" + JGIT_REPO_DIR;
597
598
599
600
601 static final String JGIT_REMOTE_DIR = JGIT_REPO_DIR;
602
603
604
605
606
607
608
609 static void configCreate(String algorithm) throws Exception {
610 Properties props = Props.discover();
611 props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
612 props.put(AmazonS3.Keys.CRYPTO_ALG, algorithm);
613 try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE,
614 UTF_8.name())) {
615 props.store(writer, "JGIT S3 connection configuration file.");
616 }
617 }
618
619
620
621
622
623
624
625 static void configCreate(Properties source) throws Exception {
626 Properties target = Props.discover();
627 target.putAll(source);
628 try (PrintWriter writer = new PrintWriter(JGIT_CONF_FILE,
629 UTF_8.name())) {
630 target.store(writer, "JGIT S3 connection configuration file.");
631 }
632 }
633
634
635
636
637
638
639 static void configDelete() throws Exception {
640 File path = new File(JGIT_CONF_FILE);
641 FileUtils.delete(path, FileUtils.SKIP_MISSING);
642 }
643
644
645
646
647
648
649
650 static String amazonURI() throws Exception {
651 Properties props = Props.discover();
652 String bucket = props.getProperty(Names.TEST_BUCKET);
653 assertNotNull(bucket);
654 return TransportAmazonS3.S3_SCHEME + "://" + JGIT_USER + "@"
655 + bucket + "/" + JGIT_REPO_DIR;
656 }
657
658
659
660
661
662
663 static void remoteCreate() throws Exception {
664 Properties props = Props.discover();
665 props.remove(AmazonS3.Keys.PASSWORD);
666 String bucket = props.getProperty(Names.TEST_BUCKET);
667 AmazonS3 s3 = new AmazonS3(props);
668 String path = JGIT_REMOTE_DIR + "/";
669 s3.put(bucket, path, new byte[0]);
670 logger.debug("remote create: " + JGIT_REMOTE_DIR);
671 }
672
673
674
675
676
677
678 static void remoteDelete() throws Exception {
679 Properties props = Props.discover();
680 props.remove(AmazonS3.Keys.PASSWORD);
681 String bucket = props.getProperty(Names.TEST_BUCKET);
682 AmazonS3 s3 = new AmazonS3(props);
683 List<String> list = s3.list(bucket, JGIT_REMOTE_DIR);
684 for (String path : list) {
685 path = JGIT_REMOTE_DIR + "/" + path;
686 s3.delete(bucket, path);
687 }
688 logger.debug("remote delete: " + JGIT_REMOTE_DIR);
689 }
690
691
692
693
694
695
696 static void remoteVerify() throws Exception {
697 Properties props = Props.discover();
698 String bucket = props.getProperty(Names.TEST_BUCKET);
699 AmazonS3 s3 = new AmazonS3(props);
700 String file = JGIT_USER + "-" + UUID.randomUUID().toString();
701 String path = JGIT_REMOTE_DIR + "/" + file;
702 s3.put(bucket, path, file.getBytes(UTF_8));
703 s3.delete(bucket, path);
704 }
705
706
707
708
709
710
711
712 static boolean isAlgorithmPresent(String algorithm) {
713 Set<String> cipherSet = Security.getAlgorithms("Cipher");
714 for (String source : cipherSet) {
715
716
717 String target = algorithm.toUpperCase(Locale.ROOT);
718 if (source.equalsIgnoreCase(target)) {
719 return true;
720 }
721 }
722 return false;
723 }
724
725 static boolean isAlgorithmPresent(Properties props) {
726 String profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
727 String version = props.getProperty(AmazonS3.Keys.CRYPTO_VER,
728 WalkEncryption.Vals.DEFAULT_VERS);
729 String cryptoAlgo;
730 String keyAlgo;
731 switch (version) {
732 case WalkEncryption.Vals.DEFAULT_VERS:
733 case WalkEncryption.JGitV1.VERSION:
734 cryptoAlgo = profile;
735 keyAlgo = profile;
736 break;
737 case WalkEncryption.JGitV2.VERSION:
738 cryptoAlgo = props
739 .getProperty(profile + WalkEncryption.Keys.X_ALGO);
740 keyAlgo = props
741 .getProperty(profile + WalkEncryption.Keys.X_KEY_ALGO);
742 break;
743 default:
744 return false;
745 }
746 try {
747 InsecureCipherFactory.create(cryptoAlgo);
748 SecretKeyFactory.getInstance(keyAlgo);
749 return true;
750 } catch (Throwable e) {
751 return false;
752 }
753 }
754
755
756
757
758
759
760
761 static boolean isAlgorithmAllowed(String algorithm) {
762 try {
763 WalkEncryption crypto = new WalkEncryption.JetS3tV2(
764 algorithm, JGIT_PASS);
765 verifyCrypto(crypto);
766 return true;
767 } catch (IOException e) {
768 return false;
769 } catch (GeneralSecurityException e) {
770 throw new Error(e);
771 }
772 }
773
774 static boolean isAlgorithmAllowed(Properties props) {
775 try {
776 WalkEncryption.instance(props);
777 return true;
778 } catch (GeneralSecurityException e) {
779 return false;
780 }
781 }
782
783
784
785
786
787
788
789 static void verifyCrypto(WalkEncryption crypto) throws IOException {
790 String charset = "UTF-8";
791 String sourceText = "secret-message Свобода 老子";
792 String targetText;
793 byte[] cipherText;
794 {
795 byte[] origin = sourceText.getBytes(charset);
796 ByteArrayOutputStream target = new ByteArrayOutputStream();
797 try (OutputStream source = crypto.encrypt(target)) {
798 source.write(origin);
799 source.flush();
800 }
801 cipherText = target.toByteArray();
802 }
803 {
804 InputStream source = new ByteArrayInputStream(cipherText);
805 InputStream target = crypto.decrypt(source);
806 ByteArrayOutputStream result = new ByteArrayOutputStream();
807 transferStream(target, result);
808 targetText = result.toString(charset);
809 }
810 assertEquals(sourceText, targetText);
811 }
812
813
814
815
816
817
818
819 static boolean isAlgorithmTestable(String algorithm) {
820 return isAlgorithmPresent(algorithm)
821 && isAlgorithmAllowed(algorithm);
822 }
823
824 static boolean isAlgorithmTestable(Properties props) {
825 return isAlgorithmPresent(props) && isAlgorithmAllowed(props);
826 }
827
828
829
830
831
832
833
834 static void reportAlgorithmStatus(String algorithm) throws Exception {
835 final boolean present = isAlgorithmPresent(algorithm);
836 final boolean allowed = present && isAlgorithmAllowed(algorithm);
837 final String provider = present ? securityProviderName(algorithm)
838 : "N/A";
839 String status = "Algorithm: " + algorithm + " @ " + provider + "; "
840 + "present/allowed : " + present + "/" + allowed;
841 if (allowed) {
842 logger.info("Testing " + status);
843 } else {
844 logger.warn("Missing " + status);
845 }
846 }
847
848 static void reportAlgorithmStatus(Properties props) throws Exception {
849 final boolean present = isAlgorithmPresent(props);
850 final boolean allowed = present && isAlgorithmAllowed(props);
851
852 String profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
853 String version = props.getProperty(AmazonS3.Keys.CRYPTO_VER);
854
855 StringBuilder status = new StringBuilder();
856 status.append(" Version: " + version);
857 status.append(" Profile: " + profile);
858 status.append(" Present: " + present);
859 status.append(" Allowed: " + allowed);
860
861 if (allowed) {
862 logger.info("Testing " + status);
863 } else {
864 logger.warn("Missing " + status);
865 }
866 }
867
868
869
870
871
872
873 static boolean isTestConfigPresent() {
874 try {
875 Props.discover();
876 return true;
877 } catch (Throwable e) {
878 return false;
879 }
880 }
881
882 static void reportTestConfigPresent() {
883 if (isTestConfigPresent()) {
884 logger.info("Amazon S3 test configuration is present.");
885 } else {
886 logger.error(
887 "Amazon S3 test configuration is missing, tests will not run.");
888 }
889 }
890
891
892
893
894
895
896 static void reportPublicAddress() throws Exception {
897 logger.info("Public address: " + publicAddress());
898 }
899
900
901
902
903
904
905
906 static final String PROVIDER_BC = "org.bouncycastle.jce.provider.BouncyCastleProvider";
907
908
909
910
911 static void loadBouncyCastle() {
912 try {
913 Class<?> provider = Class.forName(PROVIDER_BC);
914 Provider instance = (Provider) provider
915 .getConstructor(new Class[] {})
916 .newInstance(new Object[] {});
917 Security.addProvider(instance);
918 logger.info("Loaded " + PROVIDER_BC);
919 } catch (Throwable e) {
920 logger.warn("Failed to load " + PROVIDER_BC);
921 }
922 }
923
924 static void reportLongTests() {
925 if (permitLongTests()) {
926 logger.info("Long running tests are enabled.");
927 } else {
928 logger.warn("Long running tests are disabled.");
929 }
930 }
931
932
933
934
935 static final String ALGO_ERROR = "PBKDF2WithHmacSHA1";
936
937
938
939
940 static final String ALGO_JETS3T = "PBEWithMD5AndDES";
941
942
943
944
945 static final String ALGO_MINIMAL_AES = "PBEWithHmacSHA1AndAES_128";
946
947
948
949
950 static final String ALGO_BOUNCY_CASTLE_CBC = "PBEWithSHAAndTwofish-CBC";
951
952
953
954 @BeforeClass
955 public static void initialize() throws Exception {
956 Transport.register(TransportAmazonS3.PROTO_S3);
957 proxySetup();
958 reportPolicy();
959 reportLongTests();
960 reportPublicAddress();
961 reportTestConfigPresent();
962 loadBouncyCastle();
963 if (isTestConfigPresent()) {
964 remoteCreate();
965 }
966 }
967
968 @AfterClass
969 public static void terminate() throws Exception {
970 configDelete();
971 folderDelete(JGIT_LOCAL_DIR);
972 if (isTestConfigPresent()) {
973 remoteDelete();
974 }
975 }
976
977 @Before
978 @Override
979 public void setUp() throws Exception {
980 super.setUp();
981 }
982
983 @After
984 @Override
985 public void tearDown() throws Exception {
986 super.tearDown();
987 }
988
989
990
991
992
993
994
995 void cryptoTestIfCan(Properties props) throws Exception {
996 reportAlgorithmStatus(props);
997 assumeTrue(isTestConfigPresent());
998 assumeTrue(isAlgorithmTestable(props));
999 cryptoTest(props);
1000 }
1001
1002
1003
1004
1005
1006
1007
1008 void cryptoTest(Properties props) throws Exception {
1009
1010 remoteDelete();
1011 configCreate(props);
1012 folderDelete(JGIT_LOCAL_DIR);
1013
1014 String uri = amazonURI();
1015
1016
1017 File dirOne = db.getWorkTree();
1018 File dirTwo = new File(JGIT_LOCAL_DIR);
1019
1020
1021 String nameStatic = "master.txt";
1022 String nameDynamic = JGIT_USER + "-" + UUID.randomUUID().toString();
1023
1024 String remote = "remote";
1025 RefSpec specs = new RefSpec("refs/heads/master:refs/heads/master");
1026
1027 {
1028
1029 StoredConfig config = db.getConfig();
1030 RemoteConfig remoteConfig = new RemoteConfig(config, remote);
1031 remoteConfig.addURI(new URIish(uri));
1032 remoteConfig.update(config);
1033 config.save();
1034
1035 try (Git git = Git.open(dirOne)) {
1036 git.checkout().setName("master").call();
1037 git.push().setRemote(remote).setRefSpecs(specs).call();
1038 }
1039
1040 File fileStatic = new File(dirOne, nameStatic);
1041 assertTrue("Provided by setup", fileStatic.exists());
1042
1043 }
1044
1045 {
1046
1047 File fileStatic = new File(dirTwo, nameStatic);
1048 assertFalse("Not Provided by setup", fileStatic.exists());
1049
1050 try (Git git = Git.cloneRepository().setURI(uri)
1051 .setDirectory(dirTwo).call()) {
1052 assertTrue("Provided by clone", fileStatic.exists());
1053 }
1054
1055 }
1056
1057 {
1058 File fileOne = new File(dirOne, nameStatic);
1059 File fileTwo = new File(dirTwo, nameStatic);
1060 verifyFileContent(fileOne, fileTwo);
1061 }
1062
1063 {
1064
1065 File fileDynamic = new File(dirOne, nameDynamic);
1066 assertFalse("Not Provided by setup", fileDynamic.exists());
1067 FileUtils.createNewFile(fileDynamic);
1068 textWrite(fileDynamic, nameDynamic);
1069 assertTrue("Provided by create", fileDynamic.exists());
1070 assertTrue("Need content to encrypt", fileDynamic.length() > 0);
1071
1072 try (Git git = Git.open(dirOne)) {
1073 git.add().addFilepattern(nameDynamic).call();
1074 git.commit().setMessage(nameDynamic).call();
1075 git.push().setRemote(remote).setRefSpecs(specs).call();
1076 }
1077
1078 }
1079
1080 {
1081
1082 File fileDynamic = new File(dirTwo, nameDynamic);
1083 assertFalse("Not Provided by setup", fileDynamic.exists());
1084
1085 try (Git git = Git.open(dirTwo)) {
1086 git.pull().call();
1087 }
1088
1089 assertTrue("Provided by pull", fileDynamic.exists());
1090 }
1091
1092 {
1093 File fileOne = new File(dirOne, nameDynamic);
1094 File fileTwo = new File(dirTwo, nameDynamic);
1095 verifyFileContent(fileOne, fileTwo);
1096 }
1097
1098 }
1099
1100 }
1101
1102
1103
1104
1105 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
1106 public static class Required extends Base {
1107
1108 @Test
1109 public void test_A1_ValidURI() throws Exception {
1110 assumeTrue(isTestConfigPresent());
1111 URIish uri = new URIish(amazonURI());
1112 assertTrue("uri=" + uri, TransportAmazonS3.PROTO_S3.canHandle(uri));
1113 }
1114
1115 @Test(expected = Exception.class)
1116 public void test_A2_CryptoError() throws Exception {
1117 assumeTrue(isTestConfigPresent());
1118 Properties props = new Properties();
1119 props.put(AmazonS3.Keys.CRYPTO_ALG, ALGO_ERROR);
1120 props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
1121 cryptoTest(props);
1122 }
1123
1124 }
1125
1126
1127
1128
1129 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
1130 public static class MinimalSet extends Base {
1131
1132 @Test
1133 public void test_V0_Java7_JET() throws Exception {
1134 assumeTrue(isTestConfigPresent());
1135 Properties props = new Properties();
1136 props.put(AmazonS3.Keys.CRYPTO_ALG, ALGO_JETS3T);
1137
1138 props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
1139 cryptoTestIfCan(props);
1140 }
1141
1142 @Test
1143 public void test_V1_Java7_GIT() throws Exception {
1144 assumeTrue(isTestConfigPresent());
1145 Properties props = new Properties();
1146 props.put(AmazonS3.Keys.CRYPTO_ALG, ALGO_JETS3T);
1147 props.put(AmazonS3.Keys.CRYPTO_VER, "1");
1148 props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
1149 cryptoTestIfCan(props);
1150 }
1151
1152 @Test
1153 public void test_V2_Java7_AES() throws Exception {
1154 assumeTrue(isTestConfigPresent());
1155
1156 String profile = "AES/CBC/PKCS5Padding+PBKDF2WithHmacSHA1";
1157 Properties props = new Properties();
1158 props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
1159 props.put(AmazonS3.Keys.CRYPTO_VER, "2");
1160 props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
1161 props.put(profile + WalkEncryption.Keys.X_ALGO, "AES/CBC/PKCS5Padding");
1162 props.put(profile + WalkEncryption.Keys.X_KEY_ALGO, "PBKDF2WithHmacSHA1");
1163 props.put(profile + WalkEncryption.Keys.X_KEY_SIZE, "128");
1164 props.put(profile + WalkEncryption.Keys.X_KEY_ITER, "10000");
1165 props.put(profile + WalkEncryption.Keys.X_KEY_SALT, "e2 55 89 67 8e 8d e8 4c");
1166 cryptoTestIfCan(props);
1167 }
1168
1169 @Test
1170 public void test_V2_Java8_PBE_AES() throws Exception {
1171 assumeTrue(isTestConfigPresent());
1172 String profile = "PBEWithHmacSHA512AndAES_256";
1173 Properties props = new Properties();
1174 props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
1175 props.put(AmazonS3.Keys.CRYPTO_VER, "2");
1176 props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
1177 props.put(profile + WalkEncryption.Keys.X_ALGO, "PBEWithHmacSHA512AndAES_256");
1178 props.put(profile + WalkEncryption.Keys.X_KEY_ALGO, "PBEWithHmacSHA512AndAES_256");
1179 props.put(profile + WalkEncryption.Keys.X_KEY_SIZE, "256");
1180 props.put(profile + WalkEncryption.Keys.X_KEY_ITER, "10000");
1181 props.put(profile + WalkEncryption.Keys.X_KEY_SALT, "e2 55 89 67 8e 8d e8 4c");
1182 policySetup(false);
1183 cryptoTestIfCan(props);
1184 }
1185
1186 }
1187
1188
1189
1190
1191
1192 @RunWith(Parameterized.class)
1193 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
1194 public static class TestablePBE extends Base {
1195
1196 @Parameters(name = "Profile: {0} Version: {1}")
1197 public static Collection<Object[]> argsList() {
1198 List<String> algorithmList = new ArrayList<>();
1199 algorithmList.addAll(cryptoCipherListPBE());
1200
1201 List<String> versionList = new ArrayList<>();
1202 versionList.add("0");
1203 versionList.add("1");
1204
1205 return product(algorithmList, versionList);
1206 }
1207
1208 final String profile;
1209
1210 final String version;
1211
1212 final String password = JGIT_PASS;
1213
1214 public TestablePBE(String profile, String version) {
1215 this.profile = profile;
1216 this.version = version;
1217 }
1218
1219 @Test
1220 public void testCrypto() throws Exception {
1221 assumeTrue(permitLongTests());
1222 Properties props = new Properties();
1223 props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
1224 props.put(AmazonS3.Keys.CRYPTO_VER, version);
1225 props.put(AmazonS3.Keys.PASSWORD, password);
1226 cryptoTestIfCan(props);
1227 }
1228
1229 }
1230
1231
1232
1233
1234
1235 @RunWith(Parameterized.class)
1236 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
1237 public static class TestableTransformation extends Base {
1238
1239 @Parameters(name = "Profile: {0} Version: {1}")
1240 public static Collection<Object[]> argsList() {
1241 List<String> algorithmList = new ArrayList<>();
1242 algorithmList.addAll(cryptoCipherListTrans());
1243
1244 List<String> versionList = new ArrayList<>();
1245 versionList.add("1");
1246
1247 return product(algorithmList, versionList);
1248 }
1249
1250 final String profile;
1251
1252 final String version;
1253
1254 final String password = JGIT_PASS;
1255
1256 public TestableTransformation(String profile, String version) {
1257 this.profile = profile;
1258 this.version = version;
1259 }
1260
1261 @Test
1262 public void testCrypto() throws Exception {
1263 assumeTrue(permitLongTests());
1264 Properties props = new Properties();
1265 props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
1266 props.put(AmazonS3.Keys.CRYPTO_VER, version);
1267 props.put(AmazonS3.Keys.PASSWORD, password);
1268 cryptoTestIfCan(props);
1269 }
1270
1271 }
1272
1273 }