From 9fa6cfa8d4654e45862ed284dc3c9931205e507a Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 26 May 2018 06:47:27 -0700 Subject: [PATCH] Bug 1464869 - Run autopep8 on security/ r=fkiefer MozReview-Commit-ID: K3aWVqsO0O8 --HG-- extra : rebase_source : 6bcf97b8b4a6e70113f36d8097f26816ce4b0acf --- security/apps/gen_cert_header.py | 38 +++++++------- security/generate_certdata.py | 5 +- security/manager/ssl/tests/unit/pycert.py | 9 +++- security/manager/ssl/tests/unit/pycms.py | 9 ++-- security/manager/ssl/tests/unit/pyct.py | 2 + security/manager/ssl/tests/unit/pykey.py | 51 +++++++++++-------- security/manager/ssl/tests/unit/sign_app.py | 21 ++++++-- .../tests/unit/test_cert_version/generate.py | 1 + .../crtshToIdentifyingStruct.py | 4 ++ security/manager/tools/getCTKnownLogs.py | 18 ++++--- security/pkix/tools/DottedOIDToCode.py | 12 +++-- 11 files changed, 110 insertions(+), 60 deletions(-) diff --git a/security/apps/gen_cert_header.py b/security/apps/gen_cert_header.py index 7cf4d4578c9e..167af5f0e9c9 100644 --- a/security/apps/gen_cert_header.py +++ b/security/apps/gen_cert_header.py @@ -4,22 +4,25 @@ import binascii + def _file_byte_generator(filename): - with open(filename, "rb") as f: - contents = f.read() + with open(filename, "rb") as f: + contents = f.read() - # Treat empty files the same as a file containing a lone 0; - # a single-element array will fail cert verifcation just as an - # empty array would. - if not contents: - return ['\0'] + # Treat empty files the same as a file containing a lone 0; + # a single-element array will fail cert verifcation just as an + # empty array would. + if not contents: + return ['\0'] + + return contents - return contents def _create_header(array_name, cert_bytes): - hexified = ["0x" + binascii.hexlify(byte) for byte in cert_bytes] - substs = { 'array_name': array_name, 'bytes': ', '.join(hexified) } - return "const uint8_t %(array_name)s[] = {\n%(bytes)s\n};\n" % substs + hexified = ["0x" + binascii.hexlify(byte) for byte in cert_bytes] + substs = {'array_name': array_name, 'bytes': ', '.join(hexified)} + return "const uint8_t %(array_name)s[] = {\n%(bytes)s\n};\n" % substs + # Create functions named the same as the data arrays that we're going to # write to the headers, so we don't have to duplicate the names like so: @@ -27,12 +30,13 @@ def _create_header(array_name, cert_bytes): # def arrayName(header, cert_filename): # header.write(_create_header("arrayName", cert_filename)) array_names = [ - 'xpcshellRoot', - 'addonsPublicRoot', - 'addonsStageRoot', - 'privilegedPackageRoot', + 'xpcshellRoot', + 'addonsPublicRoot', + 'addonsStageRoot', + 'privilegedPackageRoot', ] for n in array_names: - # Make sure the lambda captures the right string. - globals()[n] = lambda header, cert_filename, name=n: header.write(_create_header(name, _file_byte_generator(cert_filename))) + # Make sure the lambda captures the right string. + globals()[n] = lambda header, cert_filename, name=n: header.write( + _create_header(name, _file_byte_generator(cert_filename))) diff --git a/security/generate_certdata.py b/security/generate_certdata.py index 7a810f94fc3c..2a7ffeb9ee2d 100644 --- a/security/generate_certdata.py +++ b/security/generate_certdata.py @@ -7,9 +7,10 @@ import buildconfig import os import subprocess + def main(output, *inputs): - env=dict(os.environ) + env = dict(os.environ) env['PERL'] = str(buildconfig.substs['PERL']) output.write(subprocess.check_output([buildconfig.substs['PYTHON'], - inputs[0], inputs[2]], env=env)) + inputs[0], inputs[2]], env=env)) return None diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py index 7d93c6adee8a..cea5f4540888 100755 --- a/security/manager/ssl/tests/unit/pycert.py +++ b/security/manager/ssl/tests/unit/pycert.py @@ -105,6 +105,7 @@ class Error(Exception): class UnknownBaseError(Error): """Base class for handling unexpected input in this module.""" + def __init__(self, value): super(UnknownBaseError, self).__init__() self.value = value @@ -222,6 +223,7 @@ def getASN1Tag(asn1Type): type from the pyasn1 package""" return asn1Type.tagSet.baseTag.tagId + def stringToAccessDescription(string): """Helper function that takes a string representing a URI presumably identifying an OCSP authority information access @@ -234,6 +236,7 @@ def stringToAccessDescription(string): sequence.setComponentByPosition(1, accessLocation) return sequence + def stringToDN(string, tag=None): """Takes a string representing a distinguished name or directory name and returns a Name for use by pyasn1. See the documentation @@ -291,6 +294,7 @@ def stringToDN(string, tag=None): name.setComponentByPosition(0, rdns) return name + def stringToAlgorithmIdentifiers(string): """Helper function that converts a description of an algorithm to a representation usable by the pyasn1 package and a hash @@ -330,6 +334,7 @@ def stringToAlgorithmIdentifiers(string): algorithmIdentifier['parameters'] = univ.Any(nullEncapsulated) return (algorithmIdentifier, algorithmType) + def datetimeToTime(dt): """Takes a datetime object and returns an rfc2459.Time object with that time as its value as a GeneralizedTime""" @@ -337,6 +342,7 @@ def datetimeToTime(dt): time['generalTime'] = useful.GeneralizedTime(dt.strftime('%Y%m%d%H%M%SZ')) return time + def serialBytesToString(serialBytes): """Takes a list of integers in the interval [0, 255] and returns the corresponding serial number string.""" @@ -347,12 +353,13 @@ def serialBytesToString(serialBytes): stringBytes = [getASN1Tag(univ.Integer), serialBytesLen] + serialBytes return ''.join(chr(b) for b in stringBytes) + class Certificate(object): """Utility class for reading a certificate specification and generating a signed x509 certificate""" def __init__(self, paramStream): - self.versionValue = 2 # a value of 2 is X509v3 + self.versionValue = 2 # a value of 2 is X509v3 self.signature = 'sha256WithRSAEncryption' self.issuer = 'Default Issuer' actualNow = datetime.datetime.utcnow() diff --git a/security/manager/ssl/tests/unit/pycms.py b/security/manager/ssl/tests/unit/pycms.py index 3de36a52ded8..787a50059888 100755 --- a/security/manager/ssl/tests/unit/pycms.py +++ b/security/manager/ssl/tests/unit/pycms.py @@ -36,6 +36,7 @@ import pycert import pykey import sys + class Error(Exception): """Base class for exceptions in this module.""" pass @@ -133,7 +134,7 @@ class CMS(object): rsa['algorithm'] = rfc2459.rsaEncryption rsa['parameters'] = univ.Null() authenticatedAttributes = self.buildAuthenticatedAttributes(digestValue, - implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)) + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)) authenticatedAttributesTBS = self.buildAuthenticatedAttributes(digestValue) signerInfo['authenticatedAttributes'] = authenticatedAttributes signerInfo['digestEncryptionAlgorithm'] = rsa @@ -165,7 +166,7 @@ class CMS(object): implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)) extendedCertificateOrCertificate = rfc2315.ExtendedCertificateOrCertificate() certificate = decoder.decode(self.signer.toDER(), - asn1Spec=rfc2459.Certificate())[0] + asn1Spec=rfc2459.Certificate())[0] extendedCertificateOrCertificate['certificate'] = certificate certificates[0] = extendedCertificateOrCertificate signedData['certificates'] = certificates @@ -174,10 +175,10 @@ class CMS(object): if len(self.sha1) > 0: signerInfos[len(signerInfos)] = self.buildSignerInfo(certificate, - pykey.HASH_SHA1, self.sha1) + pykey.HASH_SHA1, self.sha1) if len(self.sha256) > 0: signerInfos[len(signerInfos)] = self.buildSignerInfo(certificate, - pykey.HASH_SHA256, self.sha256) + pykey.HASH_SHA256, self.sha256) signedData['signerInfos'] = signerInfos encoded = encoder.encode(signedData) diff --git a/security/manager/ssl/tests/unit/pyct.py b/security/manager/ssl/tests/unit/pyct.py index e895c4a08c9e..b0bbff07fb75 100644 --- a/security/manager/ssl/tests/unit/pyct.py +++ b/security/manager/ssl/tests/unit/pyct.py @@ -18,8 +18,10 @@ import hashlib import pykey + class InvalidKeyError(Exception): """Helper exception to handle unknown key types.""" + def __init__(self, key): self.key = key diff --git a/security/manager/ssl/tests/unit/pykey.py b/security/manager/ssl/tests/unit/pykey.py index 8d4582175997..f1f38b255beb 100755 --- a/security/manager/ssl/tests/unit/pykey.py +++ b/security/manager/ssl/tests/unit/pykey.py @@ -48,6 +48,7 @@ HASH_SHA256 = 'hash:sha256' HASH_SHA384 = 'hash:sha384' HASH_SHA512 = 'hash:sha512' + def byteStringToHexifiedBitString(string): """Takes a string of bytes and returns a hex string representing those bytes for use with pyasn1.type.univ.BitString. It must be of @@ -55,8 +56,10 @@ def byteStringToHexifiedBitString(string): pyasn1 that the input is a hex string.""" return "'%s'H" % binascii.hexlify(string) + class UnknownBaseError(Exception): """Base class for handling unexpected input in this module.""" + def __init__(self, value): super(UnknownBaseError, self).__init__() self.value = value @@ -84,6 +87,7 @@ class UnknownHashAlgorithmError(UnknownBaseError): class UnsupportedHashAlgorithmError(Exception): """Helper exception type for unsupported hash algorithms.""" + def __init__(self, value): super(UnsupportedHashAlgorithmError, self).__init__() self.value = value @@ -624,46 +628,49 @@ secp256k1Params = (long('fffffffffffffffffffffffffffffffffffffffffffffffffffffff long('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 16), long('483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', 16)) + def longToEvenLengthHexString(val): h = format(val, 'x') if not len(h) % 2 == 0: h = '0' + h return h + def notRandom(n): return n * '\x04' + class ECCKey(object): secp256k1Encoded = str('08fd87b04fba98090100004035ee7c7289d8fef7a8' - '6afe5da66d8bc2ebb6a8543fd2fead089f45ce7acd0fa64382a9500c41dad' - '770ffd4b511bf4b492eb1238800c32c4f76c73a3f3294e7c5002067cebc20' - '8a5fa3df16ec2bb34acc59a42ab4abb0538575ca99b92b6a2149a04f') + '6afe5da66d8bc2ebb6a8543fd2fead089f45ce7acd0fa64382a9500c41dad' + '770ffd4b511bf4b492eb1238800c32c4f76c73a3f3294e7c5002067cebc20' + '8a5fa3df16ec2bb34acc59a42ab4abb0538575ca99b92b6a2149a04f') secp224r1Encoded = str('0ee5587c4d18526f00e00038668d72cca6fd6a1b35' - '57b5366104d84408ecb637f08e8c86bbff82cc00e88f0066d7af63c3298ba' - '377348a1202b03b37fd6b1ff415aa311e001c04389459926c3296c242b83e' - '10a6cd2011c8fe2dae1b772ea5b21067') + '57b5366104d84408ecb637f08e8c86bbff82cc00e88f0066d7af63c3298ba' + '377348a1202b03b37fd6b1ff415aa311e001c04389459926c3296c242b83e' + '10a6cd2011c8fe2dae1b772ea5b21067') secp256r1Encoded = str('cb872ac99cd31827010000404fbfbbbb61e0f8f9b1' - 'a60a59ac8704e2ec050b423e3cf72e923f2c4f794b455c2a69d233456c36c' - '4119d0706e00eedc8d19390d7991b7b2d07a304eaa04aa6c000202191403d' - '5710bf15a265818cd42ed6fedf09add92d78b18e7a1e9feb95524702') + 'a60a59ac8704e2ec050b423e3cf72e923f2c4f794b455c2a69d233456c36c' + '4119d0706e00eedc8d19390d7991b7b2d07a304eaa04aa6c000202191403d' + '5710bf15a265818cd42ed6fedf09add92d78b18e7a1e9feb95524702') secp384r1Encoded = str('d3103f5ac81741e801800060a1687243362b5c7b18' - '89f379154615a1c73fb48dee863e022915db608e252de4b7132da8ce98e83' - '1534e6a9c0c0b09c8d639ade83206e5ba813473a11fa330e05da8c96e4383' - 'fe27873da97103be2888cff002f05af71a1fddcc8374aa6ea9ce0030035c7' - 'a1b10d9fafe837b64ad92f22f5ced0789186538669b5c6d872cec3d926122' - 'b393772b57602ff31365efe1393246') + '89f379154615a1c73fb48dee863e022915db608e252de4b7132da8ce98e83' + '1534e6a9c0c0b09c8d639ade83206e5ba813473a11fa330e05da8c96e4383' + 'fe27873da97103be2888cff002f05af71a1fddcc8374aa6ea9ce0030035c7' + 'a1b10d9fafe837b64ad92f22f5ced0789186538669b5c6d872cec3d926122' + 'b393772b57602ff31365efe1393246') secp521r1Encoded = str('77f4b0ac81948ddc02090084014cdc9cacc4794109' - '6bc9cc66752ec27f597734fa66c62b792f88c519d6d37f0d16ea1c483a182' - '7a010b9128e3a08070ca33ef5f57835b7c1ba251f6cc3521dc42b01065345' - '1981b445d343eed3782a35d6cff0ff484f5a883d209f1b9042b726703568b' - '2f326e18b833bdd8aa0734392bcd19501e10d698a79f53e11e0a22bdd2aad' - '900042014f3284fa698dd9fe1118dd331851cdfaac5a3829278eb8994839d' - 'e9471c940b858c69d2d05e8c01788a7d0b6e235aa5e783fc1bee807dcc386' - '5f920e12cf8f2d29') + '6bc9cc66752ec27f597734fa66c62b792f88c519d6d37f0d16ea1c483a182' + '7a010b9128e3a08070ca33ef5f57835b7c1ba251f6cc3521dc42b01065345' + '1981b445d343eed3782a35d6cff0ff484f5a883d209f1b9042b726703568b' + '2f326e18b833bdd8aa0734392bcd19501e10d698a79f53e11e0a22bdd2aad' + '900042014f3284fa698dd9fe1118dd331851cdfaac5a3829278eb8994839d' + 'e9471c940b858c69d2d05e8c01788a7d0b6e235aa5e783fc1bee807dcc386' + '5f920e12cf8f2d29') def __init__(self, specification=None): if specification == 'secp256k1': @@ -748,6 +755,8 @@ def keyFromSpecification(specification): # The build harness will call this function with an output file-like # object and a path to a file containing a specification. This will # read the specification and output the key as ASCII-encoded PKCS #8. + + def main(output, inputPath): with open(inputPath) as configStream: output.write(keyFromSpecification(configStream.read().strip()).toPEM()) diff --git a/security/manager/ssl/tests/unit/sign_app.py b/security/manager/ssl/tests/unit/sign_app.py index 8af56c11dd17..d1e4dbd993d6 100755 --- a/security/manager/ssl/tests/unit/sign_app.py +++ b/security/manager/ssl/tests/unit/sign_app.py @@ -30,6 +30,7 @@ KID = 4 ALG = 1 COSE_Sign = 98 + def coseAlgorithmToPykeyHash(algorithm): """Helper function that takes one of (ES256, ES384, ES512) and returns the corresponding pykey.HASH_* identifier.""" @@ -47,6 +48,8 @@ def coseAlgorithmToPykeyHash(algorithm): # unprotected : {}, # signature : bstr # ] + + def coseSignature(payload, algorithm, signingKey, signingCertificate, bodyProtected): """Returns a COSE_Signature structure. @@ -78,6 +81,8 @@ def coseSignature(payload, algorithm, signingKey, signingCertificate, # payload : nil, # signatures : [+ COSE_Signature] # ] + + def coseSig(payload, intermediates, signatures): """Returns the entire (tagged) COSE_Sign structure. payload is a string representing the data to be signed @@ -96,6 +101,7 @@ def coseSig(payload, intermediates, signatures): tagged = CBORTag(COSE_Sign, [protectedEncoded, {}, None, coseSignatures]) return dumps(tagged) + def walkDirectory(directory): """Given a relative path to a directory, enumerates the files in the tree rooted at that location. Returns a list @@ -110,6 +116,7 @@ def walkDirectory(directory): paths.append((fullPath, internalPath)) return paths + def addManifestEntry(filename, hashes, contents, entries): """Helper function to fill out a manifest entry. Takes the filename, a list of (hash function, hash function name) @@ -121,6 +128,7 @@ def addManifestEntry(filename, hashes, contents, entries): entry += '%s-Digest: %s\n' % (name, base64hash) entries.append(entry) + def getCert(subject, keyName, issuerName, ee, issuerKey=""): """Helper function to create an X509 cert from a specification. Takes the subject, the subject key name to use, the issuer name, @@ -141,6 +149,7 @@ def getCert(subject, keyName, issuerName, ee, issuerKey=""): certSpecificationStream.seek(0) return pycert.Certificate(certSpecificationStream) + def coseAlgorithmToSignatureParams(coseAlgorithm, issuerName): """Given a COSE algorithm ('ES256', 'ES384', 'ES512') and an issuer name, returns a (algorithm id, pykey.ECCKey, encoded certificate) @@ -153,15 +162,17 @@ def coseAlgorithmToSignatureParams(coseAlgorithm, issuerName): keyName = 'secp384r1' algId = ES384 elif coseAlgorithm == 'ES512': - keyName = 'secp521r1' # COSE uses the hash algorithm; this is the curve + keyName = 'secp521r1' # COSE uses the hash algorithm; this is the curve algId = ES512 else: raise UnknownCOSEAlgorithmError(coseAlgorithm) key = pykey.ECCKey(keyName) # The subject must differ to avoid errors when importing into NSS later. - ee = getCert('xpcshell signed app test signer ' + keyName, keyName, issuerName, True, 'default') + ee = getCert('xpcshell signed app test signer ' + keyName, + keyName, issuerName, True, 'default') return (algId, key, ee.toDER()) + def signZip(appDirectory, outputFile, issuerName, rootName, manifestHashes, signatureHashes, pkcs7Hashes, coseAlgorithms, emptySignerInfos): """Given a directory containing the files to package up, @@ -198,8 +209,8 @@ def signZip(appDirectory, outputFile, issuerName, rootName, manifestHashes, intermediate = intermediate.toDER() intermediates.append(intermediate) signatures = map(lambda coseAlgorithm: - coseAlgorithmToSignatureParams(coseAlgorithm, coseIssuerName), - coseAlgorithms) + coseAlgorithmToSignatureParams(coseAlgorithm, coseIssuerName), + coseAlgorithms) coseSignatureBytes = coseSig(coseManifest, intermediates, signatures) outZip.writestr('META-INF/cose.sig', coseSignatureBytes) addManifestEntry('META-INF/cose.sig', manifestHashes, @@ -230,6 +241,7 @@ def signZip(appDirectory, outputFile, issuerName, rootName, manifestHashes, outZip.writestr('META-INF/A.SF', sfContents) outZip.writestr('META-INF/MANIFEST.MF', mfContents) + class Error(Exception): """Base class for exceptions in this module.""" pass @@ -264,6 +276,7 @@ def hashNameToFunctionAndIdentifier(name): return (sha256, 'SHA256') raise UnknownHashAlgorithmError(name) + def main(outputFile, appPath, *args): """Main entrypoint. Given an already-opened file-like object, a path to the app directory to sign, and some diff --git a/security/manager/ssl/tests/unit/test_cert_version/generate.py b/security/manager/ssl/tests/unit/test_cert_version/generate.py index 88386efaa74f..5f0437e95edf 100755 --- a/security/manager/ssl/tests/unit/test_cert_version/generate.py +++ b/security/manager/ssl/tests/unit/test_cert_version/generate.py @@ -43,6 +43,7 @@ basicConstraintsTypes = { 'BC-cA': 'extension:basicConstraints:cA,' } + def writeCertspec(issuer, subject, fields): filename = '%s_%s.pem.certspec' % (subject, issuer) if issuer == subject: diff --git a/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py index 2a9facf9f56a..ca672108703e 100644 --- a/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py +++ b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py @@ -30,12 +30,15 @@ from cryptography.x509.oid import NameOID assert sys.version_info >= (3, 2), "Requires Python 3.2 or later" + def hex_string_for_struct(bytes): return ["0x{:02X}".format(x) for x in bytes] + def hex_string_human_readable(bytes): return ["{:02X}".format(x) for x in bytes] + def nameOIDtoString(oid): if oid == NameOID.COUNTRY_NAME: return "C" @@ -49,6 +52,7 @@ def nameOIDtoString(oid): return "OU" raise Exception("Unknown OID: {}".format(oid)) + def print_block(pemData, identifierType="DN", crtshId=None): substrate = pem.readPemFromFile(io.StringIO(pemData.decode("utf-8"))) cert, rest = decoder.decode(substrate, asn1Spec=rfc5280.Certificate()) diff --git a/security/manager/tools/getCTKnownLogs.py b/security/manager/tools/getCTKnownLogs.py index e4671e315805..97de974b9e50 100755 --- a/security/manager/tools/getCTKnownLogs.py +++ b/security/manager/tools/getCTKnownLogs.py @@ -188,6 +188,7 @@ def generate_cpp_header_file(json_data, out_file): logs="\n".join(log_info_initializers), operators="\n".join(operator_info_initializers))) + def patch_in_test_logs(json_data): """ Insert Mozilla-specific test log data. """ max_id = 0 @@ -195,15 +196,15 @@ def patch_in_test_logs(json_data): if operator["id"] > max_id: max_id = operator["id"] mozilla_test_operator_1 = {"name": "Mozilla Test Org 1", "id": max_id + 1, - "test_only": True} + "test_only": True} mozilla_test_operator_2 = {"name": "Mozilla Test Org 2", "id": max_id + 2, - "test_only": True} + "test_only": True} json_data["operators"].append(mozilla_test_operator_1) json_data["operators"].append(mozilla_test_operator_2) # The easiest way to get this is # `openssl x509 -noout -pubkey -in ` mozilla_rsa_log_1 = {"description": "Mozilla Test RSA Log 1", - "key": """ + "key": """ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2 ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n @@ -212,11 +213,11 @@ def patch_in_test_logs(json_data): tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt jQIDAQAB """, - "operated_by": [max_id + 1]} + "operated_by": [max_id + 1]} # Similarly, # `openssl x509 -noout -pubkey -in ` mozilla_rsa_log_2 = {"description": "Mozilla Test RSA Log 2", - "key": """ + "key": """ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXXGUmYJn3cIKmeR8bh2 w39c5TiwbErNIrHL1G+mWtoq3UHIwkmKxKOzwfYUh/QbaYlBvYClHDwSAkTFhKTE SDMF5ROMAQbPCL6ahidguuai6PNvI8XZgxO53683g0XazlHU1tzSpss8xwbrzTBw @@ -225,18 +226,19 @@ def patch_in_test_logs(json_data): gys1uJMPdLqQqovHYWckKrH9bWIUDRjEwLjGj8N0hFcyStfehuZVLx0eGR1xIWjT uwIDAQAB """, - "operated_by": [max_id + 2]} + "operated_by": [max_id + 2]} # `openssl x509 -noout -pubkey -in