Use lowercase hex numbers fixes #1692
diff --git a/.gitignore b/.gitignore
index 3207e72ae28..30225ec7764 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.venv
 .coverage
 _build
 .DS_Store
@@ -15,4 +16,4 @@ src/_black_version.py
 .dmypy.json
 *.swp
 .hypothesis/
-venv/
\ No newline at end of file
+venv/
diff --git a/CHANGES.md b/CHANGES.md
index 240abe302a4..67697bd7b07 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,6 +21,8 @@
 
 - Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
 
+- use lowercase hex strings (#1692)
+
 #### _Packaging_
 
 - Self-contained native _Black_ binaries are now provided for releases via GitHub
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 7e13a5d33f5..44edeb0d9f1 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5192,31 +5192,52 @@ def normalize_numeric_literal(leaf: Leaf) -> None:
         # Leave octal and binary literals alone.
         pass
     elif text.startswith("0x"):
-        # Change hex literals to upper case.
-        before, after = text[:2], text[2:]
-        text = f"{before}{after.upper()}"
+        text = format_hex(text)
     elif "e" in text:
-        before, after = text.split("e")
-        sign = ""
-        if after.startswith("-"):
-            after = after[1:]
-            sign = "-"
-        elif after.startswith("+"):
-            after = after[1:]
-        before = format_float_or_int_string(before)
-        text = f"{before}e{sign}{after}"
+        text = format_scientific_notation(text)
     elif text.endswith(("j", "l")):
-        number = text[:-1]
-        suffix = text[-1]
-        # Capitalize in "2L" because "l" looks too similar to "1".
-        if suffix == "l":
-            suffix = "L"
-        text = f"{format_float_or_int_string(number)}{suffix}"
+        text = format_long_or_complex_number(text)
     else:
         text = format_float_or_int_string(text)
     leaf.value = text
 
 
+def format_hex(text: str) -> str:
+    """
+    Formats a hexadecimal string like "0x12b3"
+
+    Uses lowercase because of similarity between "B" and "8", which
+    can cause security issues.
+    see: https://github.com/psf/black/issues/1692
+    """
+
+    before, after = text[:2], text[2:]
+    return f"{before}{after.lower()}"
+
+
+def format_scientific_notation(text: str) -> str:
+    """Formats a numeric string utilizing scentific notation"""
+    before, after = text.split("e")
+    sign = ""
+    if after.startswith("-"):
+        after = after[1:]
+        sign = "-"
+    elif after.startswith("+"):
+        after = after[1:]
+    before = format_float_or_int_string(before)
+    return f"{before}e{sign}{after}"
+
+
+def format_long_or_complex_number(text: str) -> str:
+    """Formats a long or complex string like `10L` or `10j`"""
+    number = text[:-1]
+    suffix = text[-1]
+    # Capitalize in "2L" because "l" looks too similar to "1".
+    if suffix == "l":
+        suffix = "L"
+    return f"{format_float_or_int_string(number)}{suffix}"
+
+
 def format_float_or_int_string(text: str) -> str:
     """Formats a float string like "1.0"."""
     if "." not in text:
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index cdc863ca032..32df01571a7 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -10,7 +10,7 @@
     },
     "attrs": {
       "cli_arguments": [],
-      "expect_formatting_changes": false,
+      "expect_formatting_changes": true,
       "git_clone_url": "https://github.com/python-attrs/attrs.git",
       "long_checkout": false,
       "py_versions": ["all"]
@@ -47,7 +47,7 @@
     },
     "hypothesis": {
       "cli_arguments": [],
-      "expect_formatting_changes": false,
+      "expect_formatting_changes": true,
       "git_clone_url": "https://github.com/HypothesisWorks/hypothesis.git",
       "long_checkout": false,
       "py_versions": ["all"]
@@ -63,7 +63,7 @@
     },
     "pillow": {
       "cli_arguments": [],
-      "expect_formatting_changes": false,
+      "expect_formatting_changes": true,
       "git_clone_url": "https://github.com/python-pillow/Pillow.git",
       "long_checkout": false,
       "py_versions": ["all"]
@@ -77,7 +77,7 @@
     },
     "pyramid": {
       "cli_arguments": [],
-      "expect_formatting_changes": false,
+      "expect_formatting_changes": true,
       "git_clone_url": "https://github.com/Pylons/pyramid.git",
       "long_checkout": false,
       "py_versions": ["all"]
@@ -112,7 +112,7 @@
     },
     "virtualenv": {
       "cli_arguments": [],
-      "expect_formatting_changes": false,
+      "expect_formatting_changes": true,
       "git_clone_url": "https://github.com/pypa/virtualenv.git",
       "long_checkout": false,
       "py_versions": ["all"]
diff --git a/src/blib2to3/pytree.py b/src/blib2to3/pytree.py
index 4b841b768e7..6dba3c7bb15 100644
--- a/src/blib2to3/pytree.py
+++ b/src/blib2to3/pytree.py
@@ -34,7 +34,7 @@
 import sys
 from io import StringIO
 
-HUGE: int = 0x7FFFFFFF  # maximum repeat count, default max
+HUGE: int = 0x7fffffff  # maximum repeat count, default max
 
 _type_reprs: Dict[int, Union[Text, int]] = {}
 
diff --git a/test_requirements.txt b/test_requirements.txt
index 3e65cdb669f..9f69b8edf83 100644
--- a/test_requirements.txt
+++ b/test_requirements.txt
@@ -2,4 +2,4 @@ pytest >= 6.1.1
 pytest-mock >= 3.3.1
 pytest-cases >= 2.3.0
 coverage >= 5.3
-parameterized >= 0.7.4
\ No newline at end of file
+parameterized >= 0.7.4
diff --git a/tests/data/numeric_literals.py b/tests/data/numeric_literals.py
index 254da68d330..06b7f7758ee 100644
--- a/tests/data/numeric_literals.py
+++ b/tests/data/numeric_literals.py
@@ -12,7 +12,7 @@
 x = 123456789E123456789
 x = 123456789J
 x = 123456789.123456789J
-x = 0XB1ACC
+x = 0Xb1aCc
 x = 0B1011
 x = 0O777
 x = 0.000000006
@@ -36,7 +36,7 @@
 x = 123456789e123456789
 x = 123456789j
 x = 123456789.123456789j
-x = 0xB1ACC
+x = 0xb1acc
 x = 0b1011
 x = 0o777
 x = 0.000000006
diff --git a/tests/data/numeric_literals_py2.py b/tests/data/numeric_literals_py2.py
index 8f85c43f265..8b2c7faa306 100644
--- a/tests/data/numeric_literals_py2.py
+++ b/tests/data/numeric_literals_py2.py
@@ -3,7 +3,7 @@
 x = 123456789L
 x = 123456789l
 x = 123456789
-x = 0xb1acc
+x = 0xB1aCc
 
 # output
 
@@ -13,4 +13,4 @@
 x = 123456789L
 x = 123456789L
 x = 123456789
-x = 0xB1ACC
+x = 0xb1acc
diff --git a/tests/data/numeric_literals_skip_underscores.py b/tests/data/numeric_literals_skip_underscores.py
index e345bb90276..f83e23312f2 100644
--- a/tests/data/numeric_literals_skip_underscores.py
+++ b/tests/data/numeric_literals_skip_underscores.py
@@ -3,7 +3,7 @@
 x = 123456789
 x = 1_2_3_4_5_6_7
 x = 1E+1
-x = 0xb1acc
+x = 0xb1AcC
 x = 0.00_00_006
 x = 12_34_567J
 x = .1_2
@@ -16,8 +16,8 @@
 x = 123456789
 x = 1_2_3_4_5_6_7
 x = 1e1
-x = 0xB1ACC
+x = 0xb1acc
 x = 0.00_00_006
 x = 12_34_567j
 x = 0.1_2
-x = 1_2.0
\ No newline at end of file
+x = 1_2.0

First PR onto black. Please tell me if i did something wrong according to your ordinary rules.

The issue, #1692, argues whether we/you should "simply" do this, as it might impact source code of a large set of users, however you can either merge it if you like it or wait if not. :) Please write me if I can do anything in that regard.

I created two seperate commits:
- Made hex lower case
- Refactored numeric formatting section

This was done to seperate the two focus points, the first one solves the issue, whereas the second adds a bit of refactoring to that part of the code. Whether my version has a higher code quality or not, is definitely subjective, so please ignore it and cherry-pick if you dislike it :) 

I am looking forward to contributing to black, long time user, and all that, so I hope you like it. I'll try to do something once in a while - Am doing this as part of hacktoberfest though, so if you can either label it `hacktoberfest-accepted` or merge it before the end of the month, that will be great ;) I feel super cheap writing that, but i am trying to pimp my bike with a yearly sticker from hacktoberfest, and this month is running a bit short. If any changes are needed, I'll gladly make those :) 

Thank you!
