Merge lp:~apw/launchpad/signing-key-generate-subject-limit into lp:launchpad

Proposed by Andy Whitcroft
Status: Merged
Merged at revision: 18427
Proposed branch: lp:~apw/launchpad/signing-key-generate-subject-limit
Merge into: lp:launchpad
Diff against target: 115 lines (+58/-4)
2 files modified
lib/lp/archivepublisher/signing.py (+18/-4)
lib/lp/archivepublisher/tests/test_signing.py (+40/-0)
To merge this branch: bzr merge lp:~apw/launchpad/signing-key-generate-subject-limit
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+301642@code.launchpad.net

Commit message

signing: truncate signing key common-names to 64 characters

Description of the change

When owner+ppa name is very long the subjects generated for the EFI and KMOD keys are too long to represent in the keys; 64 characters maximum. As these are purely visual truncate the generated cname strings to 64 characters.

Also drops extraneous framing from kmod keys.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

Looks good, but any chance of a test for the truncation case?

review: Approve
Revision history for this message
Andy Whitcroft (apw) wrote :

> Looks good, but any chance of a test for the truncation case?

Sounds reasonable indeed. For this purpose I have pulled out the common name generation to a shiny new helper. This I have added a range of tests for.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/archivepublisher/signing.py'
--- lib/lp/archivepublisher/signing.py 2016-06-22 08:54:11 +0000
+++ lib/lp/archivepublisher/signing.py 2017-07-24 10:01:21 +0000
@@ -193,20 +193,30 @@
193 return [None for k in keynames]193 return [None for k in keynames]
194 return keynames194 return keynames
195195
196 def generateKeyCommonName(self, owner, archive, suffix=''):
197 # PPA <owner> <archive> <suffix>
198 # truncate <owner> <archive> to ensure the overall form is shorter
199 # than 64 characters but the suffix is maintained
200 if suffix:
201 suffix = " " + suffix
202 common_name = "PPA %s %s" % (owner, archive)
203 return common_name[0:64 - len(suffix)] + suffix
204
196 def generateUefiKeys(self):205 def generateUefiKeys(self):
197 """Generate new UEFI Keys for this archive."""206 """Generate new UEFI Keys for this archive."""
198 directory = os.path.dirname(self.uefi_key)207 directory = os.path.dirname(self.uefi_key)
199 if not os.path.exists(directory):208 if not os.path.exists(directory):
200 os.makedirs(directory)209 os.makedirs(directory)
201210
202 common_name = '/CN=PPA %s %s/' % (211 common_name = self.generateKeyCommonName(
203 self.archive.owner.name, self.archive.name)212 self.archive.owner.name, self.archive.name)
213 subject = '/CN=' + common_name + '/'
204214
205 old_mask = os.umask(0o077)215 old_mask = os.umask(0o077)
206 try:216 try:
207 new_key_cmd = [217 new_key_cmd = [
208 'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048',218 'openssl', 'req', '-new', '-x509', '-newkey', 'rsa:2048',
209 '-subj', common_name, '-keyout', self.uefi_key,219 '-subj', subject, '-keyout', self.uefi_key,
210 '-out', self.uefi_cert, '-days', '3650', '-nodes', '-sha256',220 '-out', self.uefi_cert, '-days', '3650', '-nodes', '-sha256',
211 ]221 ]
212 self.callLog("UEFI keygen", new_key_cmd)222 self.callLog("UEFI keygen", new_key_cmd)
@@ -233,6 +243,10 @@
233 if not os.path.exists(directory):243 if not os.path.exists(directory):
234 os.makedirs(directory)244 os.makedirs(directory)
235245
246 # Truncate name to 64 character maximum.
247 common_name = self.generateKeyCommonName(
248 self.archive.owner.name, self.archive.name, "kmod")
249
236 old_mask = os.umask(0o077)250 old_mask = os.umask(0o077)
237 try:251 try:
238 with tempfile.NamedTemporaryFile(suffix='.keygen') as tf:252 with tempfile.NamedTemporaryFile(suffix='.keygen') as tf:
@@ -245,14 +259,14 @@
245 x509_extensions = myexts259 x509_extensions = myexts
246260
247 [ req_distinguished_name ]261 [ req_distinguished_name ]
248 CN = /CN=PPA %s %s kmod/262 CN = %s
249263
250 [ myexts ]264 [ myexts ]
251 basicConstraints=critical,CA:FALSE265 basicConstraints=critical,CA:FALSE
252 keyUsage=digitalSignature266 keyUsage=digitalSignature
253 subjectKeyIdentifier=hash267 subjectKeyIdentifier=hash
254 authorityKeyIdentifier=keyid268 authorityKeyIdentifier=keyid
255 """ % (self.archive.owner.name, self.archive.name))269 """ % common_name)
256270
257 print(genkey_text, file=tf)271 print(genkey_text, file=tf)
258272
259273
=== modified file 'lib/lp/archivepublisher/tests/test_signing.py'
--- lib/lp/archivepublisher/tests/test_signing.py 2017-04-29 15:24:32 +0000
+++ lib/lp/archivepublisher/tests/test_signing.py 2017-07-24 10:01:21 +0000
@@ -250,6 +250,46 @@
250 self.assertEqual(1, upload.callLog.caller_count('UEFI signing'))250 self.assertEqual(1, upload.callLog.caller_count('UEFI signing'))
251 self.assertEqual(1, upload.callLog.caller_count('Kmod signing'))251 self.assertEqual(1, upload.callLog.caller_count('Kmod signing'))
252252
253 def test_common_name_plain(self):
254 upload = SigningUpload()
255 common_name = upload.generateKeyCommonName('testing-team', 'ppa')
256 self.assertEqual('PPA testing-team ppa', common_name)
257
258 def test_common_name_suffix(self):
259 upload = SigningUpload()
260 common_name = upload.generateKeyCommonName(
261 'testing-team', 'ppa', 'kmod')
262 self.assertEqual('PPA testing-team ppa kmod', common_name)
263
264 def test_common_name_plain_just_short(self):
265 upload = SigningUpload()
266 common_name = upload.generateKeyCommonName('t' * 30, 'p' * 29)
267 expected_name = 'PPA ' + 't' * 30 + ' ' + 'p' * 29
268 self.assertEqual(expected_name, common_name)
269 self.assertEqual(64, len(common_name))
270
271 def test_common_name_suffix_just_short(self):
272 upload = SigningUpload()
273 common_name = upload.generateKeyCommonName('t' * 30, 'p' * 24, 'kmod')
274 expected_name = 'PPA ' + 't' * 30 + ' ' + 'p' * 24 + ' kmod'
275 self.assertEqual(expected_name, common_name)
276 self.assertEqual(64, len(common_name))
277
278 def test_common_name_plain_long(self):
279 upload = SigningUpload()
280 common_name = upload.generateKeyCommonName('t' * 40, 'p' * 40)
281 expected_name = 'PPA ' + 't' * 40 + ' ' + 'p' * 19
282 self.assertEqual(expected_name, common_name)
283 self.assertEqual(64, len(common_name))
284
285 def test_common_name_suffix_long(self):
286 upload = SigningUpload()
287 common_name = upload.generateKeyCommonName(
288 't' * 40, 'p' * 40, 'kmod-plus')
289 expected_name = 'PPA ' + 't' * 40 + ' ' + 'p' * 9 + ' kmod-plus'
290 self.assertEqual(expected_name, common_name)
291 self.assertEqual(64, len(common_name))
292
253 def test_options_handling_none(self):293 def test_options_handling_none(self):
254 # If the configured key/cert are missing, processing succeeds but294 # If the configured key/cert are missing, processing succeeds but
255 # nothing is signed.295 # nothing is signed.