Skip to content

Commit

Permalink
Merge branch 'Legrandin:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
lonepig authored Jun 5, 2023
2 parents b9272c0 + ca85cd2 commit 36eb9c6
Show file tree
Hide file tree
Showing 22 changed files with 438 additions and 80 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:

- name: Install MSVC
if: matrix.os == 'windows-latest' && matrix.python-version == '2.7'
uses: ilammy/msvc-dev-cmd@f456b805b3f63911738cb71d4f255e4e129c7e7a
uses: ilammy/msvc-dev-cmd@v1

- name: Prepare environmental variables
if: matrix.os == 'windows-latest' && matrix.python-version == '2.7'
Expand Down Expand Up @@ -101,7 +101,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libc6-dev-i386
sudo apt-get install libc6-dev-i386 libubsan1
- name: Test Linux x32 and x64
run: |
cd src/test
Expand All @@ -125,7 +125,7 @@ jobs:
with:
python-version: "3.11"
- name: Install MSVC
uses: ilammy/msvc-dev-cmd@f456b805b3f63911738cb71d4f255e4e129c7e7a
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch }}
- name: Test Windows 32 and 64
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, windows-latest, macos-10.15]
os: [ubuntu-20.04, windows-latest, macos-12]

if: github.actor == 'Legrandin'

Expand Down Expand Up @@ -79,7 +79,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, windows-latest, macos-10.15]
os: [ubuntu-20.04, windows-latest, macos-12]
python-version: ['3.5']

if: github.actor == 'Legrandin'
Expand Down Expand Up @@ -117,7 +117,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-20.04, windows-latest, macos-10.15 ]
os: [ ubuntu-20.04, windows-latest, macos-12 ]
arch: [ multi-arch ]
# Python 2 on Windows requires manual toolchain setup (per-arch) due to newer MSVC used here
exclude:
Expand Down
10 changes: 9 additions & 1 deletion Changelog.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
Changelog
=========

3.18.0 (under development)
3.18.0 (18 May 2023)
++++++++++++++++++++++++++

New features
---------------
* Added support for DER BOOLEAN encodings.
* The library now compiles on Windows ARM64. Thanks to Niyas Sait.

Resolved issues
---------------
* GH#722: ``nonce`` attribute was not correctly set for XChaCha20_Poly1305 ciphers. Thanks to Liam Haber.
* GH#728: Workaround for a possible x86 emulator bug in Windows for ARM64.
* GH#739: OID encoding for arc 2 didn't accept children larger than 39. Thanks to James.
* Correctly check that the scalar matches the point when importing an ECC private key.

3.17.0 (29 January 2023)
++++++++++++++++++++++++++
Expand Down
4 changes: 2 additions & 2 deletions lib/Crypto/Cipher/AES.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
MODE_OPENPGP = 7 #: OpenPGP mode (:ref:`openpgp_mode`)
MODE_CCM = 8 #: Counter with CBC-MAC (:ref:`ccm_mode`)
MODE_EAX = 9 #: :ref:`eax_mode`
MODE_SIV = 10 #: Galois Counter Mode (:ref:`gcm_mode`)
MODE_GCM = 11 #: Synthetic Initialization Vector (:ref:`siv_mode`)
MODE_SIV = 10 #: Synthetic Initialization Vector (:ref:`siv_mode`)
MODE_GCM = 11 #: Galois Counter Mode (:ref:`gcm_mode`)
MODE_OCB = 12 #: Offset Code Book (:ref:`ocb_mode`)


Expand Down
10 changes: 5 additions & 5 deletions lib/Crypto/Cipher/ChaCha20_Poly1305.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ def __init__(self, key, nonce):
See also `new()` at the module level."""

self.nonce = _copy_bytes(None, None, nonce)

self._next = ("update", "encrypt", "decrypt", "digest",
"verify")

Expand Down Expand Up @@ -316,10 +314,10 @@ def new(**kwargs):
nonce = get_random_bytes(12)

if len(nonce) in (8, 12):
pass
chacha20_poly1305_nonce = nonce
elif len(nonce) == 24:
key = _HChaCha20(key, nonce[:16])
nonce = b'\x00\x00\x00\x00' + nonce[16:]
chacha20_poly1305_nonce = b'\x00\x00\x00\x00' + nonce[16:]
else:
raise ValueError("Nonce must be 8, 12 or 24 bytes long")

Expand All @@ -329,7 +327,9 @@ def new(**kwargs):
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))

return ChaCha20Poly1305Cipher(key, nonce)
cipher = ChaCha20Poly1305Cipher(key, chacha20_poly1305_nonce)
cipher.nonce = _copy_bytes(None, None, nonce)
return cipher


# Size of a key (in bytes)
Expand Down
4 changes: 2 additions & 2 deletions lib/Crypto/PublicKey/ECC.py
Original file line number Diff line number Diff line change
Expand Up @@ -1447,8 +1447,8 @@ def _import_rfc5915_der(encoded, passphrase, curve_oid=None):
d = Integer.from_bytes(scalar_bytes)

# Decode public key (if any)
if len(private_key) == 4:
public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value
if len(private_key) > 2:
public_key_enc = DerBitString(explicit=1).decode(private_key[-1]).value
public_key = _import_public_der(public_key_enc, curve_oid=curve_oid)
point_x = public_key.pointQ.x
point_y = public_key.pointQ.y
Expand Down
10 changes: 8 additions & 2 deletions lib/Crypto/SelfTest/Cipher/test_ChaCha20_Poly1305.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Hash import SHAKE128

from Crypto.Util._file_system import pycryptodome_filename
from Crypto.Util.strxor import strxor


Expand Down Expand Up @@ -326,6 +325,13 @@ def test_memoryview(self):

class XChaCha20Poly1305Tests(unittest.TestCase):

def test_nonce(self):
# Nonce can only be 24 bytes
cipher = ChaCha20_Poly1305.new(key=b'Y' * 32,
nonce=b'H' * 24)
self.assertEqual(len(cipher.nonce), 24)
self.assertEqual(cipher.nonce, b'H' * 24)

def test_encrypt(self):
# From https://tools.ietf.org/html/draft-arciszewski-xchacha-03
# Section A.3.1
Expand Down Expand Up @@ -599,7 +605,7 @@ class TestVectorsRFC(unittest.TestCase):
)
]

test_vectors = [[unhexlify(x.replace(" ","").replace(":","")) for x in tv] for tv in test_vectors_hex]
test_vectors = [[unhexlify(x.replace(" ", "").replace(":", "")) for x in tv] for tv in test_vectors_hex]

def runTest(self):
for assoc_data, pt, ct, mac, key, nonce in self.test_vectors:
Expand Down
4 changes: 2 additions & 2 deletions lib/Crypto/SelfTest/PublicKey/test_ECC_25519.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ def test_equal(self):

def test_pai(self):
pai = EccPoint(0, 1, curve="Ed25519")
self.failUnless(pai.is_point_at_infinity())
self.assertTrue(pai.is_point_at_infinity())
self.assertEqual(pai, pai.point_at_infinity())

def test_negate(self):
negG = -self.pointG
sum = self.pointG + negG
self.failUnless(sum.is_point_at_infinity())
self.assertTrue(sum.is_point_at_infinity())

def test_addition(self):
self.assertEqual(self.pointG + self.pointG2, self.pointG3)
Expand Down
4 changes: 2 additions & 2 deletions lib/Crypto/SelfTest/PublicKey/test_ECC_448.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ def test_equal(self):

def test_pai(self):
pai = EccPoint(0, 1, curve="Ed448")
self.failUnless(pai.is_point_at_infinity())
self.assertTrue(pai.is_point_at_infinity())
self.assertEqual(pai, pai.point_at_infinity())

def test_negate(self):
negG = -self.pointG
sum = self.pointG + negG
self.failUnless(sum.is_point_at_infinity())
self.assertTrue(sum.is_point_at_infinity())

def test_addition(self):
self.assertEqual(self.pointG + self.pointG2, self.pointG3)
Expand Down
10 changes: 10 additions & 0 deletions lib/Crypto/SelfTest/PublicKey/test_import_ECC.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,16 @@ class TestImport(unittest.TestCase):
def test_empty(self):
self.assertRaises(ValueError, ECC.import_key, b"")

def test_mismatch(self):
# The private key does not match the public key
mismatch = """-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJChZANiAAQarFRaqflo
I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXk=
-----END PRIVATE KEY-----"""
self.assertRaises(ValueError, ECC.import_key, mismatch)


class TestImport_P192(unittest.TestCase):

Expand Down
29 changes: 22 additions & 7 deletions lib/Crypto/SelfTest/Util/test_asn1.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,35 +614,50 @@ class DerObjectIdTests(unittest.TestCase):

def testInit1(self):
der = DerObjectId("1.1")
self.assertEqual(der.encode(), b('\x06\x01)'))
self.assertEqual(der.encode(), b'\x06\x01)')

def testEncode1(self):
der = DerObjectId('1.2.840.113549.1.1.1')
self.assertEqual(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'))
#
self.assertEqual(der.encode(), b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')

der = DerObjectId()
der.value = '1.2.840.113549.1.1.1'
self.assertEqual(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'))
self.assertEqual(der.encode(), b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')

der = DerObjectId('2.999.1234')
self.assertEqual(der.encode(), b'\x06\x04\x88\x37\x89\x52')

def testEncode2(self):
der = DerObjectId('3.4')
self.assertRaises(ValueError, der.encode)

der = DerObjectId('1.40')
self.assertRaises(ValueError, der.encode)

####

def testDecode1(self):
# Empty sequence
der = DerObjectId()
der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'))
der.decode(b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')
self.assertEqual(der.value, '1.2.840.113549.1.1.1')

def testDecode2(self):
# Verify that decode returns the object
der = DerObjectId()
self.assertEqual(der,
der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')))
der.decode(b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'))

def testDecode3(self):
der = DerObjectId()
der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x00\x01'))
der.decode(b'\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x00\x01')
self.assertEqual(der.value, '1.2.840.113549.1.0.1')

def testDecode4(self):
der = DerObjectId()
der.decode(b'\x06\x04\x88\x37\x89\x52')
self.assertEqual(der.value, '2.999.1234')


class DerBitStringTests(unittest.TestCase):

Expand Down
66 changes: 46 additions & 20 deletions lib/Crypto/Util/asn1.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
# - https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/
# - https://www.zytrax.com/tech/survival/asn1.html
# - https://www.oss.com/asn1/resources/books-whitepapers-pubs/larmouth-asn1-book.pdf

# - https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
# - https://misc.daniel-marschall.de/asn.1/oid-converter/online.php

def _is_number(x, only_non_negative=False):
test = 0
Expand Down Expand Up @@ -480,20 +481,27 @@ class DerSequence(DerObject):
"""

def __init__(self, startSeq=None, implicit=None):
def __init__(self, startSeq=None, implicit=None, explicit=None):
"""Initialize the DER object as a SEQUENCE.
:Parameters:
startSeq : Python sequence
A sequence whose element are either integers or
other DER objects.
implicit : integer
The IMPLICIT tag to use for the encoded object.
implicit : integer or byte
The IMPLICIT tag number (< 0x1F) to use for the encoded object.
It overrides the universal tag for SEQUENCE (16).
It cannot be combined with the ``explicit`` parameter.
By default, there is no IMPLICIT tag.
explicit : integer or byte
The EXPLICIT tag number (< 0x1F) to use for the encoded object.
It cannot be combined with the ``implicit`` parameter.
By default, there is no EXPLICIT tag.
"""

DerObject.__init__(self, 0x10, b'', implicit, True)
DerObject.__init__(self, 0x10, b'', implicit, True, explicit)
if startSeq is None:
self._seq = []
else:
Expand Down Expand Up @@ -747,19 +755,25 @@ def encode(self):
binary string."""

comps = [int(x) for x in self.value.split(".")]

if len(comps) < 2:
raise ValueError("Not a valid Object Identifier string")
self.payload = bchr(40*comps[0]+comps[1])
for v in comps[2:]:
if v == 0:
enc = [0]
else:
enc = []
while v:
enc.insert(0, (v & 0x7F) | 0x80)
v >>= 7
enc[-1] &= 0x7F
self.payload += b''.join([bchr(x) for x in enc])
if comps[0] > 2:
raise ValueError("First component must be 0, 1 or 2")
if comps[0] < 2 and comps[1] > 39:
raise ValueError("Second component must be 39 at most")

subcomps = [40 * comps[0] + comps[1]] + comps[2:]

encoding = []
for v in reversed(subcomps):
encoding.append(v & 0x7F)
v >>= 7
while v:
encoding.append((v & 0x7F) | 0x80)
v >>= 7

self.payload = b''.join([bchr(x) for x in reversed(encoding)])
return DerObject.encode(self)

def decode(self, der_encoded, strict=False):
Expand All @@ -786,15 +800,27 @@ def _decodeFromStream(self, s, strict):

# Derive self.value from self.payload
p = BytesIO_EOF(self.payload)
comps = [str(x) for x in divmod(p.read_byte(), 40)]

subcomps = []
v = 0
while p.remaining_data():
c = p.read_byte()
v = v*128 + (c & 0x7F)
v = (v << 7) + (c & 0x7F)
if not (c & 0x80):
comps.append(str(v))
subcomps.append(v)
v = 0
self.value = '.'.join(comps)

if len(subcomps) == 0:
raise ValueError("Empty payload")

if subcomps[0] < 40:
subcomps[:1] = [0, subcomps[0]]
elif subcomps[0] < 80:
subcomps[:1] = [1, subcomps[0] - 40]
else:
subcomps[:1] = [2, subcomps[0] - 80]

self.value = ".".join([str(x) for x in subcomps])


class DerBitString(DerObject):
Expand Down
2 changes: 1 addition & 1 deletion lib/Crypto/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature',
'IO', 'Math']

version_info = (3, 18, '0b0')
version_info = (3, 18, '0')

__version__ = ".".join([str(x) for x in version_info])
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ max-line-length = 110
[metadata]
project_urls =
Source=https://github.com/Legrandin/pycryptodome/
Changelog=https://www.pycryptodome.org/src/changelog

[bdist_wheel]
py-limited-api = cp35
Expand Down
Loading

0 comments on commit 36eb9c6

Please sign in to comment.