summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/BUILD2
-rw-r--r--org.eclipse.jgit.gpg.bc.test/.classpath9
-rw-r--r--org.eclipse.jgit.gpg.bc.test/.gitignore1
-rw-r--r--org.eclipse.jgit.gpg.bc.test/BUILD20
-rw-r--r--org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.jgit.gpg.bc.test/build.properties2
-rw-r--r--org.eclipse.jgit.gpg.bc.test/pom.xml6
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc14
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc17
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc19
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc44
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc9
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc14
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc16
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc19
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc40
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc14
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc13
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java61
-rw-r--r--org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java143
-rw-r--r--org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF13
-rw-r--r--org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties8
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java6
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java112
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java322
25 files changed, 869 insertions, 67 deletions
diff --git a/lib/BUILD b/lib/BUILD
index b56ba2f66d..8ad49d450d 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -162,6 +162,7 @@ java_library(
"//org.eclipse.jgit:__pkg__",
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
+ "//org.eclipse.jgit.gpg.bc.test:__pkg__",
],
exports = ["@bcpg//jar"],
)
@@ -172,6 +173,7 @@ java_library(
"//org.eclipse.jgit:__pkg__",
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
+ "//org.eclipse.jgit.gpg.bc.test:__pkg__",
],
exports = ["@bcprov//jar"],
)
diff --git a/org.eclipse.jgit.gpg.bc.test/.classpath b/org.eclipse.jgit.gpg.bc.test/.classpath
index f08af0a4e9..0acccbaaec 100644
--- a/org.eclipse.jgit.gpg.bc.test/.classpath
+++ b/org.eclipse.jgit.gpg.bc.test/.classpath
@@ -2,10 +2,15 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="tst">
+ <classpathentry kind="src" output="bin-tst" path="tst">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
- <classpathentry kind="output" path="bin"/>
+ <classpathentry kind="src" output="bin-tst" path="tst-rsrc">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin-tst"/>
</classpath>
diff --git a/org.eclipse.jgit.gpg.bc.test/.gitignore b/org.eclipse.jgit.gpg.bc.test/.gitignore
index 934e0e06ff..8b6760c93c 100644
--- a/org.eclipse.jgit.gpg.bc.test/.gitignore
+++ b/org.eclipse.jgit.gpg.bc.test/.gitignore
@@ -1,2 +1,3 @@
/bin
+/bin-tst
/target
diff --git a/org.eclipse.jgit.gpg.bc.test/BUILD b/org.eclipse.jgit.gpg.bc.test/BUILD
index 1e3677d929..59859b2837 100644
--- a/org.eclipse.jgit.gpg.bc.test/BUILD
+++ b/org.eclipse.jgit.gpg.bc.test/BUILD
@@ -1,4 +1,9 @@
load(
+ "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl",
+ "genrule2",
+)
+load("@rules_java//java:defs.bzl", "java_import")
+load(
"@com_googlesource_gerrit_bazlets//tools:junit.bzl",
"junit_tests",
)
@@ -8,7 +13,22 @@ junit_tests(
srcs = glob(["tst/**/*.java"]),
tags = ["bc"],
deps = [
+ "//lib:bcpg",
+ "//lib:bcprov",
"//lib:junit",
"//org.eclipse.jgit.gpg.bc:gpg-bc",
+ "//org.eclipse.jgit.gpg.bc.test:tst_rsrc",
],
)
+
+java_import(
+ name = "tst_rsrc",
+ jars = [":tst_rsrc_jar"],
+)
+
+genrule2(
+ name = "tst_rsrc_jar",
+ srcs = glob(["tst-rsrc/**"]),
+ outs = ["tst_rsrc.jar"],
+ cmd = "o=$$PWD/$@ && tar cf - $(SRCS) | tar -C $$TMP --strip-components=2 -xf - && cd $$TMP && zip -qr $$o .",
+)
diff --git a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
index 35a418c5a2..39ece1fcf0 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -7,8 +7,16 @@ Bundle-Version: 5.11.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.gpg.bc.internal;version="[5.11.0,5.12.0)",
- org.junit;version="[4.13,5.0.0)"
+Import-Package: org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.sha1;version="[5.11.0,5.12.0)",
+ org.junit;version="[4.13,5.0.0)",
+ org.junit.runner;version="[4.13,5.0.0)",
+ org.junit.runners;version="[4.13,5.0.0)"
Export-Package: org.eclipse.jgit.gpg.bc.internal;x-internal:=true
Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
org.hamcrest.library;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.gpg.bc.test/build.properties b/org.eclipse.jgit.gpg.bc.test/build.properties
index 9ffa0caf78..e36d6667ee 100644
--- a/org.eclipse.jgit.gpg.bc.test/build.properties
+++ b/org.eclipse.jgit.gpg.bc.test/build.properties
@@ -1,5 +1,5 @@
source.. = tst/
-output.. = bin/
+output.. = bin-tst/
bin.includes = META-INF/,\
.,\
plugin.properties
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index f244fb4769..cac7e151e9 100644
--- a/org.eclipse.jgit.gpg.bc.test/pom.xml
+++ b/org.eclipse.jgit.gpg.bc.test/pom.xml
@@ -85,6 +85,12 @@
<sourceDirectory>src/</sourceDirectory>
<testSourceDirectory>tst/</testSourceDirectory>
+ <testResources>
+ <testResource>
+ <directory>tst-rsrc/</directory>
+ </testResource>
+ </testResources>
+
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc
new file mode 100644
index 0000000000..8427cfcc05
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mFMEWsODvRMJKyQDAwIIAQEHAgMEQLOxiiHZ/V6v3kvrhbnRtTp+oOPVpuvDKOiy
+gJOCZ7EWMVAwTr4syaSh8W8hdRgZ85Evv/1PYNFovYb6vzgVr7QJZWNjLWJwMjU2
+iJQEExMIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheAFiEEBjPF9yoZ
+j1HmUOSr0Mij2vngY0oFAlxVsKIACgkQ0Mij2vngY0pARAD/RozGDidH/0aFlxeU
+VWNJKjPiax6vdHqur5dqBS/RhhIA/1sPUnyAIvAXXID1uhK6oIBRKi7WJ5rI7vSy
+rBR5MlNJuFcEWsODvRIJKyQDAwIIAQEHAgMEE9Vd8dIjHJkmRs/8MLz4Krfwz5BK
+hunq1T0xnp65OEZJd00VxA+VUXdEUHfaDehtSv7izCpq4lbXGCkEGFN7QwMBCAeI
+eAQYEwgAIAIbDBYhBAYzxfcqGY9R5lDkq9DIo9r54GNKBQJcVbCpAAoJENDIo9r5
+4GNK0MYA/2p5cq5smjSvKD/EGkosQwfcqkeygsQuEpDDLeEdsv8vAP9j+RHKX2tl
+W08zbayxGB0E+aCHuKCF8iLPeI4eroi/fw==
+=vsa4
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc
new file mode 100644
index 0000000000..bdb20fe939
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mHMEWsOEUBMJKyQDAwIIAQELAwMEivcvlPJsPmivhJcrfHx+ORxyum57GtRhWM49
+Yr8fJ48gyFqj9cLYOBdhEVvcfceyBPXmyt0TozWtjkGzgbF4LIvN1EB0DW0Rlsdn
+p72/hf0gnXvWZdD8euArX4RaAYuQtAllY2MtYnAzODSItAQTEwkAPAIbAwULCQgH
+AgMiAgEGFQoJCAsCBBYCAwECHgMCF4AWIQRbiiVMgjztmN7NEO1s8tzoVZmtogUC
+XFWwugAKCRBs8tzoVZmtoj1yAX9P1UV7FYpGUIP13aPP0d5Bx8HdQDAoexdXz3WW
+WPL/7OhSjPde23Q8TfgWyO21M2wBf1oWjOsDSjO5mDLCr7ypAFF6IJAgx76tSUe9
+Qmy7sL94OWDQ4+1Dccnc9GGiHLtRI7h3BFrDhFASCSskAwMCCAEBCwMDBETUkqGr
+7p8kX2dm38jzzxXRh1+OL7nmY168Zeo9yfwDbnyx8BoihP9ZgPWjGXmefT78GSfw
+ZDaYgC2NFQOcI/b8agh3PcjrXgZaFCZbUR9v2DnLUpCF8ZbxDJwEqweNTAMBCQmI
+mAQYEwkAIAIbDBYhBFuKJUyCPO2Y3s0Q7Wzy3OhVma2iBQJcVbDCAAoJEGzy3OhV
+ma2ig1IBfifduIiwdAlD45MOolSpHMX0AT7KoJHpt9ZFvWnjQkq9ZGEA/RA9vx7Z
+sLb7IsG1GgF/Sn+gtf/JIteXaZMnOhEOZ2oFUufij6o8gII8/9s8mkIjkrIICy//
+0n3q82ndFg0c
+=fcpz
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc
new file mode 100644
index 0000000000..5b4bca2c67
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mJMEWsOEhxMJKyQDAwIIAQENBAMEA28ylDnn3LR7t6EQow5DszCpmUA+yup03LsT
+9o0Tw4/asi8nAz+1tlRY5HD49j53PziOlsGzKYa/jxGWjhVESgqLrJp/Eo65zK9v
+yDhX+iCkSYQ15WGKr3QaRUmBOUbX9PqE6dY+DDGQ1kewI93QIGCB1gn+OSmyKPm3
+YaVIbo60CWVjYy1icDUxMojUBBMTCgA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBExZq5JyqmofYLhb0KpcWNFPe49IBQJcVbDYAAoJEKpcWNFPe49I
+F8UB/i8DwypbNusdbAqTbu1Twpn/QFMaVKHn8Ysgzqpimv+6hRq7uzekCvEOPOjl
+Oke5yLp8bpTTMPRKyfjNatQhdn8B/2+54qtXJuQd9oTSz7f2eFYvA8ZsMQgApYNl
+ksvKSw6dhSNX/DXK7JYIlgZXx7UGTkP4h3FQSiyCUJhVpClVVGa4lwRaw4SHEgkr
+JAMDAggBAQ0EAwRCtEqQkEgzQDxNGCj0duj0aGvnH+DHKlP4V6p9LJVIL0TuF1uZ
+BzP04efRvZT2vzCTcvvdE/G6G/txEZsR/989OchbkVWOPM/oCVktkaA02rBMcefh
+k9wKD+O9E3oHEN+tBt3yhmsv0MIR9IfPwi1GCsu55p4WUI//+ysB2T0YaQMBCgmI
+uAQYEwoAIAIbDBYhBExZq5JyqmofYLhb0KpcWNFPe49IBQJcVbDgAAoJEKpcWNFP
+e49IZQUB/R4U4aWBMimZSL8kyaK+/Y8NcInIGqRzrm5zPnTSHrgQeH55SVKahzsq
+j57D1Ec1HnUd4ctISVocOxpUfnJq5NAB/1fzbh+1RN2ZyNW6tAJlA/Irkwzzbil9
+6fAIvRolwwaGsUZNMEiCF3rTcFaenJg9UhQvX6BoqXCdwawqTZCRN6E=
+=h+On
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc
new file mode 100644
index 0000000000..db067325cb
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc
@@ -0,0 +1,44 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+v5BhpoOOm+jM
+=PnGk
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc
new file mode 100644
index 0000000000..636a5a95f3
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc
@@ -0,0 +1,9 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc
new file mode 100644
index 0000000000..fd1509e9ba
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mFIEWsOCNRMIKoZIzj0DAQcCAwQS5G6mn5dhamZ6678SXE1azavqf8BItWO9Qv8V
+dS1vEEoD14urr5OQKTLuHhDRjvSQdaxRtkf0sI51T7230sT3tAhlY2MtcDI1NoiU
+BBMTCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgIDAQIeAwIXgBYhBLVP3ru2c0I6
+XQqlRCNnTyGyRBUnBQJcVa/nAAoJECNnTyGyRBUn1ycA+wVg9sEfHDBaGtLqlUSB
+WdGKURrHN7CJe2UTz1/7oQCBAQDDi4RQyLHs+TfOrBNSbLEswCu1oEh8VmHt/SN7
++mqNLbhWBFrDgjUSCCqGSM49AwEHAgMELDOArLIG85ABQu1IwgQMpiIuUwj+N7ib
+gGenTRck5dkBpX48eK3lbjovXn4YkBneA7z14iez3+Sdg6UFAMFV2QMBCAeIeAQY
+EwgAIAIbDBYhBLVP3ru2c0I6XQqlRCNnTyGyRBUnBQJcVa/vAAoJECNnTyGyRBUn
+ZKoBAJ64gv3w27nFBERvIsRqufvR6xcimqS7Gif+WehBU+P5AQC5bqoISh0oSQid
+adI84f60RuOaozpjvR3B1bPZiR6u7w==
+=H2xn
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc
new file mode 100644
index 0000000000..b2b59959e6
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mG8EWsOCnBMFK4EEACIDAwTRTCLBHlq6SUTiXZfWR0vbUh/0VAlrePaqHVIE4LEK
+0UBhPCIQOGuGL4JIufc8UzBWhiMLJ0z7KRjBWufsMZR2+rqj+unOK2lLu7sc9or8
+X6B74hhP3Ili24PgGFAeAG+0CGVjYy1wMzg0iLQEExMJADwCGwMFCwkIBwIDIgIB
+BhUKCQgLAgQWAgMBAh4DAheAFiEEqyXLoELdkkw6zD7TJCo6peqF9EoFAlxVsBEA
+CgkQJCo6peqF9EooJQF7BPZelriXwZ/kJzaamImHBddkLFc7d2WbuSfDxEZQ+Mfw
+BAP3+QYUaFtfeqApjY69AX4w6LhTUgI2kl4O0Vc7ZOlqZBlwAc8CMV08TTfOEio2
+b51SItvhLdDrFRJ2K4jiO+a4cwRaw4KcEgUrgQQAIgMDBORWqhYflSrYzF04SK8q
+8Om+DYTvwRtUlr3Aoq44+gm5yBcmJmgT3TKrp/bx5Jg/zwzIASFn0agbxkqKpQqH
+sHeelWsSBROQzy98HXdCp3nVmghI2aDk8zdD6AV4m7c2ewMBCQmImAQYEwkAIAIb
+DBYhBKsly6BC3ZJMOsw+0yQqOqXqhfRKBQJcVbAZAAoJECQqOqXqhfRKgAIBf3Wk
+TsqUA1JXkPGetA9sjHglIICN+DZY5k+PwTJUxaW2zrkiPJ3BYEnKbmmBLzA7BgGA
+4RYatyl2WOUYh/poRLgu7JpE4oRqdmNA+QOpCILMId1AeXfj4W01RKFWaKeH+3Yy
+=2H/0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc
new file mode 100644
index 0000000000..db18f81a29
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mJMEWsODGxMFK4EEACMEIwQA8OZCJ8Iv4Qr2oRr0kqez0nPSW/vNxYBZRpCJ9ab8
+kVaRhW7+5vEsecm5XugZSePbAmERmk3oEiSgu35r6aovowcAGrOBfBm9fyIVqaoX
+veTS3yRHH6NEf044+pC+uBaaFukkrDmVTecdRvYr3Yrdc5ifyOve053znlpQ6a4n
+9bh4GGy0CGVjYy1wNTIxiNYEExMKADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMB
+Ah4DAheAFiEET7Of9vpIV6S9fvW0IJLKgyQmO2oFAlxVsCkACgkQIJLKgyQmO2oK
+DwIGO72zo6otVkbHfeI9hWx/8FAOXh4MT4YtDicF/sj8QbHzdbEBHcLCByLYAnph
+8VVoCxpPcBLmNSHbNyreoksjEE0CB10P5kPrd/bYkdNx/HTu+1i8f7a448+Nr2rF
+PwdI9tOsghkT41qobZavjjnBlT/xv5DqXldJkEfaeiJxPHOKvMhWuJcEWsODGxIF
+K4EEACMEIwQBAY7ZCAjks1MWWxibg/EVaz5t6iEKJTwu8mGGKWdPZAQRKKNtNpf0
+pZAMV3I8ue/WQMsYKRYv5AGq1PnjV19DmLsA0aGw4MDM260coctkcn/2MAJQMC9+
+3Z+BJS3hqzwDuZ+LS13r0RLpgnt3ks+3ucG4II38ZZ1lTwKoIc+w/OuhsOIDAQoJ
+iLsEGBMKACACGwwWIQRPs5/2+khXpL1+9bQgksqDJCY7agUCXFWwLgAKCRAgksqD
+JCY7ahqbAgkBiXYtiBlp5dmSYnbc4JoIYWcxTBQ+/dGHyU6ZEfC5VQz2mrdJetK1
+bIID0rFSsd24/8IzAqM3L+nY9h9bULWroroCBjTohh0j2EbW+hFOrRqL01osnlY+
+1/G8e44blB5JqsPI9FqOZOUj6IzsUuV1N9gJbm1RHu/hSpm52d6rX4nOTbqt
+=3jnl
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc
new file mode 100644
index 0000000000..e74df7a2a9
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc
@@ -0,0 +1,40 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=aibx
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc
new file mode 100644
index 0000000000..837f8a867e
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mE8EWsOE8xMFK4EEAAoCAwQVHqFZWqedXUIkNFs82PsQB3bsCDhrL/73xZca3+vo
+kB4T7jHcACThuMZYuUqUo9NzNTJioluOvZG+UdYXPdfdtAplY2MtcDI1NmsxiJQE
+ExMIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheAFiEEgfdytX1Ov+cA
+CmYjPqW7b5aSwaAFAlxVsQ0ACgkQPqW7b5aSwaD2tQD/R4d15NBuSJ6IB1brH0E9
+nEWkqo892PaAY5akdCO/i9EBAMsjE5NPxBnCs03c+VHFU200k27ixdrWpUa+HZEI
+A5wSuFMEWsOE8xIFK4EEAAoCAwSUWwe7CaaOYRANiKet2evLiOumefIHuvRpyOSK
+hyRdclIWpBUCAWEnmalkEL/8cEM5fjtILtCOKXqCOBsPv45HAwEIB4h4BBgTCAAg
+AhsMFiEEgfdytX1Ov+cACmYjPqW7b5aSwaAFAlxVsRUACgkQPqW7b5aSwaCETgD/
+YXzCMYMbPGAU2oTitjAno8hDWmgTeaFWeCmqf6l9mP8BAKvpewWeFGZfWGAQcWPi
+E+jv7vadvEt1yMA8rmT041F5
+=mDCI
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc
new file mode 100644
index 0000000000..d531d7a8ba
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEW8SVGhYJKwYBBAHaRw8BAQdA9SMZ2uw0YugMFcl5TpEZeBRAGniEk9a42XNs
+7QA4Tky0DGVkZHNhLXgyNTUxOYiQBBMWCAA4FiEETJc4pvK+Thp5bJt7lBgioPwb
+MKUFAlvElRoCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQlBgioPwbMKUi
+1wEAgMq3X7o17OJBPfY3He/exDR6LhWwAAXrVQR/WdRiHkEBALd1Mj0BlZZLoKTr
+uJ4MD5CYZLicXTRwOv6e52F/DHwJuDgEW8SVGhIKKwYBBAGXVQEFAQEHQA0Lh2mG
+lB1O4xDYgztm/aX7+8AdHEGaMsCF1RQ6wVUeAwEIB4h4BBgWCAAgFiEETJc4pvK+
+Thp5bJt7lBgioPwbMKUFAlvElRoCGwwACgkQlBgioPwbMKXmlQD+KxVg2dGL8lRW
+rQajwzmuwMrJX1lvJylg5Ozk6SGrBeABANZrdt8bmArEqeRVxFO2F4P7btyIpf1w
+5aNpqqtvkRcB
+=EYfV
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java
new file mode 100644
index 0000000000..e30080258f
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+import java.util.Locale;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.util.sha1.SHA1;
+import org.junit.Test;
+
+public class KeyGrip25519Test {
+
+ interface Hash {
+ byte[] hash(SHA1 sha, BigInteger q) throws PGPException;
+ }
+
+ private void assertKeyGrip(String key, String expectedKeyGrip, Hash hash)
+ throws Exception {
+ SHA1 grip = SHA1.newInstance();
+ grip.setDetectCollision(false);
+ BigInteger pk = new BigInteger(key, 16);
+ byte[] keyGrip = hash.hash(grip, pk);
+ assertEquals("Keygrip should match", expectedKeyGrip,
+ Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT));
+ }
+
+ @Test
+ public void testCompressed() throws Exception {
+ assertKeyGrip("40"
+ + "773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB",
+ "9DB6C64A38830F4960701789475520BE8C821F47",
+ KeyGrip::hashEd25519);
+ }
+
+ @Test
+ public void testCompressedNoPrefix() throws Exception {
+ assertKeyGrip(
+ "773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB",
+ "9DB6C64A38830F4960701789475520BE8C821F47",
+ KeyGrip::hashEd25519);
+ }
+
+ @Test
+ public void testCurve25519() throws Exception {
+ assertKeyGrip("40"
+ + "918C1733127F6BF2646FAE3D081A18AE77111C903B906310B077505EFFF12740",
+ "0F89A565D3EA187CE839332398F5D480677DF49C",
+ KeyGrip::hashCurve25519);
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java
new file mode 100644
index 0000000000..a4aaf40d9b
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeyGripTest {
+
+ @BeforeClass
+ public static void ensureBC() {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
+
+ protected static class TestData {
+
+ String filename;
+
+ String[] expectedKeyGrips;
+
+ TestData(String filename, String... keyGrips) {
+ this.filename = filename;
+ this.expectedKeyGrips = keyGrips;
+ }
+
+ @Override
+ public String toString() {
+ return filename;
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static TestData[] initTestData() {
+ return new TestData[] {
+ new TestData("rsa.asc",
+ "D148210FAF36468055B83D0F5A6DEB83FBC8E864",
+ "A5E4CD2CBBE44A16E4D6EC05C2E3C3A599DC763C"),
+ new TestData("dsa-elgamal.asc",
+ "552286BEB2999F0A9E26A50385B90D9724001187",
+ "CED7034A8EB5F4CE90DF99147EC33D86FCD3296C"),
+ new TestData("brainpool256.asc",
+ "A01BAA22A72F09A0FF0A1D4CBCE70844DD52DDD7",
+ "C1678B7DE5F144C93B89468D5F9764ACE182ED36"),
+ new TestData("brainpool384.asc",
+ "2F25DB025DEBF3EA2715350209B985829B04F50A",
+ "B6BD8B81F75AF914163D97DF8DE8F6FC64C283F8"),
+ new TestData("brainpool512.asc",
+ "5A484F56AB4B8B6583B6365034999F6543FAE1AE",
+ "9133E4A7E8FC8515518DF444C3F2F247EEBBADEC"),
+ new TestData("nistp256.asc",
+ "FC81AECE90BCE6E54D0D637D266109783AC8DAC0",
+ "A56DC8DB8355747A809037459B4258B8A743EAB5"),
+ new TestData("nistp384.asc",
+ "A1338230AED1C9C125663518470B49056C9D1733",
+ "797A83FE041FFE06A7F4B1D32C6F4AE0F6D87ADF"),
+ new TestData("nistp521.asc",
+ "D91B789603EC9138AA20342A2B6DC86C81B70F5D",
+ "FD048B2CA1919CB241DC8A2C7FA3E742EF343DCA"),
+ new TestData("secp256k1.asc",
+ "498B89C485489BA16B40755C0EBA580166393074",
+ "48FFED40D018747363BDEFFDD404D1F4870F8064"),
+ new TestData("ed25519.asc",
+ "940D97D75C306D737A59A98EAFF1272832CEDC0B"),
+ new TestData("x25519.asc",
+ "A77DC8173DA6BEE126F5BD6F5A14E01200B52FCE",
+ "636C983EDB558527BA82780B52CB5DAE011BE46B")
+ };
+ }
+
+ // Injected by JUnit
+ @Parameter
+ public TestData data;
+
+ private void readAsc(InputStream in, Consumer<PGPPublicKey> process)
+ throws IOException, PGPException {
+ PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
+ PGPUtil.getDecoderStream(in), new JcaKeyFingerprintCalculator());
+
+ Iterator<PGPPublicKeyRing> keyRings = pgpPub.getKeyRings();
+ while (keyRings.hasNext()) {
+ PGPPublicKeyRing keyRing = keyRings.next();
+
+ Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
+ while (keys.hasNext()) {
+ process.accept(keys.next());
+ }
+ }
+ }
+
+ @Test
+ public void testGrip() throws Exception {
+ try (InputStream in = this.getClass()
+ .getResourceAsStream(data.filename)) {
+ int index[] = { 0 };
+ readAsc(in, key -> {
+ byte[] keyGrip = null;
+ try {
+ keyGrip = KeyGrip.getKeyGrip(key);
+ } catch (PGPException e) {
+ throw new RuntimeException(e);
+ }
+ assertTrue("More keys than expected",
+ index[0] < data.expectedKeyGrips.length);
+ assertEquals("Wrong keygrip", data.expectedKeyGrips[index[0]++],
+ Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT));
+ });
+ assertEquals("Missing keys", data.expectedKeyGrips.length,
+ index[0]);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
index e5f432dc57..b379a2b47e 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -8,12 +8,19 @@ Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-Version: 5.11.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
+Import-Package: org.bouncycastle.asn1;version="[1.65.0,2.0.0)",
+ org.bouncycastle.asn1.cryptlib;version="[1.65.0,2.0.0)",
+ org.bouncycastle.asn1.x9;version="[1.65.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
org.bouncycastle.bcpg.sig;version="[1.65.0,2.0.0)",
+ org.bouncycastle.crypto.ec;version="[1.65.0,2.0.0)",
org.bouncycastle.gpg;version="[1.65.0,2.0.0)",
org.bouncycastle.gpg.keybox;version="[1.65.0,2.0.0)",
org.bouncycastle.gpg.keybox.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.jcajce.interfaces;version="[1.65.0,2.0.0)",
org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
+ org.bouncycastle.math.ec;version="[1.65.0,2.0.0)",
+ org.bouncycastle.math.field;version="[1.65.0,2.0.0)",
org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
org.bouncycastle.openpgp.jcajce;version="[1.65.0,2.0.0)",
org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)",
@@ -27,5 +34,5 @@ Import-Package: org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc.internal;version="5.11.0";
- x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc.internal;version="5.11.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="5.11.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
diff --git a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
index 83ed9059ec..f2aa014d6b 100644
--- a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
+++ b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
@@ -1,6 +1,8 @@
+corrupt25519Key=Ed25519/Curve25519 public key has wrong length: {0}
credentialPassphrase=Passphrase
-gpgFailedToParseSecretKey=Failed to parse secret key file in directory: {0}. Is the entered passphrase correct?
+gpgFailedToParseSecretKey=Failed to parse secret key file {0}. Is the entered passphrase correct?
gpgNoCredentialsProvider=missing credentials provider
+gpgNoKeygrip=Cannot find key {0}: cannot determine key grip
gpgNoKeyring=neither pubring.kbx nor secring.gpg files found
gpgNoKeyInLegacySecring=no matching secret key found in legacy secring.gpg for key or user id: {0}
gpgNoPublicKeyFound=Unable to find a public-key with key or user id: {0}
@@ -16,3 +18,7 @@ signatureNoPublicKey=No public key found to verify the signature
signatureParseError=Signature cannot be parsed
signatureVerificationError=Signature verification failed
unableToSignCommitNoSecretKey=Unable to sign commit. Signing key not available.
+uncompressed25519Key=Cannot handle ed25519 public key with uncompressed data: {0}
+unknownCurve=Unknown curve {0}
+unknownCurveParameters=Curve {0} does not have a prime field
+unknownKeyType=Unknown key type {0} \ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
index 9403ba2352..4753ac138d 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
@@ -27,9 +27,11 @@ public final class BCText extends TranslationBundle {
}
// @formatter:off
+ /***/ public String corrupt25519Key;
/***/ public String credentialPassphrase;
/***/ public String gpgFailedToParseSecretKey;
/***/ public String gpgNoCredentialsProvider;
+ /***/ public String gpgNoKeygrip;
/***/ public String gpgNoKeyring;
/***/ public String gpgNoKeyInLegacySecring;
/***/ public String gpgNoPublicKeyFound;
@@ -45,5 +47,9 @@ public final class BCText extends TranslationBundle {
/***/ public String signatureParseError;
/***/ public String signatureVerificationError;
/***/ public String unableToSignCommitNoSecretKey;
+ /***/ public String uncompressed25519Key;
+ /***/ public String unknownCurve;
+ /***/ public String unknownCurveParameters;
+ /***/ public String unknownKeyType;
}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
index 13655c0d55..7f0f32a2a7 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
@@ -27,12 +27,8 @@ import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.text.MessageFormat;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
import java.util.Locale;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.bouncycastle.gpg.SExprParser;
import org.bouncycastle.gpg.keybox.BlobType;
@@ -61,6 +57,7 @@ import org.bouncycastle.util.encoders.Hex;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.gpg.bc.internal.keys.KeyGrip;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -158,15 +155,10 @@ public class BouncyCastleGpgKeyLocator {
private PGPSecretKey attemptParseSecretKey(Path keyFile,
PGPDigestCalculatorProvider calculatorProvider,
PBEProtectionRemoverFactory passphraseProvider,
- PGPPublicKey publicKey) {
+ PGPPublicKey publicKey) throws IOException, PGPException {
try (InputStream in = newInputStream(keyFile)) {
return new SExprParser(calculatorProvider).parseSecretKey(
new BufferedInputStream(in), passphraseProvider, publicKey);
- } catch (IOException | PGPException | ClassCastException e) {
- if (log.isDebugEnabled())
- log.debug("Ignoring unreadable file '{}': {}", keyFile, //$NON-NLS-1$
- e.getMessage(), e);
- return null;
}
}
@@ -472,67 +464,71 @@ public class BouncyCastleGpgKeyLocator {
PGPPublicKey publicKey, Path userKeyboxPath)
throws PGPException, CanceledException, UnsupportedCredentialItem,
URISyntaxException {
- /*
- * this is somewhat brute-force but there doesn't seem to be another
- * way; we have to walk all private key files we find and try to open
- * them
- */
-
- PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
- .build();
-
- try (Stream<Path> keyFiles = Files.walk(USER_SECRET_KEY_DIR)) {
- List<Path> allPaths = keyFiles.filter(Files::isRegularFile)
- .collect(Collectors.toCollection(ArrayList::new));
- if (allPaths.isEmpty()) {
- return null;
- }
+ byte[] keyGrip = null;
+ try {
+ keyGrip = KeyGrip.getKeyGrip(publicKey);
+ } catch (PGPException e) {
+ throw new PGPException(
+ MessageFormat.format(BCText.get().gpgNoKeygrip,
+ Hex.toHexString(publicKey.getFingerprint())),
+ e);
+ }
+ String filename = Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT)
+ + ".key"; //$NON-NLS-1$
+ Path keyFile = USER_SECRET_KEY_DIR.resolve(filename);
+ if (!Files.exists(keyFile)) {
+ return null;
+ }
+ boolean clearPrompt = false;
+ try {
+ PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
+ .build();
PBEProtectionRemoverFactory passphraseProvider = p -> {
throw new EncryptedPgpKeyException();
};
- for (int attempts = 0; attempts < 2; attempts++) {
- // Second pass will traverse only the encrypted keys with a real
- // passphrase provider.
- Iterator<Path> pathIterator = allPaths.iterator();
- while (pathIterator.hasNext()) {
- Path keyFile = pathIterator.next();
- try {
- PGPSecretKey secretKey = attemptParseSecretKey(keyFile,
- calculatorProvider, passphraseProvider,
- publicKey);
- pathIterator.remove();
- if (secretKey != null) {
- if (!secretKey.isSigningKey()) {
- throw new PGPException(MessageFormat.format(
- BCText.get().gpgNotASigningKey,
- signingKey));
- }
- return new BouncyCastleGpgKey(secretKey,
- userKeyboxPath);
- }
- } catch (EncryptedPgpKeyException e) {
- // Ignore; we'll try again.
- }
- }
- if (attempts > 0 || allPaths.isEmpty()) {
- break;
- }
- // allPaths contains only the encrypted keys now.
+ PGPSecretKey secretKey = null;
+ try {
+ // Try without passphrase
+ secretKey = attemptParseSecretKey(keyFile, calculatorProvider,
+ passphraseProvider, publicKey);
+ } catch (EncryptedPgpKeyException e) {
+ // Let's try again with a passphrase
passphraseProvider = new JcePBEProtectionRemoverFactory(
passphrasePrompt.getPassphrase(
publicKey.getFingerprint(), userKeyboxPath));
- }
+ clearPrompt = true;
+ try {
+ secretKey = attemptParseSecretKey(keyFile, calculatorProvider,
+ passphraseProvider, publicKey);
+ } catch (PGPException e1) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().gpgFailedToParseSecretKey,
+ keyFile.toAbsolutePath()), e);
- passphrasePrompt.clear();
+ }
+ }
+ if (secretKey != null) {
+ if (!secretKey.isSigningKey()) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().gpgNotASigningKey, signingKey));
+ }
+ clearPrompt = false;
+ return new BouncyCastleGpgKey(secretKey, userKeyboxPath);
+ }
return null;
} catch (RuntimeException e) {
- passphrasePrompt.clear();
throw e;
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ clearPrompt = false;
+ return null;
} catch (IOException e) {
- passphrasePrompt.clear();
throw new PGPException(MessageFormat.format(
BCText.get().gpgFailedToParseSecretKey,
- USER_SECRET_KEY_DIR.toAbsolutePath()), e);
+ keyFile.toAbsolutePath()), e);
+ } finally {
+ if (clearPrompt) {
+ passphrasePrompt.clear();
+ }
}
}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java
new file mode 100644
index 0000000000..b1d4446010
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.DSAPublicBCPGKey;
+import org.bouncycastle.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.RSAPublicBCPGKey;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.field.FiniteField;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.gpg.bc.internal.BCText;
+import org.eclipse.jgit.util.sha1.SHA1;
+
+/**
+ * Utilities to compute the <em>keygrip</em> of a key. A keygrip is a SHA1 hash
+ * over the public key parameters and is used internally by the gpg-agent to
+ * find the secret key belonging to a public key: the secret key is stored in a
+ * file under ~/.gnupg/private-keys-v1.d/ with a name "&lt;keygrip>.key". While
+ * this storage organization is an implementation detail of GPG, the way
+ * keygrips are computed is not; they are computed by libgcrypt and their
+ * definition is stable.
+ */
+public final class KeyGrip {
+
+ // Some OIDs apparently unknown to BouncyCastle.
+
+ private static String OID_OPENPGP_ED25519 = "1.3.6.1.4.1.11591.15.1"; //$NON-NLS-1$
+
+ private static String OID_RFC8410_CURVE25519 = "1.3.101.110"; //$NON-NLS-1$
+
+ private static String OID_RFC8410_ED25519 = "1.3.101.112"; //$NON-NLS-1$
+
+ private KeyGrip() {
+ // No instantiation
+ }
+
+ /**
+ * Computes the keygrip for a {@link PGPPublicKey}.
+ *
+ * @param publicKey
+ * to get the keygrip of
+ * @return the keygrip
+ * @throws PGPException
+ * if an unknown key type is encountered.
+ */
+ @NonNull
+ public static byte[] getKeyGrip(PGPPublicKey publicKey)
+ throws PGPException {
+ SHA1 grip = SHA1.newInstance();
+ grip.setDetectCollision(false);
+
+ switch (publicKey.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.RSA_GENERAL:
+ case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+ case PublicKeyAlgorithmTags.RSA_SIGN:
+ BigInteger modulus = ((RSAPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey()).getModulus();
+ hash(grip, modulus.toByteArray());
+ break;
+ case PublicKeyAlgorithmTags.DSA:
+ DSAPublicBCPGKey dsa = (DSAPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ hash(grip, dsa.getP().toByteArray(), 'p', true);
+ hash(grip, dsa.getQ().toByteArray(), 'q', true);
+ hash(grip, dsa.getG().toByteArray(), 'g', true);
+ hash(grip, dsa.getY().toByteArray(), 'y', true);
+ break;
+ case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
+ case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ ElGamalPublicBCPGKey eg = (ElGamalPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ hash(grip, eg.getP().toByteArray(), 'p', true);
+ hash(grip, eg.getG().toByteArray(), 'g', true);
+ hash(grip, eg.getY().toByteArray(), 'y', true);
+ break;
+ case PublicKeyAlgorithmTags.ECDH:
+ case PublicKeyAlgorithmTags.ECDSA:
+ case PublicKeyAlgorithmTags.EDDSA:
+ ECPublicBCPGKey ec = (ECPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ ASN1ObjectIdentifier curveOID = ec.getCurveOID();
+ // BC doesn't know these OIDs.
+ if (OID_OPENPGP_ED25519.equals(curveOID.getId())
+ || OID_RFC8410_ED25519.equals(curveOID.getId())) {
+ return hashEd25519(grip, ec.getEncodedPoint());
+ } else if (CryptlibObjectIdentifiers.curvey25519.equals(curveOID)
+ || OID_RFC8410_CURVE25519.equals(curveOID.getId())) {
+ // curvey25519 actually is the OpenPGP OID for Curve25519 and is
+ // known to BC, but the parameters are for the short Weierstrass
+ // form. See https://github.com/bcgit/bc-java/issues/399 .
+ // libgcrypt uses Montgomery form.
+ return hashCurve25519(grip, ec.getEncodedPoint());
+ }
+ X9ECParameters params = getX9Parameters(curveOID);
+ if (params == null) {
+ throw new PGPException(MessageFormat
+ .format(BCText.get().unknownCurve, curveOID.getId()));
+ }
+ // Need to write p, a, b, g, n, q
+ BigInteger q = ec.getEncodedPoint();
+ byte[] g = params.getG().getEncoded(false);
+ BigInteger a = params.getCurve().getA().toBigInteger();
+ BigInteger b = params.getCurve().getB().toBigInteger();
+ BigInteger n = params.getN();
+ BigInteger p = null;
+ FiniteField field = params.getCurve().getField();
+ if (ECAlgorithms.isFpField(field)) {
+ p = field.getCharacteristic();
+ }
+ if (p == null) {
+ // Don't know...
+ throw new PGPException(MessageFormat.format(
+ BCText.get().unknownCurveParameters, curveOID.getId()));
+ }
+ hash(grip, p.toByteArray(), 'p', false);
+ hash(grip, a.toByteArray(), 'a', false);
+ hash(grip, b.toByteArray(), 'b', false);
+ hash(grip, g, 'g', false);
+ hash(grip, n.toByteArray(), 'n', false);
+ if (publicKey.getAlgorithm() == PublicKeyAlgorithmTags.EDDSA) {
+ hashQ25519(grip, q);
+ } else {
+ hash(grip, q.toByteArray(), 'q', false);
+ }
+ break;
+ default:
+ throw new PGPException(
+ MessageFormat.format(BCText.get().unknownKeyType,
+ Integer.toString(publicKey.getAlgorithm())));
+ }
+ return grip.digest();
+ }
+
+ private static void hash(SHA1 grip, byte[] data) {
+ // Need to skip leading zero bytes
+ int i = 0;
+ while (i < data.length && data[i] == 0) {
+ i++;
+ }
+ int length = data.length - i;
+ if (i < data.length) {
+ if ((data[i] & 0x80) != 0) {
+ grip.update((byte) 0);
+ }
+ grip.update(data, i, length);
+ }
+ }
+
+ private static void hash(SHA1 grip, byte[] data, char id, boolean zeroPad) {
+ // Need to skip leading zero bytes
+ int i = 0;
+ while (i < data.length && data[i] == 0) {
+ i++;
+ }
+ int length = data.length - i;
+ boolean addZero = false;
+ if (i < data.length && zeroPad && (data[i] & 0x80) != 0) {
+ addZero = true;
+ }
+ // libgcrypt includes an SExp in the hash
+ String prefix = "(1:" + id + (addZero ? length + 1 : length) + ':'; //$NON-NLS-1$
+ grip.update(prefix.getBytes(StandardCharsets.US_ASCII));
+ // For some items, gcrypt prepends a zero byte if the high bit is set
+ if (addZero) {
+ grip.update((byte) 0);
+ }
+ if (i < data.length) {
+ grip.update(data, i, length);
+ }
+ grip.update((byte) ')');
+ }
+
+ private static void hashQ25519(SHA1 grip, BigInteger q)
+ throws PGPException {
+ byte[] data = q.toByteArray();
+ switch (data[0]) {
+ case 0x04:
+ if (data.length != 65) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().corrupt25519Key, Hex.toHexString(data)));
+ }
+ // Uncompressed: should not occur with ed25519 or curve25519
+ throw new PGPException(MessageFormat.format(
+ BCText.get().uncompressed25519Key, Hex.toHexString(data)));
+ case 0x40:
+ if (data.length != 33) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().corrupt25519Key, Hex.toHexString(data)));
+ }
+ // Compressed; normal case. Skip prefix.
+ hash(grip, Arrays.copyOfRange(data, 1, data.length), 'q', false);
+ break;
+ default:
+ if (data.length != 32) {
+ throw new PGPException(MessageFormat.format(
+ BCText.get().corrupt25519Key, Hex.toHexString(data)));
+ }
+ // Compressed format without prefix. Should not occur?
+ hash(grip, data, 'q', false);
+ break;
+ }
+ }
+
+ /**
+ * Computes the keygrip for an ed25519 public key.
+ * <p>
+ * Package-visible for tests only.
+ * </p>
+ *
+ * @param grip
+ * initialized {@link SHA1}
+ * @param q
+ * the public key's EC point
+ * @return the keygrip
+ * @throws PGPException
+ * if q indicates uncompressed format
+ */
+ @SuppressWarnings("nls")
+ static byte[] hashEd25519(SHA1 grip, BigInteger q) throws PGPException {
+ // For the values, see RFC 7748: https://tools.ietf.org/html/rfc7748
+ // p = 2^255 - 19
+ hash(grip, Hex.decodeStrict(
+ "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
+ 'p', false);
+ // Field: a = 1
+ hash(grip, new byte[] { 0x01 }, 'a', false);
+ // Field: b = 121665/121666 (mod p)
+ // See Berstein et.al., "Twisted Edwards Curves",
+ // https://doi.org/10.1007/978-3-540-68164-9_26
+ hash(grip, Hex.decodeStrict(
+ "2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A"),
+ 'b', false);
+ // Generator point with affine X,Y
+ // @formatter:off
+ // X(P) = 15112221349535400772501151409588531511454012693041857206046113283949847762202
+ // Y(P) = 46316835694926478169428394003475163141307993866256225615783033603165251855960
+ // the "04" signifies uncompressed format.
+ // @formatter:on
+ hash(grip, Hex.decodeStrict("04"
+ + "216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+ + "6666666666666666666666666666666666666666666666666666666666666658"),
+ 'g', false);
+ // order = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
+ hash(grip, Hex.decodeStrict(
+ "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
+ 'n', false);
+ hashQ25519(grip, q);
+ return grip.digest();
+ }
+
+ /**
+ * Computes the keygrip for a curve25519 public key.
+ * <p>
+ * Package-visible for tests only.
+ * </p>
+ *
+ * @param grip
+ * initialized {@link SHA1}
+ * @param q
+ * the public key's EC point
+ * @return the keygrip
+ * @throws PGPException
+ * if q indicates uncompressed format
+ */
+ @SuppressWarnings("nls")
+ static byte[] hashCurve25519(SHA1 grip, BigInteger q) throws PGPException {
+ hash(grip, Hex.decodeStrict(
+ "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
+ 'p', false);
+ // Unclear: RFC 7748 says A = 486662. This value here is (A-2)/4 =
+ // 121665. Compare ecc-curves.c in libgcrypt:
+ // https://github.com/gpg/libgcrypt/blob/361a058/cipher/ecc-curves.c#L146
+ hash(grip, new byte[] { 0x01, (byte) 0xDB, 0x41 }, 'a', false);
+ hash(grip, new byte[] { 0x01 }, 'b', false);
+ // libgcrypt uses the old g.y value before the erratum to RFC 7748 for
+ // the keygrip. The new value would be
+ // 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14. See
+ // https://www.rfc-editor.org/errata/eid4730 and
+ // https://github.com/gpg/libgcrypt/commit/f67b6492e0b0
+ hash(grip, Hex.decodeStrict("04"
+ + "0000000000000000000000000000000000000000000000000000000000000009"
+ + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"),
+ 'g', false);
+ hash(grip, Hex.decodeStrict(
+ "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
+ 'n', false);
+ hashQ25519(grip, q);
+ return grip.digest();
+ }
+
+ private static X9ECParameters getX9Parameters(
+ ASN1ObjectIdentifier curveOID) {
+ X9ECParameters params = CustomNamedCurves.getByOID(curveOID);
+ if (params == null) {
+ params = ECNamedCurveTable.getByOID(curveOID);
+ }
+ return params;
+ }
+
+}