diff options
author | Thomas Wolf <thomas.wolf@paranor.ch> | 2019-10-23 00:55:28 +0200 |
---|---|---|
committer | Thomas Wolf <thomas.wolf@paranor.ch> | 2019-10-23 11:17:24 +0200 |
commit | 6a39da37fefd036929df9e63bf42875abd028b1f (patch) | |
tree | 55a7668caefd240dfb485c99f18dd331728be1ba /org.eclipse.jgit/src/org/eclipse/jgit | |
parent | d4404fb438c4cb31725a98e876cef8e3218ebd7f (diff) | |
download | jgit-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.java | 92 | ||||
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java | 2 |
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 '=', '<', + * '@', 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( |