From 8caaaf956a070864b8e700afc76b5ab9e8c12f36 Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Tue, 29 Dec 2020 10:15:20 +0100 Subject: [PATCH] GPG user ID matching: use case-insensitive matching Although not mentioned in the GPG documentation at [1], GPG uses case-insensitive matching also for the '<' (exact e-mail) and '@' (partial e-mail) operators. Matching for '=' (full exact match) is case-sensitive. Compare [2]. [1] https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html [2] https://dev.gnupg.org/source/gnupg/browse/master/g10/keyring.c;22f7dddc34446a8c3e9eddf6cb281f16802351d7$890 Bug: 547789 Change-Id: I2f5ab65807d5dde3aa00ff032894701bbd8418c9 Signed-off-by: Thomas Wolf --- .../BouncyCastleGpgKeyLocatorTest.java | 11 ++++++----- .../internal/BouncyCastleGpgKeyLocator.java | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java index 744620163..5f4337870 100644 --- a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java +++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019, Thomas Wolf and others + * Copyright (C) 2019, 2020 Thomas Wolf 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 @@ -53,7 +53,7 @@ public void testFullEmail() throws Exception { assertFalse(match(USER_ID, "")); assertFalse(match(USER_ID, "")); assertFalse(match(USER_ID, "")); - assertFalse(match(USER_ID, "")); + assertTrue(match(USER_ID, "")); assertFalse(match(USER_ID.substring(0, USER_ID.length() - 1), "")); assertFalse(match("", "<>")); @@ -72,8 +72,8 @@ public void testPartialEmail() throws Exception { assertFalse(match(USER_ID, "@ ")); assertFalse(match(USER_ID, "@")); assertFalse(match(USER_ID, "@Heine")); - assertFalse(match(USER_ID, "@HeinrichH")); - assertFalse(match(USER_ID, "@Heinrich")); + assertTrue(match(USER_ID, "@HeinrichH")); + assertTrue(match(USER_ID, "@Heinrich")); assertFalse(match("", "@")); assertFalse(match("", "@h")); } @@ -110,6 +110,7 @@ public void testSubstringAsterisk() throws Exception { public void testExplicitFingerprint() throws Exception { assertFalse(match("John Fade ", "0xfade")); assertFalse(match("John Fade <0xfade@example.com>", "0xfade")); + assertFalse(match("John Fade <0xfade@example.com>", "0xFADE")); assertFalse(match("", "0xfade")); } @@ -128,7 +129,7 @@ public void testZeroX() throws Exception { assertTrue(match("John Fade <0xfade@example.com>", "*0xfade")); assertTrue(match("John Fade <0xfade@example.com>", "*0xFADE")); assertTrue(match("John Fade <0xfade@example.com>", "@0xfade")); - assertFalse(match("John Fade <0xfade@example.com>", "@0xFADE")); + assertTrue(match("John Fade <0xfade@example.com>", "@0xFADE")); assertFalse(match("", "0x")); } } 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 eca45072b..1389f8bd1 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 @@ -219,23 +219,34 @@ static boolean containsSigningKey(String userId, String signingKeySpec) { int stop = toMatch.indexOf('>'); return begin >= 0 && end > begin + 1 && stop > 0 && userId.substring(begin + 1, end) - .equals(toMatch.substring(0, stop)); + .equalsIgnoreCase(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); + && containsIgnoreCase(userId.substring(begin + 1, end), + toMatch); } default: if (toMatch.trim().isEmpty()) { return false; } - return userId.toLowerCase(Locale.ROOT) - .contains(toMatch.toLowerCase(Locale.ROOT)); + return containsIgnoreCase(userId, toMatch); } } + private static boolean containsIgnoreCase(String a, String b) { + int alength = a.length(); + int blength = b.length(); + for (int i = 0; i + blength <= alength; i++) { + if (a.regionMatches(true, i, b, 0, blength)) { + return true; + } + } + return false; + } + private String toFingerprint(String keyId) { if (keyId.startsWith("0x")) { //$NON-NLS-1$ return keyId.substring(2);