aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit
diff options
context:
space:
mode:
authorThomas Wolf <thomas.wolf@paranor.ch>2019-10-23 00:55:28 +0200
committerThomas Wolf <thomas.wolf@paranor.ch>2019-10-23 11:17:24 +0200
commit6a39da37fefd036929df9e63bf42875abd028b1f (patch)
tree55a7668caefd240dfb485c99f18dd331728be1ba /org.eclipse.jgit/src/org/eclipse/jgit
parentd4404fb438c4cb31725a98e876cef8e3218ebd7f (diff)
downloadjgit-6a39da37fefd036929df9e63bf42875abd028b1f.tar.gz
jgit-6a39da37fefd036929df9e63bf42875abd028b1f.zip
GPG: implement more OpenPGP UserId matching formats
Instead of just looking for a substring match of user.signingKey in a key's user ID implement the GPG matching formats[1] for: '=' Full exact match '<' Full exact match of the e-mail address '@' Substring match within the e-mail address only '*' General case-insensitive substring match (default) When user.signingKey is not set, the committer's e-mail address is used by default. In that case, use '<', i.e., require an exact match on the OpenPGP e-mail address. Also handle the optional "0x" prefix for (partial) key fingerprints. [1] https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html Bug: 550335 Change-Id: I6ce482a099ff1a0dc9de45435cd4d3ec5b504f12 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java92
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java2
2 files changed, 84 insertions, 10 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
index e3233590f6..f28334cb80 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
@@ -92,6 +92,7 @@ import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -192,14 +193,87 @@ class BouncyCastleGpgKeyLocator {
}
}
- private boolean containsSigningKey(String userId) {
- return userId.toLowerCase(Locale.ROOT)
- .contains(signingKey.toLowerCase(Locale.ROOT));
+ /**
+ * Checks whether a given OpenPGP {@code userId} matches a given
+ * {@code signingKeySpec}, which is supposed to have one of the formats
+ * defined by GPG.
+ * <p>
+ * Not all formats are supported; only formats starting with '=', '&lt;',
+ * '@', and '*' are handled. Any other format results in a case-insensitive
+ * substring match.
+ * </p>
+ *
+ * @param userId
+ * of a key
+ * @param signingKeySpec
+ * GPG key identification
+ * @return whether the {@code userId} matches
+ * @see <a href=
+ * "https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html">GPG
+ * Documentation: How to Specify a User ID</a>
+ */
+ static boolean containsSigningKey(String userId, String signingKeySpec) {
+ if (StringUtils.isEmptyOrNull(userId)
+ || StringUtils.isEmptyOrNull(signingKeySpec)) {
+ return false;
+ }
+ String toMatch = signingKeySpec;
+ if (toMatch.startsWith("0x") && toMatch.trim().length() > 2) { //$NON-NLS-1$
+ return false; // Explicit fingerprint
+ }
+ int command = toMatch.charAt(0);
+ switch (command) {
+ case '=':
+ case '<':
+ case '@':
+ case '*':
+ toMatch = toMatch.substring(1);
+ if (toMatch.isEmpty()) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ switch (command) {
+ case '=':
+ return userId.equals(toMatch);
+ case '<': {
+ int begin = userId.indexOf('<');
+ int end = userId.indexOf('>', begin + 1);
+ int stop = toMatch.indexOf('>');
+ return begin >= 0 && end > begin + 1 && stop > 0
+ && userId.substring(begin + 1, end)
+ .equals(toMatch.substring(0, stop));
+ }
+ case '@': {
+ int begin = userId.indexOf('<');
+ int end = userId.indexOf('>', begin + 1);
+ return begin >= 0 && end > begin + 1
+ && userId.substring(begin + 1, end).contains(toMatch);
+ }
+ default:
+ if (toMatch.trim().isEmpty()) {
+ return false;
+ }
+ return userId.toLowerCase(Locale.ROOT)
+ .contains(toMatch.toLowerCase(Locale.ROOT));
+ }
+ }
+
+ private String toFingerprint(String keyId) {
+ if (keyId.startsWith("0x")) { //$NON-NLS-1$
+ return keyId.substring(2);
+ }
+ return keyId;
}
private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
throws IOException {
- String keyId = signingKey.toLowerCase(Locale.ROOT);
+ String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
+ if (keyId.isEmpty()) {
+ return null;
+ }
for (KeyInformation keyInfo : keyBlob.getKeyInformation()) {
String fingerprint = Hex.toHexString(keyInfo.getFingerprint())
.toLowerCase(Locale.ROOT);
@@ -213,7 +287,7 @@ class BouncyCastleGpgKeyLocator {
private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob)
throws IOException {
for (UserID userID : keyBlob.getUserIds()) {
- if (containsSigningKey(userID.getUserIDAsString())) {
+ if (containsSigningKey(userID.getUserIDAsString(), signingKey)) {
return getSigningPublicKey(keyBlob);
}
}
@@ -446,7 +520,7 @@ class BouncyCastleGpgKeyLocator {
PGPUtil.getDecoderStream(new BufferedInputStream(in)),
new JcaKeyFingerprintCalculator());
- String keyId = signingkey.toLowerCase(Locale.ROOT);
+ String keyId = toFingerprint(signingkey).toLowerCase(Locale.ROOT);
Iterator<PGPSecretKeyRing> keyrings = pgpSec.getKeyRings();
while (keyrings.hasNext()) {
PGPSecretKeyRing keyRing = keyrings.next();
@@ -464,7 +538,7 @@ class BouncyCastleGpgKeyLocator {
Iterator<String> userIDs = key.getUserIDs();
while (userIDs.hasNext()) {
String userId = userIDs.next();
- if (containsSigningKey(userId)) {
+ if (containsSigningKey(userId, signingKey)) {
return key;
}
}
@@ -492,7 +566,7 @@ class BouncyCastleGpgKeyLocator {
new BufferedInputStream(in),
new JcaKeyFingerprintCalculator());
- String keyId = signingKey.toLowerCase(Locale.ROOT);
+ String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
Iterator<PGPPublicKeyRing> keyrings = pgpPub.getKeyRings();
while (keyrings.hasNext()) {
PGPPublicKeyRing keyRing = keyrings.next();
@@ -509,7 +583,7 @@ class BouncyCastleGpgKeyLocator {
Iterator<String> userIDs = key.getUserIDs();
while (userIDs.hasNext()) {
String userId = userIDs.next();
- if (containsSigningKey(userId)) {
+ if (containsSigningKey(userId, signingKey)) {
return key;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
index cfe0931b47..cfa67eefdc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
@@ -115,7 +115,7 @@ public class BouncyCastleGpgSigner extends GpgSigner {
NoSuchAlgorithmException, NoSuchProviderException, PGPException,
URISyntaxException {
if (gpgSigningKey == null || gpgSigningKey.isEmpty()) {
- gpgSigningKey = committer.getEmailAddress();
+ gpgSigningKey = '<' + committer.getEmailAddress() + '>';
}
BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator(