1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367 |
- # Copyright 2005 Divmod, Inc. See LICENSE file for details
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
-
- """
- Tests for L{twisted.internet._sslverify}.
- """
-
-
- import datetime
- import itertools
- import sys
- from unittest import skipIf
-
- from zope.interface import implementer
-
- from incremental import Version
-
- from twisted.internet import defer, interfaces, protocol, reactor
- from twisted.internet._idna import _idnaText
- from twisted.internet.error import CertificateError, ConnectionClosed, ConnectionLost
- from twisted.python.compat import nativeString
- from twisted.python.filepath import FilePath
- from twisted.python.modules import getModule
- from twisted.python.reflect import requireModule
- from twisted.test.iosim import connectedServerAndClient
- from twisted.test.test_twisted import SetAsideModule
- from twisted.trial import util
- from twisted.trial.unittest import SkipTest, SynchronousTestCase, TestCase
-
- skipSSL = ""
- skipSNI = ""
- skipNPN = ""
- skipALPN = ""
-
- if requireModule("OpenSSL"):
- import ipaddress
-
- from OpenSSL import SSL
- from OpenSSL.crypto import FILETYPE_PEM, TYPE_RSA, X509, PKey, get_elliptic_curves
-
- from cryptography import x509
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.asymmetric import rsa
- from cryptography.hazmat.primitives.serialization import (
- Encoding,
- NoEncryption,
- PrivateFormat,
- )
- from cryptography.x509.oid import NameOID
-
- from twisted.internet import ssl
-
- try:
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.set_npn_advertise_callback(lambda c: None)
- except (NotImplementedError, AttributeError):
- skipNPN = (
- "NPN is deprecated (and OpenSSL 1.0.1 or greater required for NPN"
- " support)"
- )
-
- try:
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.set_alpn_select_callback(lambda c: None) # type: ignore[misc,arg-type]
- except NotImplementedError:
- skipALPN = "OpenSSL 1.0.2 or greater required for ALPN support"
- else:
- skipSSL = "OpenSSL is required for SSL tests."
- skipSNI = skipSSL
- skipNPN = skipSSL
- skipALPN = skipSSL
-
- if not skipSSL:
- from twisted.internet import _sslverify as sslverify
- from twisted.internet.ssl import VerificationError, platformTrust
- from twisted.protocols.tls import TLSMemoryBIOFactory
-
-
- # A couple of static PEM-format certificates to be used by various tests.
- A_HOST_CERTIFICATE_PEM = """
- -----BEGIN CERTIFICATE-----
- MIIC2jCCAkMCAjA5MA0GCSqGSIb3DQEBBAUAMIG0MQswCQYDVQQGEwJVUzEiMCAG
- A1UEAxMZZXhhbXBsZS50d2lzdGVkbWF0cml4LmNvbTEPMA0GA1UEBxMGQm9zdG9u
- MRwwGgYDVQQKExNUd2lzdGVkIE1hdHJpeCBMYWJzMRYwFAYDVQQIEw1NYXNzYWNo
- dXNldHRzMScwJQYJKoZIhvcNAQkBFhhub2JvZHlAdHdpc3RlZG1hdHJpeC5jb20x
- ETAPBgNVBAsTCFNlY3VyaXR5MB4XDTA2MDgxNjAxMDEwOFoXDTA3MDgxNjAxMDEw
- OFowgbQxCzAJBgNVBAYTAlVTMSIwIAYDVQQDExlleGFtcGxlLnR3aXN0ZWRtYXRy
- aXguY29tMQ8wDQYDVQQHEwZCb3N0b24xHDAaBgNVBAoTE1R3aXN0ZWQgTWF0cml4
- IExhYnMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxJzAlBgkqhkiG9w0BCQEWGG5v
- Ym9keUB0d2lzdGVkbWF0cml4LmNvbTERMA8GA1UECxMIU2VjdXJpdHkwgZ8wDQYJ
- KoZIhvcNAQEBBQADgY0AMIGJAoGBAMzH8CDF/U91y/bdbdbJKnLgnyvQ9Ig9ZNZp
- 8hpsu4huil60zF03+Lexg2l1FIfURScjBuaJMR6HiMYTMjhzLuByRZ17KW4wYkGi
- KXstz03VIKy4Tjc+v4aXFI4XdRw10gGMGQlGGscXF/RSoN84VoDKBfOMWdXeConJ
- VyC4w3iJAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAviMT4lBoxOgQy32LIgZ4lVCj
- JNOiZYg8GMQ6y0ugp86X80UjOvkGtNf/R7YgED/giKRN/q/XJiLJDEhzknkocwmO
- S+4b2XpiaZYxRyKWwL221O7CGmtWYyZl2+92YYmmCiNzWQPfP6BOMlfax0AGLHls
- fXzCWdG0O/3Lk2SRM0I=
- -----END CERTIFICATE-----
- """
-
- A_PEER_CERTIFICATE_PEM = """
- -----BEGIN CERTIFICATE-----
- MIIC3jCCAkcCAjA6MA0GCSqGSIb3DQEBBAUAMIG2MQswCQYDVQQGEwJVUzEiMCAG
- A1UEAxMZZXhhbXBsZS50d2lzdGVkbWF0cml4LmNvbTEPMA0GA1UEBxMGQm9zdG9u
- MRwwGgYDVQQKExNUd2lzdGVkIE1hdHJpeCBMYWJzMRYwFAYDVQQIEw1NYXNzYWNo
- dXNldHRzMSkwJwYJKoZIhvcNAQkBFhpzb21lYm9keUB0d2lzdGVkbWF0cml4LmNv
- bTERMA8GA1UECxMIU2VjdXJpdHkwHhcNMDYwODE2MDEwMTU2WhcNMDcwODE2MDEw
- MTU2WjCBtjELMAkGA1UEBhMCVVMxIjAgBgNVBAMTGWV4YW1wbGUudHdpc3RlZG1h
- dHJpeC5jb20xDzANBgNVBAcTBkJvc3RvbjEcMBoGA1UEChMTVHdpc3RlZCBNYXRy
- aXggTGFiczEWMBQGA1UECBMNTWFzc2FjaHVzZXR0czEpMCcGCSqGSIb3DQEJARYa
- c29tZWJvZHlAdHdpc3RlZG1hdHJpeC5jb20xETAPBgNVBAsTCFNlY3VyaXR5MIGf
- MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnm+WBlgFNbMlHehib9ePGGDXF+Nz4
- CjGuUmVBaXCRCiVjg3kSDecwqfb0fqTksBZ+oQ1UBjMcSh7OcvFXJZnUesBikGWE
- JE4V8Bjh+RmbJ1ZAlUPZ40bAkww0OpyIRAGMvKG+4yLFTO4WDxKmfDcrOb6ID8WJ
- e1u+i3XGkIf/5QIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAD4Oukm3YYkhedUepBEA
- vvXIQhVDqL7mk6OqYdXmNj6R7ZMC8WWvGZxrzDI1bZuB+4aIxxd1FXC3UOHiR/xg
- i9cDl1y8P/qRp4aEBNF6rI0D4AxTbfnHQx4ERDAOShJdYZs/2zifPJ6va6YvrEyr
- yqDtGhklsWW3ZwBzEh5VEOUp
- -----END CERTIFICATE-----
- """
-
- A_KEYPAIR = getModule(__name__).filePath.sibling("server.pem").getContent()
-
-
- def counter(counter=itertools.count()):
- """
- Each time we're called, return the next integer in the natural numbers.
- """
- return next(counter)
-
-
- def makeCertificate(**kw):
- keypair = PKey()
- keypair.generate_key(TYPE_RSA, 2048)
-
- certificate = X509()
- certificate.gmtime_adj_notBefore(0)
- certificate.gmtime_adj_notAfter(60 * 60 * 24 * 365) # One year
- for xname in certificate.get_issuer(), certificate.get_subject():
- for (k, v) in kw.items():
- setattr(xname, k, nativeString(v))
-
- certificate.set_serial_number(counter())
- certificate.set_pubkey(keypair)
- certificate.sign(keypair, "md5")
-
- return keypair, certificate
-
-
- def certificatesForAuthorityAndServer(serviceIdentity="example.com"):
- """
- Create a self-signed CA certificate and server certificate signed by the
- CA.
-
- @param serviceIdentity: The identity (hostname) of the server.
- @type serviceIdentity: L{unicode}
-
- @return: a 2-tuple of C{(certificate_authority_certificate,
- server_certificate)}
- @rtype: L{tuple} of (L{sslverify.Certificate},
- L{sslverify.PrivateCertificate})
- """
- commonNameForCA = x509.Name(
- [x509.NameAttribute(NameOID.COMMON_NAME, "Testing Example CA")]
- )
- commonNameForServer = x509.Name(
- [x509.NameAttribute(NameOID.COMMON_NAME, "Testing Example Server")]
- )
- oneDay = datetime.timedelta(1, 0, 0)
- privateKeyForCA = rsa.generate_private_key(
- public_exponent=65537, key_size=4096, backend=default_backend()
- )
- publicKeyForCA = privateKeyForCA.public_key()
- caCertificate = (
- x509.CertificateBuilder()
- .subject_name(commonNameForCA)
- .issuer_name(commonNameForCA)
- .not_valid_before(datetime.datetime.today() - oneDay)
- .not_valid_after(datetime.datetime.today() + oneDay)
- .serial_number(x509.random_serial_number())
- .public_key(publicKeyForCA)
- .add_extension(
- x509.BasicConstraints(ca=True, path_length=9),
- critical=True,
- )
- .sign(
- private_key=privateKeyForCA,
- algorithm=hashes.SHA256(),
- backend=default_backend(),
- )
- )
-
- privateKeyForServer = rsa.generate_private_key(
- public_exponent=65537, key_size=4096, backend=default_backend()
- )
- publicKeyForServer = privateKeyForServer.public_key()
-
- try:
- ipAddress = ipaddress.ip_address(serviceIdentity)
- except ValueError:
- subjectAlternativeNames = [
- x509.DNSName(serviceIdentity.encode("idna").decode("ascii"))
- ]
- else:
- subjectAlternativeNames = [x509.IPAddress(ipAddress)]
-
- serverCertificate = (
- x509.CertificateBuilder()
- .subject_name(commonNameForServer)
- .issuer_name(commonNameForCA)
- .not_valid_before(datetime.datetime.today() - oneDay)
- .not_valid_after(datetime.datetime.today() + oneDay)
- .serial_number(x509.random_serial_number())
- .public_key(publicKeyForServer)
- .add_extension(
- x509.BasicConstraints(ca=False, path_length=None),
- critical=True,
- )
- .add_extension(
- x509.SubjectAlternativeName(subjectAlternativeNames),
- critical=True,
- )
- .sign(
- private_key=privateKeyForCA,
- algorithm=hashes.SHA256(),
- backend=default_backend(),
- )
- )
- caSelfCert = sslverify.Certificate.loadPEM(caCertificate.public_bytes(Encoding.PEM))
- serverCert = sslverify.PrivateCertificate.loadPEM(
- b"\n".join(
- [
- privateKeyForServer.private_bytes(
- Encoding.PEM,
- PrivateFormat.TraditionalOpenSSL,
- NoEncryption(),
- ),
- serverCertificate.public_bytes(Encoding.PEM),
- ]
- )
- )
-
- return caSelfCert, serverCert
-
-
- def _loopbackTLSConnection(serverOpts, clientOpts):
- """
- Common implementation code for both L{loopbackTLSConnection} and
- L{loopbackTLSConnectionInMemory}. Creates a loopback TLS connection
- using the provided server and client context factories.
-
- @param serverOpts: An OpenSSL context factory for the server.
- @type serverOpts: C{OpenSSLCertificateOptions}, or any class with an
- equivalent API.
-
- @param clientOpts: An OpenSSL context factory for the client.
- @type clientOpts: C{OpenSSLCertificateOptions}, or any class with an
- equivalent API.
-
- @return: 5-tuple of server-tls-protocol, server-inner-protocol,
- client-tls-protocol, client-inner-protocol and L{IOPump}
- @rtype: L{tuple}
- """
-
- class GreetingServer(protocol.Protocol):
- greeting = b"greetings!"
-
- def connectionMade(self):
- self.transport.write(self.greeting)
-
- class ListeningClient(protocol.Protocol):
- data = b""
- lostReason = None
-
- def dataReceived(self, data):
- self.data += data
-
- def connectionLost(self, reason):
- self.lostReason = reason
-
- clientWrappedProto = ListeningClient()
- serverWrappedProto = GreetingServer()
-
- plainClientFactory = protocol.Factory()
- plainClientFactory.protocol = lambda: clientWrappedProto
- plainServerFactory = protocol.Factory()
- plainServerFactory.protocol = lambda: serverWrappedProto
-
- clientFactory = TLSMemoryBIOFactory(
- clientOpts, isClient=True, wrappedFactory=plainServerFactory
- )
- serverFactory = TLSMemoryBIOFactory(
- serverOpts, isClient=False, wrappedFactory=plainClientFactory
- )
-
- sProto, cProto, pump = connectedServerAndClient(
- lambda: serverFactory.buildProtocol(None),
- lambda: clientFactory.buildProtocol(None),
- )
- return sProto, cProto, serverWrappedProto, clientWrappedProto, pump
-
-
- def loopbackTLSConnection(trustRoot, privateKeyFile, chainedCertFile=None):
- """
- Create a loopback TLS connection with the given trust and keys.
-
- @param trustRoot: the C{trustRoot} argument for the client connection's
- context.
- @type trustRoot: L{sslverify.IOpenSSLTrustRoot}
-
- @param privateKeyFile: The name of the file containing the private key.
- @type privateKeyFile: L{str} (native string; file name)
-
- @param chainedCertFile: The name of the chained certificate file.
- @type chainedCertFile: L{str} (native string; file name)
-
- @return: 3-tuple of server-protocol, client-protocol, and L{IOPump}
- @rtype: L{tuple}
- """
-
- class ContextFactory:
- def getContext(self):
- """
- Create a context for the server side of the connection.
-
- @return: an SSL context using a certificate and key.
- @rtype: C{OpenSSL.SSL.Context}
- """
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- if chainedCertFile is not None:
- ctx.use_certificate_chain_file(chainedCertFile)
- ctx.use_privatekey_file(privateKeyFile)
- # Let the test author know if they screwed something up.
- ctx.check_privatekey()
- return ctx
-
- serverOpts = ContextFactory()
- clientOpts = sslverify.OpenSSLCertificateOptions(trustRoot=trustRoot)
-
- return _loopbackTLSConnection(serverOpts, clientOpts)
-
-
- def loopbackTLSConnectionInMemory(
- trustRoot,
- privateKey,
- serverCertificate,
- clientProtocols=None,
- serverProtocols=None,
- clientOptions=None,
- ):
- """
- Create a loopback TLS connection with the given trust and keys. Like
- L{loopbackTLSConnection}, but using in-memory certificates and keys rather
- than writing them to disk.
-
- @param trustRoot: the C{trustRoot} argument for the client connection's
- context.
- @type trustRoot: L{sslverify.IOpenSSLTrustRoot}
-
- @param privateKey: The private key.
- @type privateKey: L{str} (native string)
-
- @param serverCertificate: The certificate used by the server.
- @type chainedCertFile: L{str} (native string)
-
- @param clientProtocols: The protocols the client is willing to negotiate
- using NPN/ALPN.
-
- @param serverProtocols: The protocols the server is willing to negotiate
- using NPN/ALPN.
-
- @param clientOptions: The type of C{OpenSSLCertificateOptions} class to
- use for the client. Defaults to C{OpenSSLCertificateOptions}.
-
- @return: 3-tuple of server-protocol, client-protocol, and L{IOPump}
- @rtype: L{tuple}
- """
- if clientOptions is None:
- clientOptions = sslverify.OpenSSLCertificateOptions
-
- clientCertOpts = clientOptions(
- trustRoot=trustRoot, acceptableProtocols=clientProtocols
- )
- serverCertOpts = sslverify.OpenSSLCertificateOptions(
- privateKey=privateKey,
- certificate=serverCertificate,
- acceptableProtocols=serverProtocols,
- )
-
- return _loopbackTLSConnection(serverCertOpts, clientCertOpts)
-
-
- def pathContainingDumpOf(testCase, *dumpables):
- """
- Create a temporary file to store some serializable-as-PEM objects in, and
- return its name.
-
- @param testCase: a test case to use for generating a temporary directory.
- @type testCase: L{twisted.trial.unittest.TestCase}
-
- @param dumpables: arguments are objects from pyOpenSSL with a C{dump}
- method, taking a pyOpenSSL file-type constant, such as
- L{OpenSSL.crypto.FILETYPE_PEM} or L{OpenSSL.crypto.FILETYPE_ASN1}.
- @type dumpables: L{tuple} of L{object} with C{dump} method taking L{int}
- returning L{bytes}
-
- @return: the path to a file where all of the dumpables were dumped in PEM
- format.
- @rtype: L{str}
- """
- fname = testCase.mktemp()
- with open(fname, "wb") as f:
- for dumpable in dumpables:
- f.write(dumpable.dump(FILETYPE_PEM))
- return fname
-
-
- class DataCallbackProtocol(protocol.Protocol):
- def dataReceived(self, data):
- d, self.factory.onData = self.factory.onData, None
- if d is not None:
- d.callback(data)
-
- def connectionLost(self, reason):
- d, self.factory.onLost = self.factory.onLost, None
- if d is not None:
- d.errback(reason)
-
-
- class WritingProtocol(protocol.Protocol):
- byte = b"x"
-
- def connectionMade(self):
- self.transport.write(self.byte)
-
- def connectionLost(self, reason):
- self.factory.onLost.errback(reason)
-
-
- class FakeContext:
- """
- Introspectable fake of an C{OpenSSL.SSL.Context}.
-
- Saves call arguments for later introspection.
-
- Necessary because C{Context} offers poor introspection. cf. this
- U{pyOpenSSL bug<https://bugs.launchpad.net/pyopenssl/+bug/1173899>}.
-
- @ivar _method: See C{method} parameter of L{__init__}.
-
- @ivar _options: L{int} of C{OR}ed values from calls of L{set_options}.
-
- @ivar _certificate: Set by L{use_certificate}.
-
- @ivar _privateKey: Set by L{use_privatekey}.
-
- @ivar _verify: Set by L{set_verify}.
-
- @ivar _verifyDepth: Set by L{set_verify_depth}.
-
- @ivar _mode: Set by L{set_mode}.
-
- @ivar _sessionID: Set by L{set_session_id}.
-
- @ivar _extraCertChain: Accumulated L{list} of all extra certificates added
- by L{add_extra_chain_cert}.
-
- @ivar _cipherList: Set by L{set_cipher_list}.
-
- @ivar _dhFilename: Set by L{load_tmp_dh}.
-
- @ivar _defaultVerifyPathsSet: Set by L{set_default_verify_paths}
-
- @ivar _ecCurve: Set by L{set_tmp_ecdh}
- """
-
- _options = 0
-
- def __init__(self, method):
- self._method = method
- self._extraCertChain = []
- self._defaultVerifyPathsSet = False
- self._ecCurve = None
-
- # Note that this value is explicitly documented as the default by
- # https://www.openssl.org/docs/man1.1.1/man3/
- # SSL_CTX_set_session_cache_mode.html
- self._sessionCacheMode = SSL.SESS_CACHE_SERVER
-
- def set_options(self, options):
- self._options |= options
-
- def use_certificate(self, certificate):
- self._certificate = certificate
-
- def use_privatekey(self, privateKey):
- self._privateKey = privateKey
-
- def check_privatekey(self):
- return None
-
- def set_mode(self, mode):
- """
- Set the mode. See L{SSL.Context.set_mode}.
-
- @param mode: See L{SSL.Context.set_mode}.
- """
- self._mode = mode
-
- def set_verify(self, flags, callback=None):
- self._verify = flags, callback
-
- def set_verify_depth(self, depth):
- self._verifyDepth = depth
-
- def set_session_id(self, sessionIDContext):
- # This fake should change when the upstream changes:
- # https://github.com/pyca/pyopenssl/issues/845
- self._sessionIDContext = sessionIDContext
-
- def set_session_cache_mode(self, cacheMode):
- """
- Set the session cache mode on the context, as per
- L{SSL.Context.set_session_cache_mode}.
- """
- self._sessionCacheMode = cacheMode
-
- def get_session_cache_mode(self):
- """
- Retrieve the session cache mode from the context, as per
- L{SSL.Context.get_session_cache_mode}.
- """
- return self._sessionCacheMode
-
- def add_extra_chain_cert(self, cert):
- self._extraCertChain.append(cert)
-
- def set_cipher_list(self, cipherList):
- self._cipherList = cipherList
-
- def load_tmp_dh(self, dhfilename):
- self._dhFilename = dhfilename
-
- def set_default_verify_paths(self):
- """
- Set the default paths for the platform.
- """
- self._defaultVerifyPathsSet = True
-
- def set_tmp_ecdh(self, curve):
- """
- Set an ECDH curve. Should only be called by OpenSSL 1.0.1
- code.
-
- @param curve: See L{OpenSSL.SSL.Context.set_tmp_ecdh}
- """
- self._ecCurve = curve
-
-
- class ClientOptionsTests(SynchronousTestCase):
- """
- Tests for L{sslverify.optionsForClientTLS}.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def test_extraKeywords(self):
- """
- When passed a keyword parameter other than C{extraCertificateOptions},
- L{sslverify.optionsForClientTLS} raises an exception just like a
- normal Python function would.
- """
- error = self.assertRaises(
- TypeError,
- sslverify.optionsForClientTLS,
- hostname="alpha",
- someRandomThing="beta",
- )
- self.assertEqual(
- str(error),
- "optionsForClientTLS() got an unexpected keyword argument "
- "'someRandomThing'",
- )
-
- def test_bytesFailFast(self):
- """
- If you pass L{bytes} as the hostname to
- L{sslverify.optionsForClientTLS} it immediately raises a L{TypeError}.
- """
- error = self.assertRaises(
- TypeError, sslverify.optionsForClientTLS, b"not-actually-a-hostname.com"
- )
- expectedText = (
- "optionsForClientTLS requires text for host names, not " + bytes.__name__
- )
- self.assertEqual(str(error), expectedText)
-
- def test_dNSNameHostname(self):
- """
- If you pass a dNSName to L{sslverify.optionsForClientTLS}
- L{_hostnameIsDnsName} will be True
- """
- options = sslverify.optionsForClientTLS("example.com")
- self.assertTrue(options._hostnameIsDnsName)
-
- def test_IPv4AddressHostname(self):
- """
- If you pass an IPv4 address to L{sslverify.optionsForClientTLS}
- L{_hostnameIsDnsName} will be False
- """
- options = sslverify.optionsForClientTLS("127.0.0.1")
- self.assertFalse(options._hostnameIsDnsName)
-
- def test_IPv6AddressHostname(self):
- """
- If you pass an IPv6 address to L{sslverify.optionsForClientTLS}
- L{_hostnameIsDnsName} will be False
- """
- options = sslverify.optionsForClientTLS("::1")
- self.assertFalse(options._hostnameIsDnsName)
-
-
- class FakeChooseDiffieHellmanEllipticCurve:
- """
- A fake implementation of L{_ChooseDiffieHellmanEllipticCurve}
- """
-
- def __init__(self, versionNumber, openSSLlib, openSSLcrypto):
- """
- A no-op constructor.
- """
-
- def configureECDHCurve(self, ctx):
- """
- A null configuration.
-
- @param ctx: An L{OpenSSL.SSL.Context} that would be
- configured.
- """
-
-
- class OpenSSLOptionsTestsMixin:
- """
- A mixin for L{OpenSSLOptions} test cases creates client and server
- certificates, signs them with a CA, and provides a L{loopback}
- that creates TLS a connections with them.
- """
-
- if skipSSL:
- skip = skipSSL
-
- serverPort = clientConn = None
- onServerLost = onClientLost = None
-
- def setUp(self):
- """
- Create class variables of client and server certificates.
- """
- self.sKey, self.sCert = makeCertificate(
- O=b"Server Test Certificate", CN=b"server"
- )
- self.cKey, self.cCert = makeCertificate(
- O=b"Client Test Certificate", CN=b"client"
- )
- self.caCert1 = makeCertificate(O=b"CA Test Certificate 1", CN=b"ca1")[1]
- self.caCert2 = makeCertificate(O=b"CA Test Certificate", CN=b"ca2")[1]
- self.caCerts = [self.caCert1, self.caCert2]
- self.extraCertChain = self.caCerts
-
- def tearDown(self):
- if self.serverPort is not None:
- self.serverPort.stopListening()
- if self.clientConn is not None:
- self.clientConn.disconnect()
-
- L = []
- if self.onServerLost is not None:
- L.append(self.onServerLost)
- if self.onClientLost is not None:
- L.append(self.onClientLost)
-
- return defer.DeferredList(L, consumeErrors=True)
-
- def loopback(
- self,
- serverCertOpts,
- clientCertOpts,
- onServerLost=None,
- onClientLost=None,
- onData=None,
- ):
- if onServerLost is None:
- self.onServerLost = onServerLost = defer.Deferred()
- if onClientLost is None:
- self.onClientLost = onClientLost = defer.Deferred()
- if onData is None:
- onData = defer.Deferred()
-
- serverFactory = protocol.ServerFactory()
- serverFactory.protocol = DataCallbackProtocol
- serverFactory.onLost = onServerLost
- serverFactory.onData = onData
-
- clientFactory = protocol.ClientFactory()
- clientFactory.protocol = WritingProtocol
- clientFactory.onLost = onClientLost
-
- self.serverPort = reactor.listenSSL(0, serverFactory, serverCertOpts)
- self.clientConn = reactor.connectSSL(
- "127.0.0.1", self.serverPort.getHost().port, clientFactory, clientCertOpts
- )
-
-
- class OpenSSLOptionsTests(OpenSSLOptionsTestsMixin, TestCase):
- """
- Tests for L{sslverify.OpenSSLOptions}.
- """
-
- def setUp(self):
- """
- Same as L{OpenSSLOptionsTestsMixin.setUp}, but it also patches
- L{sslverify._ChooseDiffieHellmanEllipticCurve}.
- """
- super().setUp()
- self.patch(
- sslverify,
- "_ChooseDiffieHellmanEllipticCurve",
- FakeChooseDiffieHellmanEllipticCurve,
- )
-
- def test_constructorWithOnlyPrivateKey(self):
- """
- C{privateKey} and C{certificate} make only sense if both are set.
- """
- self.assertRaises(
- ValueError, sslverify.OpenSSLCertificateOptions, privateKey=self.sKey
- )
-
- def test_constructorWithOnlyCertificate(self):
- """
- C{privateKey} and C{certificate} make only sense if both are set.
- """
- self.assertRaises(
- ValueError, sslverify.OpenSSLCertificateOptions, certificate=self.sCert
- )
-
- def test_constructorWithCertificateAndPrivateKey(self):
- """
- Specifying C{privateKey} and C{certificate} initializes correctly.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey, certificate=self.sCert
- )
- self.assertEqual(opts.privateKey, self.sKey)
- self.assertEqual(opts.certificate, self.sCert)
- self.assertEqual(opts.extraCertChain, [])
-
- def test_constructorDoesNotAllowVerifyWithoutCACerts(self):
- """
- C{verify} must not be C{True} without specifying C{caCerts}.
- """
- self.assertRaises(
- ValueError,
- sslverify.OpenSSLCertificateOptions,
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=True,
- )
-
- def test_constructorDoesNotAllowLegacyWithTrustRoot(self):
- """
- C{verify}, C{requireCertificate}, and C{caCerts} must not be specified
- by the caller (to be I{any} value, even the default!) when specifying
- C{trustRoot}.
- """
- self.assertRaises(
- TypeError,
- sslverify.OpenSSLCertificateOptions,
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=True,
- trustRoot=None,
- caCerts=self.caCerts,
- )
- self.assertRaises(
- TypeError,
- sslverify.OpenSSLCertificateOptions,
- privateKey=self.sKey,
- certificate=self.sCert,
- trustRoot=None,
- requireCertificate=True,
- )
-
- def test_constructorAllowsCACertsWithoutVerify(self):
- """
- It's currently a NOP, but valid.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey, certificate=self.sCert, caCerts=self.caCerts
- )
- self.assertFalse(opts.verify)
- self.assertEqual(self.caCerts, opts.caCerts)
-
- def test_constructorWithVerifyAndCACerts(self):
- """
- Specifying C{verify} and C{caCerts} initializes correctly.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=True,
- caCerts=self.caCerts,
- )
- self.assertTrue(opts.verify)
- self.assertEqual(self.caCerts, opts.caCerts)
-
- def test_constructorSetsExtraChain(self):
- """
- Setting C{extraCertChain} works if C{certificate} and C{privateKey} are
- set along with it.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- extraCertChain=self.extraCertChain,
- )
- self.assertEqual(self.extraCertChain, opts.extraCertChain)
-
- def test_constructorDoesNotAllowExtraChainWithoutPrivateKey(self):
- """
- A C{extraCertChain} without C{privateKey} doesn't make sense and is
- thus rejected.
- """
- self.assertRaises(
- ValueError,
- sslverify.OpenSSLCertificateOptions,
- certificate=self.sCert,
- extraCertChain=self.extraCertChain,
- )
-
- def test_constructorDoesNotAllowExtraChainWithOutPrivateKey(self):
- """
- A C{extraCertChain} without C{certificate} doesn't make sense and is
- thus rejected.
- """
- self.assertRaises(
- ValueError,
- sslverify.OpenSSLCertificateOptions,
- privateKey=self.sKey,
- extraCertChain=self.extraCertChain,
- )
-
- def test_extraChainFilesAreAddedIfSupplied(self):
- """
- If C{extraCertChain} is set and all prerequisites are met, the
- specified chain certificates are added to C{Context}s that get
- created.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- extraCertChain=self.extraCertChain,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- self.assertEqual(self.sKey, ctx._privateKey)
- self.assertEqual(self.sCert, ctx._certificate)
- self.assertEqual(self.extraCertChain, ctx._extraCertChain)
-
- def test_extraChainDoesNotBreakPyOpenSSL(self):
- """
- C{extraCertChain} doesn't break C{OpenSSL.SSL.Context} creation.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- extraCertChain=self.extraCertChain,
- )
- ctx = opts.getContext()
- self.assertIsInstance(ctx, SSL.Context)
-
- def test_acceptableCiphersAreAlwaysSet(self):
- """
- If the user doesn't supply custom acceptable ciphers, a shipped secure
- default is used. We can't check directly for it because the effective
- cipher string we set varies with platforms.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- self.assertEqual(opts._cipherString.encode("ascii"), ctx._cipherList)
-
- def test_givesMeaningfulErrorMessageIfNoCipherMatches(self):
- """
- If there is no valid cipher that matches the user's wishes,
- a L{ValueError} is raised.
- """
- self.assertRaises(
- ValueError,
- sslverify.OpenSSLCertificateOptions,
- privateKey=self.sKey,
- certificate=self.sCert,
- acceptableCiphers=sslverify.OpenSSLAcceptableCiphers.fromOpenSSLCipherString(
- ""
- ),
- )
-
- def test_honorsAcceptableCiphersArgument(self):
- """
- If acceptable ciphers are passed, they are used.
- """
-
- @implementer(interfaces.IAcceptableCiphers)
- class FakeAcceptableCiphers:
- def selectCiphers(self, _):
- return [sslverify.OpenSSLCipher("sentinel")]
-
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- acceptableCiphers=FakeAcceptableCiphers(),
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- self.assertEqual(b"sentinel", ctx._cipherList)
-
- def test_basicSecurityOptionsAreSet(self):
- """
- Every context must have C{OP_NO_SSLv2}, C{OP_NO_COMPRESSION}, and
- C{OP_CIPHER_SERVER_PREFERENCE} set.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2 | SSL.OP_NO_COMPRESSION | SSL.OP_CIPHER_SERVER_PREFERENCE
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_modeIsSet(self):
- """
- Every context must be in C{MODE_RELEASE_BUFFERS} mode.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- self.assertEqual(SSL.MODE_RELEASE_BUFFERS, ctx._mode)
-
- def test_singleUseKeys(self):
- """
- If C{singleUseKeys} is set, every context must have
- C{OP_SINGLE_DH_USE} and C{OP_SINGLE_ECDH_USE} set.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- enableSingleUseKeys=True,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = SSL.OP_SINGLE_DH_USE | SSL.OP_SINGLE_ECDH_USE
- self.assertEqual(options, ctx._options & options)
-
- def test_methodIsDeprecated(self):
- """
- Passing C{method} to L{sslverify.OpenSSLCertificateOptions} is
- deprecated.
- """
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- method=SSL.SSLv23_METHOD,
- )
-
- message = (
- "Passing method to twisted.internet.ssl.CertificateOptions "
- "was deprecated in Twisted 17.1.0. Please use a "
- "combination of insecurelyLowerMinimumTo, raiseMinimumTo, "
- "and lowerMaximumSecurityTo instead, as Twisted will "
- "correctly configure the method."
- )
-
- warnings = self.flushWarnings([self.test_methodIsDeprecated])
- self.assertEqual(1, len(warnings))
- self.assertEqual(DeprecationWarning, warnings[0]["category"])
- self.assertEqual(message, warnings[0]["message"])
-
- def test_tlsv12ByDefault(self):
- """
- L{sslverify.OpenSSLCertificateOptions} will make the default minimum
- TLS version v1.2, if no C{method}, or C{insecurelyLowerMinimumTo} is
- given.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey, certificate=self.sCert
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsAtLeastWithMinimum(self):
- """
- Passing C{insecurelyLowerMinimumTo} along with C{raiseMinimumTo} to
- L{sslverify.OpenSSLCertificateOptions} will cause it to raise an
- exception.
- """
- with self.assertRaises(TypeError) as e:
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- raiseMinimumTo=sslverify.TLSVersion.TLSv1_2,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_2,
- )
-
- self.assertIn("raiseMinimumTo", e.exception.args[0])
- self.assertIn("insecurelyLowerMinimumTo", e.exception.args[0])
- self.assertIn("exclusive", e.exception.args[0])
-
- def test_tlsProtocolsNoMethodWithAtLeast(self):
- """
- Passing C{raiseMinimumTo} along with C{method} to
- L{sslverify.OpenSSLCertificateOptions} will cause it to raise an
- exception.
- """
- with self.assertRaises(TypeError) as e:
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- method=SSL.SSLv23_METHOD,
- raiseMinimumTo=sslverify.TLSVersion.TLSv1_2,
- )
-
- self.assertIn("method", e.exception.args[0])
- self.assertIn("raiseMinimumTo", e.exception.args[0])
- self.assertIn("exclusive", e.exception.args[0])
-
- def test_tlsProtocolsNoMethodWithMinimum(self):
- """
- Passing C{insecurelyLowerMinimumTo} along with C{method} to
- L{sslverify.OpenSSLCertificateOptions} will cause it to raise an
- exception.
- """
- with self.assertRaises(TypeError) as e:
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- method=SSL.SSLv23_METHOD,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_2,
- )
-
- self.assertIn("method", e.exception.args[0])
- self.assertIn("insecurelyLowerMinimumTo", e.exception.args[0])
- self.assertIn("exclusive", e.exception.args[0])
-
- def test_tlsProtocolsNoMethodWithMaximum(self):
- """
- Passing C{lowerMaximumSecurityTo} along with C{method} to
- L{sslverify.OpenSSLCertificateOptions} will cause it to raise an
- exception.
- """
- with self.assertRaises(TypeError) as e:
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- method=SSL.TLS_METHOD,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_2,
- )
-
- self.assertIn("method", e.exception.args[0])
- self.assertIn("lowerMaximumSecurityTo", e.exception.args[0])
- self.assertIn("exclusive", e.exception.args[0])
-
- def test_tlsVersionRangeInOrder(self):
- """
- Passing out of order TLS versions to C{insecurelyLowerMinimumTo} and
- C{lowerMaximumSecurityTo} will cause it to raise an exception.
- """
- with self.assertRaises(ValueError) as e:
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_0,
- lowerMaximumSecurityTo=sslverify.TLSVersion.SSLv3,
- )
-
- self.assertEqual(
- e.exception.args,
- (
- (
- "insecurelyLowerMinimumTo needs to be lower than "
- "lowerMaximumSecurityTo"
- ),
- ),
- )
-
- def test_tlsVersionRangeInOrderAtLeast(self):
- """
- Passing out of order TLS versions to C{raiseMinimumTo} and
- C{lowerMaximumSecurityTo} will cause it to raise an exception.
- """
- with self.assertRaises(ValueError) as e:
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- raiseMinimumTo=sslverify.TLSVersion.TLSv1_0,
- lowerMaximumSecurityTo=sslverify.TLSVersion.SSLv3,
- )
-
- self.assertEqual(
- e.exception.args,
- (("raiseMinimumTo needs to be lower than " "lowerMaximumSecurityTo"),),
- )
-
- def test_tlsProtocolsreduceToMaxWithoutMin(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{lowerMaximumSecurityTo} but no C{raiseMinimumTo} or
- C{insecurelyLowerMinimumTo} set, and C{lowerMaximumSecurityTo} is
- below the minimum default, the minimum will be made the new maximum.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- lowerMaximumSecurityTo=sslverify.TLSVersion.SSLv3,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- | SSL.OP_NO_TLSv1_2
- | opts._OP_NO_TLSv1_3
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsSSLv3Only(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{insecurelyLowerMinimumTo} and C{lowerMaximumSecurityTo} set to
- SSLv3, it will exclude all others.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.SSLv3,
- lowerMaximumSecurityTo=sslverify.TLSVersion.SSLv3,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- | SSL.OP_NO_TLSv1_2
- | opts._OP_NO_TLSv1_3
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsTLSv1Point0Only(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{insecurelyLowerMinimumTo} and C{lowerMaximumSecurityTo} set to v1.0,
- it will exclude all others.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_0,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_0,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1_1
- | SSL.OP_NO_TLSv1_2
- | opts._OP_NO_TLSv1_3
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsTLSv1Point1Only(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{insecurelyLowerMinimumTo} and C{lowerMaximumSecurityTo} set to v1.1,
- it will exclude all others.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_1,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_1,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_2
- | opts._OP_NO_TLSv1_3
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsTLSv1Point2Only(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{insecurelyLowerMinimumTo} and C{lowerMaximumSecurityTo} set to v1.2,
- it will exclude all others.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_2,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_2,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- | opts._OP_NO_TLSv1_3
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsAllModernTLS(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{insecurelyLowerMinimumTo} set to TLSv1.0 and
- C{lowerMaximumSecurityTo} to TLSv1.2, it will exclude both SSLs and
- the (unreleased) TLSv1.3.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_0,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_2,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | opts._OP_NO_TLSv1_3
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsAtLeastAllSecureTLS(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{raiseMinimumTo} set to TLSv1.2, it will ignore all TLSs below
- 1.2 and SSL.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- raiseMinimumTo=sslverify.TLSVersion.TLSv1_2,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_tlsProtocolsAtLeastWillAcceptHigherDefault(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{raiseMinimumTo} set to a value lower than Twisted's default will
- cause it to use the more secure default.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- raiseMinimumTo=sslverify.TLSVersion.SSLv3,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- # Future maintainer warning: this will break if we change our default
- # up, so you should change it to add the relevant OP_NO flags when we
- # do make that change and this test fails.
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- )
- self.assertEqual(options, ctx._options & options)
- self.assertEqual(opts._defaultMinimumTLSVersion, sslverify.TLSVersion.TLSv1_2)
-
- def test_tlsProtocolsAllSecureTLS(self):
- """
- When calling L{sslverify.OpenSSLCertificateOptions} with
- C{insecurelyLowerMinimumTo} set to TLSv1.2, it will ignore all TLSs below
- 1.2 and SSL.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- insecurelyLowerMinimumTo=sslverify.TLSVersion.TLSv1_2,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- options = (
- SSL.OP_NO_SSLv2
- | SSL.OP_NO_COMPRESSION
- | SSL.OP_CIPHER_SERVER_PREFERENCE
- | SSL.OP_NO_SSLv3
- | SSL.OP_NO_TLSv1
- | SSL.OP_NO_TLSv1_1
- )
- self.assertEqual(options, ctx._options & options)
-
- def test_dhParams(self):
- """
- If C{dhParams} is set, they are loaded into each new context.
- """
-
- class FakeDiffieHellmanParameters:
- _dhFile = FilePath(b"dh.params")
-
- dhParams = FakeDiffieHellmanParameters()
- opts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- dhParameters=dhParams,
- )
- opts._contextFactory = FakeContext
- ctx = opts.getContext()
- self.assertEqual(FakeDiffieHellmanParameters._dhFile.path, ctx._dhFilename)
-
- def test_abbreviatingDistinguishedNames(self):
- """
- Check that abbreviations used in certificates correctly map to
- complete names.
- """
- self.assertEqual(
- sslverify.DN(CN=b"a", OU=b"hello"),
- sslverify.DistinguishedName(
- commonName=b"a", organizationalUnitName=b"hello"
- ),
- )
- self.assertNotEqual(
- sslverify.DN(CN=b"a", OU=b"hello"),
- sslverify.DN(CN=b"a", OU=b"hello", emailAddress=b"xxx"),
- )
- dn = sslverify.DN(CN=b"abcdefg")
- self.assertRaises(AttributeError, setattr, dn, "Cn", b"x")
- self.assertEqual(dn.CN, dn.commonName)
- dn.CN = b"bcdefga"
- self.assertEqual(dn.CN, dn.commonName)
-
- def testInspectDistinguishedName(self):
- n = sslverify.DN(
- commonName=b"common name",
- organizationName=b"organization name",
- organizationalUnitName=b"organizational unit name",
- localityName=b"locality name",
- stateOrProvinceName=b"state or province name",
- countryName=b"country name",
- emailAddress=b"email address",
- )
- s = n.inspect()
- for k in [
- "common name",
- "organization name",
- "organizational unit name",
- "locality name",
- "state or province name",
- "country name",
- "email address",
- ]:
- self.assertIn(k, s, f"{k!r} was not in inspect output.")
- self.assertIn(k.title(), s, f"{k!r} was not in inspect output.")
-
- def testInspectDistinguishedNameWithoutAllFields(self):
- n = sslverify.DN(localityName=b"locality name")
- s = n.inspect()
- for k in [
- "common name",
- "organization name",
- "organizational unit name",
- "state or province name",
- "country name",
- "email address",
- ]:
- self.assertNotIn(k, s, f"{k!r} was in inspect output.")
- self.assertNotIn(k.title(), s, f"{k!r} was in inspect output.")
- self.assertIn("locality name", s)
- self.assertIn("Locality Name", s)
-
- def test_inspectCertificate(self):
- """
- Test that the C{inspect} method of L{sslverify.Certificate} returns
- a human-readable string containing some basic information about the
- certificate.
- """
- c = sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM)
- pk = c.getPublicKey()
- keyHash = pk.keyHash()
- # Maintenance Note: the algorithm used to compute the "public key hash"
- # is highly dubious and can differ between underlying versions of
- # OpenSSL (and across versions of Twisted), since it is not actually
- # the hash of the public key by itself. If we can get the appropriate
- # APIs to get the hash of the key itself out of OpenSSL, then we should
- # be able to make it statically declared inline below again rather than
- # computing it here.
- self.assertEqual(
- c.inspect().split("\n"),
- [
- "Certificate For Subject:",
- " Common Name: example.twistedmatrix.com",
- " Country Name: US",
- " Email Address: nobody@twistedmatrix.com",
- " Locality Name: Boston",
- " Organization Name: Twisted Matrix Labs",
- " Organizational Unit Name: Security",
- " State Or Province Name: Massachusetts",
- "",
- "Issuer:",
- " Common Name: example.twistedmatrix.com",
- " Country Name: US",
- " Email Address: nobody@twistedmatrix.com",
- " Locality Name: Boston",
- " Organization Name: Twisted Matrix Labs",
- " Organizational Unit Name: Security",
- " State Or Province Name: Massachusetts",
- "",
- "Serial Number: 12345",
- "Digest: C4:96:11:00:30:C3:EC:EE:A3:55:AA:ED:8C:84:85:18",
- "Public Key with Hash: " + keyHash,
- ],
- )
-
- def test_publicKeyMatching(self):
- """
- L{PublicKey.matches} returns L{True} for keys from certificates with
- the same key, and L{False} for keys from certificates with different
- keys.
- """
- hostA = sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM)
- hostB = sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM)
- peerA = sslverify.Certificate.loadPEM(A_PEER_CERTIFICATE_PEM)
-
- self.assertTrue(hostA.getPublicKey().matches(hostB.getPublicKey()))
- self.assertFalse(peerA.getPublicKey().matches(hostA.getPublicKey()))
-
- def test_enablingAndDisablingSessions(self):
- """
- The enableSessions argument sets the session cache mode; it defaults to
- False (at least until https://twistedmatrix.com/trac/ticket/9764 can be
- resolved).
- """
- options = sslverify.OpenSSLCertificateOptions()
- self.assertEqual(options.enableSessions, False)
- ctx = options.getContext()
- self.assertEqual(ctx.get_session_cache_mode(), SSL.SESS_CACHE_OFF)
- options = sslverify.OpenSSLCertificateOptions(enableSessions=True)
- self.assertEqual(options.enableSessions, True)
- ctx = options.getContext()
- self.assertEqual(ctx.get_session_cache_mode(), SSL.SESS_CACHE_SERVER)
-
- def test_certificateOptionsSerialization(self):
- """
- Test that __setstate__(__getstate__()) round-trips properly.
- """
- firstOpts = sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- method=SSL.SSLv23_METHOD,
- verify=True,
- caCerts=[self.sCert],
- verifyDepth=2,
- requireCertificate=False,
- verifyOnce=False,
- enableSingleUseKeys=False,
- enableSessions=False,
- fixBrokenPeers=True,
- enableSessionTickets=True,
- )
- context = firstOpts.getContext()
- self.assertIs(context, firstOpts._context)
- self.assertIsNotNone(context)
- state = firstOpts.__getstate__()
- self.assertNotIn("_context", state)
-
- opts = sslverify.OpenSSLCertificateOptions()
- opts.__setstate__(state)
- self.assertEqual(opts.privateKey, self.sKey)
- self.assertEqual(opts.certificate, self.sCert)
- self.assertEqual(opts.method, SSL.SSLv23_METHOD)
- self.assertTrue(opts.verify)
- self.assertEqual(opts.caCerts, [self.sCert])
- self.assertEqual(opts.verifyDepth, 2)
- self.assertFalse(opts.requireCertificate)
- self.assertFalse(opts.verifyOnce)
- self.assertFalse(opts.enableSingleUseKeys)
- self.assertFalse(opts.enableSessions)
- self.assertTrue(opts.fixBrokenPeers)
- self.assertTrue(opts.enableSessionTickets)
-
- test_certificateOptionsSerialization.suppress = [ # type: ignore[attr-defined]
- util.suppress(
- category=DeprecationWarning,
- message=r"twisted\.internet\._sslverify\.*__[gs]etstate__",
- )
- ]
-
- def test_certificateOptionsSessionTickets(self):
- """
- Enabling session tickets should not set the OP_NO_TICKET option.
- """
- opts = sslverify.OpenSSLCertificateOptions(enableSessionTickets=True)
- ctx = opts.getContext()
- self.assertEqual(0, ctx.set_options(0) & 0x00004000)
-
- def test_certificateOptionsSessionTicketsDisabled(self):
- """
- Enabling session tickets should set the OP_NO_TICKET option.
- """
- opts = sslverify.OpenSSLCertificateOptions(enableSessionTickets=False)
- ctx = opts.getContext()
- self.assertEqual(0x00004000, ctx.set_options(0) & 0x00004000)
-
- def test_allowedAnonymousClientConnection(self):
- """
- Check that anonymous connections are allowed when certificates aren't
- required on the server.
- """
- onData = defer.Deferred()
- self.loopback(
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey, certificate=self.sCert, requireCertificate=False
- ),
- sslverify.OpenSSLCertificateOptions(requireCertificate=False),
- onData=onData,
- )
-
- return onData.addCallback(
- lambda result: self.assertEqual(result, WritingProtocol.byte)
- )
-
- def test_refusedAnonymousClientConnection(self):
- """
- Check that anonymous connections are refused when certificates are
- required on the server.
- """
- onServerLost = defer.Deferred()
- onClientLost = defer.Deferred()
- self.loopback(
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=True,
- caCerts=[self.sCert],
- requireCertificate=True,
- ),
- sslverify.OpenSSLCertificateOptions(requireCertificate=False),
- onServerLost=onServerLost,
- onClientLost=onClientLost,
- )
-
- d = defer.DeferredList([onClientLost, onServerLost], consumeErrors=True)
-
- def afterLost(result):
- ((cSuccess, cResult), (sSuccess, sResult)) = result
- self.assertFalse(cSuccess)
- self.assertFalse(sSuccess)
- # Win32 fails to report the SSL Error, and report a connection lost
- # instead: there is a race condition so that's not totally
- # surprising (see ticket #2877 in the tracker)
- self.assertIsInstance(cResult.value, (SSL.Error, ConnectionLost))
- self.assertIsInstance(sResult.value, SSL.Error)
-
- return d.addCallback(afterLost)
-
- def test_failedCertificateVerification(self):
- """
- Check that connecting with a certificate not accepted by the server CA
- fails.
- """
- onServerLost = defer.Deferred()
- onClientLost = defer.Deferred()
- self.loopback(
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=False,
- requireCertificate=False,
- ),
- sslverify.OpenSSLCertificateOptions(
- verify=True, requireCertificate=False, caCerts=[self.cCert]
- ),
- onServerLost=onServerLost,
- onClientLost=onClientLost,
- )
-
- d = defer.DeferredList([onClientLost, onServerLost], consumeErrors=True)
-
- def afterLost(result):
- ((cSuccess, cResult), (sSuccess, sResult)) = result
- self.assertFalse(cSuccess)
- self.assertFalse(sSuccess)
-
- return d.addCallback(afterLost)
-
- def test_successfulCertificateVerification(self):
- """
- Test a successful connection with client certificate validation on
- server side.
- """
- onData = defer.Deferred()
- self.loopback(
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=False,
- requireCertificate=False,
- ),
- sslverify.OpenSSLCertificateOptions(
- verify=True, requireCertificate=True, caCerts=[self.sCert]
- ),
- onData=onData,
- )
-
- return onData.addCallback(
- lambda result: self.assertEqual(result, WritingProtocol.byte)
- )
-
- def test_successfulSymmetricSelfSignedCertificateVerification(self):
- """
- Test a successful connection with validation on both server and client
- sides.
- """
- onData = defer.Deferred()
- self.loopback(
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- verify=True,
- requireCertificate=True,
- caCerts=[self.cCert],
- ),
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.cKey,
- certificate=self.cCert,
- verify=True,
- requireCertificate=True,
- caCerts=[self.sCert],
- ),
- onData=onData,
- )
-
- return onData.addCallback(
- lambda result: self.assertEqual(result, WritingProtocol.byte)
- )
-
- def test_verification(self):
- """
- Check certificates verification building custom certificates data.
- """
- clientDN = sslverify.DistinguishedName(commonName="client")
- clientKey = sslverify.KeyPair.generate()
- clientCertReq = clientKey.certificateRequest(clientDN)
-
- serverDN = sslverify.DistinguishedName(commonName="server")
- serverKey = sslverify.KeyPair.generate()
- serverCertReq = serverKey.certificateRequest(serverDN)
-
- clientSelfCertReq = clientKey.certificateRequest(clientDN)
- clientSelfCertData = clientKey.signCertificateRequest(
- clientDN, clientSelfCertReq, lambda dn: True, 132
- )
- clientSelfCert = clientKey.newCertificate(clientSelfCertData)
-
- serverSelfCertReq = serverKey.certificateRequest(serverDN)
- serverSelfCertData = serverKey.signCertificateRequest(
- serverDN, serverSelfCertReq, lambda dn: True, 516
- )
- serverSelfCert = serverKey.newCertificate(serverSelfCertData)
-
- clientCertData = serverKey.signCertificateRequest(
- serverDN, clientCertReq, lambda dn: True, 7
- )
- clientCert = clientKey.newCertificate(clientCertData)
-
- serverCertData = clientKey.signCertificateRequest(
- clientDN, serverCertReq, lambda dn: True, 42
- )
- serverCert = serverKey.newCertificate(serverCertData)
-
- onData = defer.Deferred()
-
- serverOpts = serverCert.options(serverSelfCert)
- clientOpts = clientCert.options(clientSelfCert)
-
- self.loopback(serverOpts, clientOpts, onData=onData)
-
- return onData.addCallback(
- lambda result: self.assertEqual(result, WritingProtocol.byte)
- )
-
-
- class OpenSSLOptionsECDHIntegrationTests(OpenSSLOptionsTestsMixin, TestCase):
- """
- ECDH-related integration tests for L{OpenSSLOptions}.
- """
-
- def test_ellipticCurveDiffieHellman(self):
- """
- Connections use ECDH when OpenSSL supports it.
- """
- if not get_elliptic_curves():
- raise SkipTest("OpenSSL does not support ECDH.")
-
- onData = defer.Deferred()
- # TLS 1.3 cipher suites do not specify the key exchange
- # mechanism:
- # https://wiki.openssl.org/index.php/TLS1.3#Differences_with_TLS1.2_and_below
- #
- # and OpenSSL only supports ECHDE groups with TLS 1.3:
- # https://wiki.openssl.org/index.php/TLS1.3#Groups
- #
- # so TLS 1.3 implies ECDHE. Force this test to use TLS 1.3 to
- # ensure ECDH is selected when it might not be.
- self.loopback(
- sslverify.OpenSSLCertificateOptions(
- privateKey=self.sKey,
- certificate=self.sCert,
- requireCertificate=False,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_3,
- ),
- sslverify.OpenSSLCertificateOptions(
- requireCertificate=False,
- lowerMaximumSecurityTo=sslverify.TLSVersion.TLSv1_3,
- ),
- onData=onData,
- )
-
- @onData.addCallback
- def assertECDH(_):
- self.assertEqual(len(self.clientConn.factory.protocols), 1)
- [clientProtocol] = self.clientConn.factory.protocols
- cipher = clientProtocol.getHandle().get_cipher_name()
- self.assertIn("ECDH", cipher)
-
- return onData
-
-
- class DeprecationTests(SynchronousTestCase):
- """
- Tests for deprecation of L{sslverify.OpenSSLCertificateOptions}'s support
- of the pickle protocol.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def test_getstateDeprecation(self):
- """
- L{sslverify.OpenSSLCertificateOptions.__getstate__} is deprecated.
- """
- self.callDeprecated(
- (Version("Twisted", 15, 0, 0), "a real persistence system"),
- sslverify.OpenSSLCertificateOptions().__getstate__,
- )
-
- def test_setstateDeprecation(self):
- """
- L{sslverify.OpenSSLCertificateOptions.__setstate__} is deprecated.
- """
- self.callDeprecated(
- (Version("Twisted", 15, 0, 0), "a real persistence system"),
- sslverify.OpenSSLCertificateOptions().__setstate__,
- {},
- )
-
-
- class TrustRootTests(TestCase):
- """
- Tests for L{sslverify.OpenSSLCertificateOptions}' C{trustRoot} argument,
- L{sslverify.platformTrust}, and their interactions.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def setUp(self):
- """
- Patch L{sslverify._ChooseDiffieHellmanEllipticCurve}.
- """
- self.patch(
- sslverify,
- "_ChooseDiffieHellmanEllipticCurve",
- FakeChooseDiffieHellmanEllipticCurve,
- )
-
- def test_caCertsPlatformDefaults(self):
- """
- Specifying a C{trustRoot} of L{sslverify.OpenSSLDefaultPaths} when
- initializing L{sslverify.OpenSSLCertificateOptions} loads the
- platform-provided trusted certificates via C{set_default_verify_paths}.
- """
- opts = sslverify.OpenSSLCertificateOptions(
- trustRoot=sslverify.OpenSSLDefaultPaths(),
- )
- fc = FakeContext(SSL.TLSv1_METHOD)
- opts._contextFactory = lambda method: fc
- opts.getContext()
- self.assertTrue(fc._defaultVerifyPathsSet)
-
- def test_trustRootPlatformRejectsUntrustedCA(self):
- """
- Specifying a C{trustRoot} of L{platformTrust} when initializing
- L{sslverify.OpenSSLCertificateOptions} causes certificates issued by a
- newly created CA to be rejected by an SSL connection using these
- options.
-
- Note that this test should I{always} pass, even on platforms where the
- CA certificates are not installed, as long as L{platformTrust} rejects
- completely invalid / unknown root CA certificates. This is simply a
- smoke test to make sure that verification is happening at all.
- """
- caSelfCert, serverCert = certificatesForAuthorityAndServer()
- chainedCert = pathContainingDumpOf(self, serverCert, caSelfCert)
- privateKey = pathContainingDumpOf(self, serverCert.privateKey)
-
- sProto, cProto, sWrapped, cWrapped, pump = loopbackTLSConnection(
- trustRoot=platformTrust(),
- privateKeyFile=privateKey,
- chainedCertFile=chainedCert,
- )
- # No data was received.
- self.assertEqual(cWrapped.data, b"")
-
- # It was an L{SSL.Error}.
- self.assertEqual(cWrapped.lostReason.type, SSL.Error)
-
- # Some combination of OpenSSL and PyOpenSSL is bad at reporting errors.
- err = cWrapped.lostReason.value
- self.assertEqual(err.args[0][0][2], "tlsv1 alert unknown ca")
-
- def test_trustRootSpecificCertificate(self):
- """
- Specifying a L{Certificate} object for L{trustRoot} will result in that
- certificate being the only trust root for a client.
- """
- caCert, serverCert = certificatesForAuthorityAndServer()
- otherCa, otherServer = certificatesForAuthorityAndServer()
- sProto, cProto, sWrapped, cWrapped, pump = loopbackTLSConnection(
- trustRoot=caCert,
- privateKeyFile=pathContainingDumpOf(self, serverCert.privateKey),
- chainedCertFile=pathContainingDumpOf(self, serverCert),
- )
- pump.flush()
- self.assertIsNone(cWrapped.lostReason)
- self.assertEqual(cWrapped.data, sWrapped.greeting)
-
-
- class ServiceIdentityTests(SynchronousTestCase):
- """
- Tests for the verification of the peer's service's identity via the
- C{hostname} argument to L{sslverify.OpenSSLCertificateOptions}.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def serviceIdentitySetup(
- self,
- clientHostname,
- serverHostname,
- serverContextSetup=lambda ctx: None,
- validCertificate=True,
- clientPresentsCertificate=False,
- validClientCertificate=True,
- serverVerifies=False,
- buggyInfoCallback=False,
- fakePlatformTrust=False,
- useDefaultTrust=False,
- ):
- """
- Connect a server and a client.
-
- @param clientHostname: The I{client's idea} of the server's hostname;
- passed as the C{hostname} to the
- L{sslverify.OpenSSLCertificateOptions} instance.
- @type clientHostname: L{unicode}
-
- @param serverHostname: The I{server's own idea} of the server's
- hostname; present in the certificate presented by the server.
- @type serverHostname: L{unicode}
-
- @param serverContextSetup: a 1-argument callable invoked with the
- L{OpenSSL.SSL.Context} after it's produced.
- @type serverContextSetup: L{callable} taking L{OpenSSL.SSL.Context}
- returning L{None}.
-
- @param validCertificate: Is the server's certificate valid? L{True} if
- so, L{False} otherwise.
- @type validCertificate: L{bool}
-
- @param clientPresentsCertificate: Should the client present a
- certificate to the server? Defaults to 'no'.
- @type clientPresentsCertificate: L{bool}
-
- @param validClientCertificate: If the client presents a certificate,
- should it actually be a valid one, i.e. signed by the same CA that
- the server is checking? Defaults to 'yes'.
- @type validClientCertificate: L{bool}
-
- @param serverVerifies: Should the server verify the client's
- certificate? Defaults to 'no'.
- @type serverVerifies: L{bool}
-
- @param buggyInfoCallback: Should we patch the implementation so that
- the C{info_callback} passed to OpenSSL to have a bug and raise an
- exception (L{ZeroDivisionError})? Defaults to 'no'.
- @type buggyInfoCallback: L{bool}
-
- @param fakePlatformTrust: Should we fake the platformTrust to be the
- same as our fake server certificate authority, so that we can test
- it's being used? Defaults to 'no' and we just pass platform trust.
- @type fakePlatformTrust: L{bool}
-
- @param useDefaultTrust: Should we avoid passing the C{trustRoot} to
- L{ssl.optionsForClientTLS}? Defaults to 'no'.
- @type useDefaultTrust: L{bool}
-
- @return: the client TLS protocol, the client wrapped protocol,
- the server TLS protocol, the server wrapped protocol and
- an L{IOPump} which, when its C{pump} and C{flush} methods are
- called, will move data between the created client and server
- protocol instances
- @rtype: 5-L{tuple} of 4 L{IProtocol}s and L{IOPump}
- """
- serverCA, serverCert = certificatesForAuthorityAndServer(serverHostname)
- other = {}
- passClientCert = None
- clientCA, clientCert = certificatesForAuthorityAndServer("client")
- if serverVerifies:
- other.update(trustRoot=clientCA)
-
- if clientPresentsCertificate:
- if validClientCertificate:
- passClientCert = clientCert
- else:
- bogusCA, bogus = certificatesForAuthorityAndServer("client")
- passClientCert = bogus
-
- serverOpts = sslverify.OpenSSLCertificateOptions(
- privateKey=serverCert.privateKey.original,
- certificate=serverCert.original,
- **other,
- )
- serverContextSetup(serverOpts.getContext())
- if not validCertificate:
- serverCA, otherServer = certificatesForAuthorityAndServer(serverHostname)
- if buggyInfoCallback:
-
- def broken(*a, **k):
- """
- Raise an exception.
-
- @param a: Arguments for an C{info_callback}
-
- @param k: Keyword arguments for an C{info_callback}
- """
- 1 / 0
-
- self.patch(
- sslverify.ClientTLSOptions,
- "_identityVerifyingInfoCallback",
- broken,
- )
-
- signature = {"hostname": clientHostname}
- if passClientCert:
- signature.update(clientCertificate=passClientCert)
- if not useDefaultTrust:
- signature.update(trustRoot=serverCA)
- if fakePlatformTrust:
- self.patch(sslverify, "platformTrust", lambda: serverCA)
-
- clientOpts = sslverify.optionsForClientTLS(**signature)
-
- class GreetingServer(protocol.Protocol):
- greeting = b"greetings!"
- lostReason = None
- data = b""
-
- def connectionMade(self):
- self.transport.write(self.greeting)
-
- def dataReceived(self, data):
- self.data += data
-
- def connectionLost(self, reason):
- self.lostReason = reason
-
- class GreetingClient(protocol.Protocol):
- greeting = b"cheerio!"
- data = b""
- lostReason = None
-
- def connectionMade(self):
- self.transport.write(self.greeting)
-
- def dataReceived(self, data):
- self.data += data
-
- def connectionLost(self, reason):
- self.lostReason = reason
-
- serverWrappedProto = GreetingServer()
- clientWrappedProto = GreetingClient()
-
- clientFactory = protocol.Factory()
- clientFactory.protocol = lambda: clientWrappedProto
- serverFactory = protocol.Factory()
- serverFactory.protocol = lambda: serverWrappedProto
-
- self.serverOpts = serverOpts
- self.clientOpts = clientOpts
-
- clientTLSFactory = TLSMemoryBIOFactory(
- clientOpts, isClient=True, wrappedFactory=clientFactory
- )
- serverTLSFactory = TLSMemoryBIOFactory(
- serverOpts, isClient=False, wrappedFactory=serverFactory
- )
-
- cProto, sProto, pump = connectedServerAndClient(
- lambda: serverTLSFactory.buildProtocol(None),
- lambda: clientTLSFactory.buildProtocol(None),
- )
- return cProto, sProto, clientWrappedProto, serverWrappedProto, pump
-
- def test_invalidHostname(self):
- """
- When a certificate containing an invalid hostname is received from the
- server, the connection is immediately dropped.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "wrong-host.example.com",
- "correct-host.example.com",
- )
- self.assertEqual(cWrapped.data, b"")
- self.assertEqual(sWrapped.data, b"")
-
- cErr = cWrapped.lostReason.value
- sErr = sWrapped.lostReason.value
-
- self.assertIsInstance(cErr, VerificationError)
- self.assertIsInstance(sErr, ConnectionClosed)
-
- def test_validHostname(self):
- """
- Whenever a valid certificate containing a valid hostname is received,
- connection proceeds normally.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com",
- "valid.example.com",
- )
- self.assertEqual(cWrapped.data, b"greetings!")
-
- cErr = cWrapped.lostReason
- sErr = sWrapped.lostReason
- self.assertIsNone(cErr)
- self.assertIsNone(sErr)
-
- def test_validHostnameInvalidCertificate(self):
- """
- When an invalid certificate containing a perfectly valid hostname is
- received, the connection is aborted with an OpenSSL error.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com",
- "valid.example.com",
- validCertificate=False,
- )
-
- self.assertEqual(cWrapped.data, b"")
- self.assertEqual(sWrapped.data, b"")
-
- cErr = cWrapped.lostReason.value
- sErr = sWrapped.lostReason.value
-
- self.assertIsInstance(cErr, SSL.Error)
- self.assertIsInstance(sErr, SSL.Error)
-
- def test_realCAsBetterNotSignOurBogusTestCerts(self):
- """
- If we use the default trust from the platform, our dinky certificate
- should I{really} fail.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com",
- "valid.example.com",
- validCertificate=False,
- useDefaultTrust=True,
- )
-
- self.assertEqual(cWrapped.data, b"")
- self.assertEqual(sWrapped.data, b"")
-
- cErr = cWrapped.lostReason.value
- sErr = sWrapped.lostReason.value
-
- self.assertIsInstance(cErr, SSL.Error)
- self.assertIsInstance(sErr, SSL.Error)
-
- def test_butIfTheyDidItWouldWork(self):
- """
- L{ssl.optionsForClientTLS} should be using L{ssl.platformTrust} by
- default, so if we fake that out then it should trust ourselves again.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com",
- "valid.example.com",
- useDefaultTrust=True,
- fakePlatformTrust=True,
- )
- self.assertEqual(cWrapped.data, b"greetings!")
-
- cErr = cWrapped.lostReason
- sErr = sWrapped.lostReason
- self.assertIsNone(cErr)
- self.assertIsNone(sErr)
-
- def test_clientPresentsCertificate(self):
- """
- When the server verifies and the client presents a valid certificate
- for that verification by passing it to
- L{sslverify.optionsForClientTLS}, communication proceeds.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com",
- "valid.example.com",
- validCertificate=True,
- serverVerifies=True,
- clientPresentsCertificate=True,
- )
-
- self.assertEqual(cWrapped.data, b"greetings!")
-
- cErr = cWrapped.lostReason
- sErr = sWrapped.lostReason
- self.assertIsNone(cErr)
- self.assertIsNone(sErr)
-
- def test_clientPresentsBadCertificate(self):
- """
- When the server verifies and the client presents an invalid certificate
- for that verification by passing it to
- L{sslverify.optionsForClientTLS}, the connection cannot be established
- with an SSL error.
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com",
- "valid.example.com",
- validCertificate=True,
- serverVerifies=True,
- validClientCertificate=False,
- clientPresentsCertificate=True,
- )
-
- self.assertEqual(cWrapped.data, b"")
-
- cErr = cWrapped.lostReason.value
- sErr = sWrapped.lostReason.value
-
- self.assertIsInstance(cErr, SSL.Error)
- self.assertIsInstance(sErr, SSL.Error)
-
- @skipIf(skipSNI, skipSNI)
- def test_hostnameIsIndicated(self):
- """
- Specifying the C{hostname} argument to L{CertificateOptions} also sets
- the U{Server Name Extension
- <https://en.wikipedia.org/wiki/Server_Name_Indication>} TLS indication
- field to the correct value.
- """
- names = []
-
- def setupServerContext(ctx):
- def servername_received(conn):
- names.append(conn.get_servername().decode("ascii"))
-
- ctx.set_tlsext_servername_callback(servername_received)
-
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "valid.example.com", "valid.example.com", setupServerContext
- )
- self.assertEqual(names, ["valid.example.com"])
-
- @skipIf(skipSNI, skipSNI)
- def test_hostnameEncoding(self):
- """
- Hostnames are encoded as IDNA.
- """
- names = []
- hello = "h\N{LATIN SMALL LETTER A WITH ACUTE}llo.example.com"
-
- def setupServerContext(ctx):
- def servername_received(conn):
- serverIDNA = _idnaText(conn.get_servername())
- names.append(serverIDNA)
-
- ctx.set_tlsext_servername_callback(servername_received)
-
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- hello, hello, setupServerContext
- )
- self.assertEqual(names, [hello])
- self.assertEqual(cWrapped.data, b"greetings!")
-
- cErr = cWrapped.lostReason
- sErr = sWrapped.lostReason
- self.assertIsNone(cErr)
- self.assertIsNone(sErr)
-
- def test_fallback(self):
- """
- L{sslverify.simpleVerifyHostname} checks string equality on the
- commonName of a connection's certificate's subject, doing nothing if it
- matches and raising L{VerificationError} if it doesn't.
- """
- name = "something.example.com"
-
- class Connection:
- def get_peer_certificate(self):
- """
- Fake of L{OpenSSL.SSL.Connection.get_peer_certificate}.
-
- @return: A certificate with a known common name.
- @rtype: L{OpenSSL.crypto.X509}
- """
- cert = X509()
- cert.get_subject().commonName = name
- return cert
-
- conn = Connection()
- self.assertIs(
- sslverify.simpleVerifyHostname(conn, "something.example.com"), None
- )
- self.assertRaises(
- sslverify.SimpleVerificationError,
- sslverify.simpleVerifyHostname,
- conn,
- "nonsense",
- )
-
- def test_surpriseFromInfoCallback(self):
- """
- pyOpenSSL isn't always so great about reporting errors. If one occurs
- in the verification info callback, it should be logged and the
- connection should be shut down (if possible, anyway; the app_data could
- be clobbered but there's no point testing for that).
- """
- cProto, sProto, cWrapped, sWrapped, pump = self.serviceIdentitySetup(
- "correct-host.example.com",
- "correct-host.example.com",
- buggyInfoCallback=True,
- )
-
- self.assertEqual(cWrapped.data, b"")
- self.assertEqual(sWrapped.data, b"")
-
- cErr = cWrapped.lostReason.value
- sErr = sWrapped.lostReason.value
-
- self.assertIsInstance(cErr, ZeroDivisionError)
- self.assertIsInstance(sErr, (ConnectionClosed, SSL.Error))
- errors = self.flushLoggedErrors(ZeroDivisionError)
- self.assertTrue(errors)
-
-
- def negotiateProtocol(serverProtocols, clientProtocols, clientOptions=None):
- """
- Create the TLS connection and negotiate a next protocol.
-
- @param serverProtocols: The protocols the server is willing to negotiate.
- @param clientProtocols: The protocols the client is willing to negotiate.
- @param clientOptions: The type of C{OpenSSLCertificateOptions} class to
- use for the client. Defaults to C{OpenSSLCertificateOptions}.
- @return: A L{tuple} of the negotiated protocol and the reason the
- connection was lost.
- """
- caCertificate, serverCertificate = certificatesForAuthorityAndServer()
- trustRoot = sslverify.OpenSSLCertificateAuthorities(
- [
- caCertificate.original,
- ]
- )
-
- sProto, cProto, sWrapped, cWrapped, pump = loopbackTLSConnectionInMemory(
- trustRoot=trustRoot,
- privateKey=serverCertificate.privateKey.original,
- serverCertificate=serverCertificate.original,
- clientProtocols=clientProtocols,
- serverProtocols=serverProtocols,
- clientOptions=clientOptions,
- )
- pump.flush()
-
- return (cProto.negotiatedProtocol, cWrapped.lostReason)
-
-
- class NPNOrALPNTests(TestCase):
- """
- NPN and ALPN protocol selection.
-
- These tests only run on platforms that have a PyOpenSSL version >= 0.15,
- and OpenSSL version 1.0.1 or later.
- """
-
- if skipSSL:
- skip = skipSSL
- elif skipNPN:
- skip = skipNPN
-
- def test_nextProtocolMechanismsNPNIsSupported(self):
- """
- When at least NPN is available on the platform, NPN is in the set of
- supported negotiation protocols.
- """
- supportedProtocols = sslverify.protocolNegotiationMechanisms()
- self.assertTrue(sslverify.ProtocolNegotiationSupport.NPN in supportedProtocols)
-
- def test_NPNAndALPNSuccess(self):
- """
- When both ALPN and NPN are used, and both the client and server have
- overlapping protocol choices, a protocol is successfully negotiated.
- Further, the negotiated protocol is the first one in the list.
- """
- protocols = [b"h2", b"http/1.1"]
- negotiatedProtocol, lostReason = negotiateProtocol(
- clientProtocols=protocols,
- serverProtocols=protocols,
- )
- self.assertEqual(negotiatedProtocol, b"h2")
- self.assertIsNone(lostReason)
-
- def test_NPNAndALPNDifferent(self):
- """
- Client and server have different protocol lists: only the common
- element is chosen.
- """
- serverProtocols = [b"h2", b"http/1.1", b"spdy/2"]
- clientProtocols = [b"spdy/3", b"http/1.1"]
- negotiatedProtocol, lostReason = negotiateProtocol(
- clientProtocols=clientProtocols,
- serverProtocols=serverProtocols,
- )
- self.assertEqual(negotiatedProtocol, b"http/1.1")
- self.assertIsNone(lostReason)
-
- def test_NPNAndALPNNoAdvertise(self):
- """
- When one peer does not advertise any protocols, the connection is set
- up with no next protocol.
- """
- protocols = [b"h2", b"http/1.1"]
- negotiatedProtocol, lostReason = negotiateProtocol(
- clientProtocols=protocols,
- serverProtocols=[],
- )
- self.assertIsNone(negotiatedProtocol)
- self.assertIsNone(lostReason)
-
- def test_NPNAndALPNNoOverlap(self):
- """
- When the client and server have no overlap of protocols, the connection
- fails.
- """
- clientProtocols = [b"h2", b"http/1.1"]
- serverProtocols = [b"spdy/3"]
- negotiatedProtocol, lostReason = negotiateProtocol(
- serverProtocols=clientProtocols,
- clientProtocols=serverProtocols,
- )
- self.assertIsNone(negotiatedProtocol)
- self.assertEqual(lostReason.type, SSL.Error)
-
-
- class ALPNTests(TestCase):
- """
- ALPN protocol selection.
-
- These tests only run on platforms that have a PyOpenSSL version >= 0.15,
- and OpenSSL version 1.0.2 or later.
-
- This covers only the ALPN specific logic, as any platform that has ALPN
- will also have NPN and so will run the NPNAndALPNTest suite as well.
- """
-
- if skipSSL:
- skip = skipSSL
- elif skipALPN:
- skip = skipALPN
-
- def test_nextProtocolMechanismsALPNIsSupported(self):
- """
- When ALPN is available on a platform, protocolNegotiationMechanisms
- includes ALPN in the suported protocols.
- """
- supportedProtocols = sslverify.protocolNegotiationMechanisms()
- self.assertTrue(sslverify.ProtocolNegotiationSupport.ALPN in supportedProtocols)
-
-
- class NPNAndALPNAbsentTests(TestCase):
- """
- NPN/ALPN operations fail on platforms that do not support them.
-
- These tests only run on platforms that have a PyOpenSSL version < 0.15,
- an OpenSSL version earlier than 1.0.1, or an OpenSSL/cryptography built
- without NPN support.
- """
-
- if skipSSL:
- skip = skipSSL
- elif not skipNPN or not skipALPN:
- skip = "NPN and/or ALPN is present on this platform"
-
- def test_nextProtocolMechanismsNoNegotiationSupported(self):
- """
- When neither NPN or ALPN are available on a platform, there are no
- supported negotiation protocols.
- """
- supportedProtocols = sslverify.protocolNegotiationMechanisms()
- self.assertFalse(supportedProtocols)
-
- def test_NPNAndALPNNotImplemented(self):
- """
- A NotImplementedError is raised when using acceptableProtocols on a
- platform that does not support either NPN or ALPN.
- """
- protocols = [b"h2", b"http/1.1"]
- self.assertRaises(
- NotImplementedError,
- negotiateProtocol,
- serverProtocols=protocols,
- clientProtocols=protocols,
- )
-
- def test_NegotiatedProtocolReturnsNone(self):
- """
- negotiatedProtocol return L{None} even when NPN/ALPN aren't supported.
- This works because, as neither are supported, negotiation isn't even
- attempted.
- """
- serverProtocols = None
- clientProtocols = None
- negotiatedProtocol, lostReason = negotiateProtocol(
- clientProtocols=clientProtocols,
- serverProtocols=serverProtocols,
- )
- self.assertIsNone(negotiatedProtocol)
- self.assertIsNone(lostReason)
-
-
- class _NotSSLTransport:
- def getHandle(self):
- return self
-
-
- class _MaybeSSLTransport:
- def getHandle(self):
- return self
-
- def get_peer_certificate(self):
- return None
-
- def get_host_certificate(self):
- return None
-
-
- class _ActualSSLTransport:
- def getHandle(self):
- return self
-
- def get_host_certificate(self):
- return sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM).original
-
- def get_peer_certificate(self):
- return sslverify.Certificate.loadPEM(A_PEER_CERTIFICATE_PEM).original
-
-
- class ConstructorsTests(TestCase):
- if skipSSL:
- skip = skipSSL
-
- def test_peerFromNonSSLTransport(self):
- """
- Verify that peerFromTransport raises an exception if the transport
- passed is not actually an SSL transport.
- """
- x = self.assertRaises(
- CertificateError,
- sslverify.Certificate.peerFromTransport,
- _NotSSLTransport(),
- )
- self.assertTrue(str(x).startswith("non-TLS"))
-
- def test_peerFromBlankSSLTransport(self):
- """
- Verify that peerFromTransport raises an exception if the transport
- passed is an SSL transport, but doesn't have a peer certificate.
- """
- x = self.assertRaises(
- CertificateError,
- sslverify.Certificate.peerFromTransport,
- _MaybeSSLTransport(),
- )
- self.assertTrue(str(x).startswith("TLS"))
-
- def test_hostFromNonSSLTransport(self):
- """
- Verify that hostFromTransport raises an exception if the transport
- passed is not actually an SSL transport.
- """
- x = self.assertRaises(
- CertificateError,
- sslverify.Certificate.hostFromTransport,
- _NotSSLTransport(),
- )
- self.assertTrue(str(x).startswith("non-TLS"))
-
- def test_hostFromBlankSSLTransport(self):
- """
- Verify that hostFromTransport raises an exception if the transport
- passed is an SSL transport, but doesn't have a host certificate.
- """
- x = self.assertRaises(
- CertificateError,
- sslverify.Certificate.hostFromTransport,
- _MaybeSSLTransport(),
- )
- self.assertTrue(str(x).startswith("TLS"))
-
- def test_hostFromSSLTransport(self):
- """
- Verify that hostFromTransport successfully creates the correct
- certificate if passed a valid SSL transport.
- """
- self.assertEqual(
- sslverify.Certificate.hostFromTransport(
- _ActualSSLTransport()
- ).serialNumber(),
- 12345,
- )
-
- def test_peerFromSSLTransport(self):
- """
- Verify that peerFromTransport successfully creates the correct
- certificate if passed a valid SSL transport.
- """
- self.assertEqual(
- sslverify.Certificate.peerFromTransport(
- _ActualSSLTransport()
- ).serialNumber(),
- 12346,
- )
-
-
- class MultipleCertificateTrustRootTests(TestCase):
- """
- Test the behavior of the trustRootFromCertificates() API call.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def test_trustRootFromCertificatesPrivatePublic(self):
- """
- L{trustRootFromCertificates} accepts either a L{sslverify.Certificate}
- or a L{sslverify.PrivateCertificate} instance.
- """
- privateCert = sslverify.PrivateCertificate.loadPEM(A_KEYPAIR)
- cert = sslverify.Certificate.loadPEM(A_HOST_CERTIFICATE_PEM)
-
- mt = sslverify.trustRootFromCertificates([privateCert, cert])
-
- # Verify that the returned object acts correctly when used as a
- # trustRoot= param to optionsForClientTLS.
- sProto, cProto, sWrap, cWrap, pump = loopbackTLSConnectionInMemory(
- trustRoot=mt,
- privateKey=privateCert.privateKey.original,
- serverCertificate=privateCert.original,
- )
-
- # This connection should succeed
- self.assertEqual(cWrap.data, b"greetings!")
- self.assertIsNone(cWrap.lostReason)
-
- def test_trustRootSelfSignedServerCertificate(self):
- """
- L{trustRootFromCertificates} called with a single self-signed
- certificate will cause L{optionsForClientTLS} to accept client
- connections to a server with that certificate.
- """
- key, cert = makeCertificate(O=b"Server Test Certificate", CN=b"server")
- selfSigned = sslverify.PrivateCertificate.fromCertificateAndKeyPair(
- sslverify.Certificate(cert),
- sslverify.KeyPair(key),
- )
-
- trust = sslverify.trustRootFromCertificates([selfSigned])
-
- # Since we trust this exact certificate, connections to this server
- # should succeed.
- sProto, cProto, sWrap, cWrap, pump = loopbackTLSConnectionInMemory(
- trustRoot=trust,
- privateKey=selfSigned.privateKey.original,
- serverCertificate=selfSigned.original,
- )
- self.assertEqual(cWrap.data, b"greetings!")
- self.assertIsNone(cWrap.lostReason)
-
- def test_trustRootCertificateAuthorityTrustsConnection(self):
- """
- L{trustRootFromCertificates} called with certificate A will cause
- L{optionsForClientTLS} to accept client connections to a server with
- certificate B where B is signed by A.
- """
- caCert, serverCert = certificatesForAuthorityAndServer()
-
- trust = sslverify.trustRootFromCertificates([caCert])
-
- # Since we've listed the CA's certificate as a trusted cert, a
- # connection to the server certificate it signed should succeed.
- sProto, cProto, sWrap, cWrap, pump = loopbackTLSConnectionInMemory(
- trustRoot=trust,
- privateKey=serverCert.privateKey.original,
- serverCertificate=serverCert.original,
- )
- self.assertEqual(cWrap.data, b"greetings!")
- self.assertIsNone(cWrap.lostReason)
-
- def test_trustRootFromCertificatesUntrusted(self):
- """
- L{trustRootFromCertificates} called with certificate A will cause
- L{optionsForClientTLS} to disallow any connections to a server with
- certificate B where B is not signed by A.
- """
- key, cert = makeCertificate(O=b"Server Test Certificate", CN=b"server")
- serverCert = sslverify.PrivateCertificate.fromCertificateAndKeyPair(
- sslverify.Certificate(cert),
- sslverify.KeyPair(key),
- )
- untrustedCert = sslverify.Certificate(
- makeCertificate(O=b"CA Test Certificate", CN=b"unknown CA")[1]
- )
-
- trust = sslverify.trustRootFromCertificates([untrustedCert])
-
- # Since we only trust 'untrustedCert' which has not signed our
- # server's cert, we should reject this connection
- sProto, cProto, sWrap, cWrap, pump = loopbackTLSConnectionInMemory(
- trustRoot=trust,
- privateKey=serverCert.privateKey.original,
- serverCertificate=serverCert.original,
- )
-
- # This connection should fail, so no data was received.
- self.assertEqual(cWrap.data, b"")
-
- # It was an L{SSL.Error}.
- self.assertEqual(cWrap.lostReason.type, SSL.Error)
-
- # Some combination of OpenSSL and PyOpenSSL is bad at reporting errors.
- err = cWrap.lostReason.value
- self.assertEqual(err.args[0][0][2], "tlsv1 alert unknown ca")
-
- def test_trustRootFromCertificatesOpenSSLObjects(self):
- """
- L{trustRootFromCertificates} rejects any L{OpenSSL.crypto.X509}
- instances in the list passed to it.
- """
- private = sslverify.PrivateCertificate.loadPEM(A_KEYPAIR)
- certX509 = private.original
-
- exception = self.assertRaises(
- TypeError,
- sslverify.trustRootFromCertificates,
- [certX509],
- )
- self.assertEqual(
- "certificates items must be twisted.internet.ssl.CertBase " "instances",
- exception.args[0],
- )
-
-
- class OpenSSLCipherTests(TestCase):
- """
- Tests for twisted.internet._sslverify.OpenSSLCipher.
- """
-
- if skipSSL:
- skip = skipSSL
-
- cipherName = "CIPHER-STRING"
-
- def test_constructorSetsFullName(self):
- """
- The first argument passed to the constructor becomes the full name.
- """
- self.assertEqual(
- self.cipherName, sslverify.OpenSSLCipher(self.cipherName).fullName
- )
-
- def test_repr(self):
- """
- C{repr(cipher)} returns a valid constructor call.
- """
- cipher = sslverify.OpenSSLCipher(self.cipherName)
- self.assertEqual(
- cipher, eval(repr(cipher), {"OpenSSLCipher": sslverify.OpenSSLCipher})
- )
-
- def test_eqSameClass(self):
- """
- Equal type and C{fullName} means that the objects are equal.
- """
- cipher1 = sslverify.OpenSSLCipher(self.cipherName)
- cipher2 = sslverify.OpenSSLCipher(self.cipherName)
- self.assertEqual(cipher1, cipher2)
-
- def test_eqSameNameDifferentType(self):
- """
- If ciphers have the same name but different types, they're still
- different.
- """
-
- class DifferentCipher:
- fullName = self.cipherName
-
- self.assertNotEqual(
- sslverify.OpenSSLCipher(self.cipherName),
- DifferentCipher(),
- )
-
-
- class ExpandCipherStringTests(TestCase):
- """
- Tests for twisted.internet._sslverify._expandCipherString.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def test_doesNotStumbleOverEmptyList(self):
- """
- If the expanded cipher list is empty, an empty L{list} is returned.
- """
- self.assertEqual(
- tuple(), sslverify._expandCipherString("", SSL.SSLv23_METHOD, 0)
- )
-
- def test_doesNotSwallowOtherSSLErrors(self):
- """
- Only no cipher matches get swallowed, every other SSL error gets
- propagated.
- """
-
- def raiser(_):
- # Unfortunately, there seems to be no way to trigger a real SSL
- # error artificially.
- raise SSL.Error([["", "", ""]])
-
- ctx = FakeContext(SSL.SSLv23_METHOD)
- ctx.set_cipher_list = raiser
- self.patch(sslverify.SSL, "Context", lambda _: ctx)
- self.assertRaises(
- SSL.Error, sslverify._expandCipherString, "ALL", SSL.SSLv23_METHOD, 0
- )
-
- def test_returnsTupleOfICiphers(self):
- """
- L{sslverify._expandCipherString} always returns a L{tuple} of
- L{interfaces.ICipher}.
- """
- ciphers = sslverify._expandCipherString("ALL", SSL.SSLv23_METHOD, 0)
- self.assertIsInstance(ciphers, tuple)
- bogus = []
- for c in ciphers:
- if not interfaces.ICipher.providedBy(c):
- bogus.append(c)
-
- self.assertEqual([], bogus)
-
-
- class AcceptableCiphersTests(TestCase):
- """
- Tests for twisted.internet._sslverify.OpenSSLAcceptableCiphers.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def test_selectOnEmptyListReturnsEmptyList(self):
- """
- If no ciphers are available, nothing can be selected.
- """
- ac = sslverify.OpenSSLAcceptableCiphers(tuple())
- self.assertEqual(tuple(), ac.selectCiphers(tuple()))
-
- def test_selectReturnsOnlyFromAvailable(self):
- """
- Select only returns a cross section of what is available and what is
- desirable.
- """
- ac = sslverify.OpenSSLAcceptableCiphers(
- [
- sslverify.OpenSSLCipher("A"),
- sslverify.OpenSSLCipher("B"),
- ]
- )
- self.assertEqual(
- (sslverify.OpenSSLCipher("B"),),
- ac.selectCiphers(
- [sslverify.OpenSSLCipher("B"), sslverify.OpenSSLCipher("C")]
- ),
- )
-
- def test_fromOpenSSLCipherStringExpandsToTupleOfCiphers(self):
- """
- If L{sslverify.OpenSSLAcceptableCiphers.fromOpenSSLCipherString} is
- called it expands the string to a tuple of ciphers.
- """
- ac = sslverify.OpenSSLAcceptableCiphers.fromOpenSSLCipherString("ALL")
- self.assertIsInstance(ac._ciphers, tuple)
- self.assertTrue(all(sslverify.ICipher.providedBy(c) for c in ac._ciphers))
-
-
- class DiffieHellmanParametersTests(TestCase):
- """
- Tests for twisted.internet._sslverify.OpenSSLDHParameters.
- """
-
- if skipSSL:
- skip = skipSSL
- filePath = FilePath(b"dh.params")
-
- def test_fromFile(self):
- """
- Calling C{fromFile} with a filename returns an instance with that file
- name saved.
- """
- params = sslverify.OpenSSLDiffieHellmanParameters.fromFile(self.filePath)
- self.assertEqual(self.filePath, params._dhFile)
-
-
- class FakeLibState:
- """
- State for L{FakeLib}
-
- @param setECDHAutoRaises: An exception
- L{FakeLib.SSL_CTX_set_ecdh_auto} should raise; if L{None},
- nothing is raised.
-
- @ivar ecdhContexts: A list of SSL contexts with which
- L{FakeLib.SSL_CTX_set_ecdh_auto} was called
- @type ecdhContexts: L{list} of L{OpenSSL.SSL.Context}s
-
- @ivar ecdhValues: A list of boolean values with which
- L{FakeLib.SSL_CTX_set_ecdh_auto} was called
- @type ecdhValues: L{list} of L{boolean}s
- """
-
- __slots__ = ("setECDHAutoRaises", "ecdhContexts", "ecdhValues")
-
- def __init__(self, setECDHAutoRaises):
- self.setECDHAutoRaises = setECDHAutoRaises
- self.ecdhContexts = []
- self.ecdhValues = []
-
-
- class FakeLib:
- """
- An introspectable fake of cryptography's lib object.
-
- @param state: A L{FakeLibState} instance that contains this fake's
- state.
- """
-
- def __init__(self, state):
- self._state = state
-
- def SSL_CTX_set_ecdh_auto(self, ctx, value):
- """
- Record the context and value under in the C{_state} instance
- variable.
-
- @see: L{FakeLibState}
-
- @param ctx: An SSL context.
- @type ctx: L{OpenSSL.SSL.Context}
-
- @param value: A boolean value
- @type value: L{bool}
- """
- self._state.ecdhContexts.append(ctx)
- self._state.ecdhValues.append(value)
- if self._state.setECDHAutoRaises is not None:
- raise self._state.setECDHAutoRaises
-
-
- class FakeLibTests(TestCase):
- """
- Tests for L{FakeLib}.
- """
-
- def test_SSL_CTX_set_ecdh_auto(self):
- """
- L{FakeLib.SSL_CTX_set_ecdh_auto} records context and value it
- was called with.
- """
- state = FakeLibState(setECDHAutoRaises=None)
- lib = FakeLib(state)
- self.assertNot(state.ecdhContexts)
- self.assertNot(state.ecdhValues)
-
- context, value = "CONTEXT", True
- lib.SSL_CTX_set_ecdh_auto(context, value)
- self.assertEqual(state.ecdhContexts, [context])
- self.assertEqual(state.ecdhValues, [True])
-
- def test_SSL_CTX_set_ecdh_autoRaises(self):
- """
- L{FakeLib.SSL_CTX_set_ecdh_auto} raises the exception provided
- by its state, while still recording its arguments.
- """
- state = FakeLibState(setECDHAutoRaises=ValueError)
- lib = FakeLib(state)
- self.assertNot(state.ecdhContexts)
- self.assertNot(state.ecdhValues)
-
- context, value = "CONTEXT", True
- self.assertRaises(ValueError, lib.SSL_CTX_set_ecdh_auto, context, value)
- self.assertEqual(state.ecdhContexts, [context])
- self.assertEqual(state.ecdhValues, [True])
-
-
- class FakeCryptoState:
- """
- State for L{FakeCrypto}
-
- @param getEllipticCurveRaises: What
- L{FakeCrypto.get_elliptic_curve} should raise; L{None} and it
- won't raise anything
-
- @param getEllipticCurveReturns: What
- L{FakeCrypto.get_elliptic_curve} should return.
-
- @ivar getEllipticCurveCalls: The arguments with which
- L{FakeCrypto.get_elliptic_curve} has been called.
- @type getEllipticCurveCalls: L{list}
- """
-
- __slots__ = (
- "getEllipticCurveRaises",
- "getEllipticCurveReturns",
- "getEllipticCurveCalls",
- )
-
- def __init__(
- self,
- getEllipticCurveRaises,
- getEllipticCurveReturns,
- ):
- self.getEllipticCurveRaises = getEllipticCurveRaises
- self.getEllipticCurveReturns = getEllipticCurveReturns
- self.getEllipticCurveCalls = []
-
-
- class FakeCrypto:
- """
- An introspectable fake of pyOpenSSL's L{OpenSSL.crypto} module.
-
- @ivar state: A L{FakeCryptoState} instance
- """
-
- def __init__(self, state):
- self._state = state
-
- def get_elliptic_curve(self, curve):
- """
- A fake that records the curve with which it was called.
-
- @param curve: see L{crypto.get_elliptic_curve}
-
- @return: see L{FakeCryptoState.getEllipticCurveReturns}
- @raises: see L{FakeCryptoState.getEllipticCurveRaises}
- """
- self._state.getEllipticCurveCalls.append(curve)
- if self._state.getEllipticCurveRaises is not None:
- raise self._state.getEllipticCurveRaises
- return self._state.getEllipticCurveReturns
-
-
- class FakeCryptoTests(SynchronousTestCase):
- """
- Tests for L{FakeCrypto}.
- """
-
- def test_get_elliptic_curveRecordsArgument(self):
- """
- L{FakeCrypto.test_get_elliptic_curve} records the curve with
- which it was called.
- """
- state = FakeCryptoState(
- getEllipticCurveRaises=None,
- getEllipticCurveReturns=None,
- )
- crypto = FakeCrypto(state)
- crypto.get_elliptic_curve("a curve name")
- self.assertEqual(state.getEllipticCurveCalls, ["a curve name"])
-
- def test_get_elliptic_curveReturns(self):
- """
- L{FakeCrypto.test_get_elliptic_curve} returns the value
- specified by its state object and records what it was called
- with.
- """
- returnValue = "object"
- state = FakeCryptoState(
- getEllipticCurveRaises=None,
- getEllipticCurveReturns=returnValue,
- )
- crypto = FakeCrypto(state)
- self.assertIs(
- crypto.get_elliptic_curve("another curve name"),
- returnValue,
- )
- self.assertEqual(state.getEllipticCurveCalls, ["another curve name"])
-
- def test_get_elliptic_curveRaises(self):
- """
- L{FakeCrypto.test_get_elliptic_curve} raises the exception
- specified by its state object.
- """
- state = FakeCryptoState(
- getEllipticCurveRaises=ValueError, getEllipticCurveReturns=None
- )
- crypto = FakeCrypto(state)
- self.assertRaises(
- ValueError,
- crypto.get_elliptic_curve,
- "yet another curve name",
- )
- self.assertEqual(
- state.getEllipticCurveCalls,
- ["yet another curve name"],
- )
-
-
- class ChooseDiffieHellmanEllipticCurveTests(SynchronousTestCase):
- """
- Tests for L{sslverify._ChooseDiffieHellmanEllipticCurve}.
-
- @cvar OPENSSL_110: A version number for OpenSSL 1.1.0
-
- @cvar OPENSSL_102: A version number for OpenSSL 1.0.2
-
- @cvar OPENSSL_101: A version number for OpenSSL 1.0.1
-
- @see:
- U{https://wiki.openssl.org/index.php/Manual:OPENSSL_VERSION_NUMBER(3)}
- """
-
- if skipSSL:
- skip = skipSSL
-
- OPENSSL_110 = 0x1010007F
- OPENSSL_102 = 0x100020EF
- OPENSSL_101 = 0x1000114F
-
- def setUp(self):
- self.libState = FakeLibState(setECDHAutoRaises=False)
- self.lib = FakeLib(self.libState)
-
- self.cryptoState = FakeCryptoState(
- getEllipticCurveReturns=None, getEllipticCurveRaises=None
- )
- self.crypto = FakeCrypto(self.cryptoState)
- self.context = FakeContext(SSL.SSLv23_METHOD)
-
- def test_openSSL110(self):
- """
- No configuration of contexts occurs under OpenSSL 1.1.0 and
- later, because they create contexts with secure ECDH curves.
-
- @see: U{http://twistedmatrix.com/trac/ticket/9210}
- """
- chooser = sslverify._ChooseDiffieHellmanEllipticCurve(
- self.OPENSSL_110,
- openSSLlib=self.lib,
- openSSLcrypto=self.crypto,
- )
- chooser.configureECDHCurve(self.context)
-
- self.assertFalse(self.libState.ecdhContexts)
- self.assertFalse(self.libState.ecdhValues)
- self.assertFalse(self.cryptoState.getEllipticCurveCalls)
- self.assertIsNone(self.context._ecCurve)
-
- def test_openSSL102(self):
- """
- OpenSSL 1.0.2 does not set ECDH curves by default, but
- C{SSL_CTX_set_ecdh_auto} requests that a context choose a
- secure set curves automatically.
- """
- context = SSL.Context(SSL.SSLv23_METHOD)
- chooser = sslverify._ChooseDiffieHellmanEllipticCurve(
- self.OPENSSL_102,
- openSSLlib=self.lib,
- openSSLcrypto=self.crypto,
- )
- chooser.configureECDHCurve(context)
-
- self.assertEqual(self.libState.ecdhContexts, [context._context])
- self.assertEqual(self.libState.ecdhValues, [True])
- self.assertFalse(self.cryptoState.getEllipticCurveCalls)
- self.assertIsNone(self.context._ecCurve)
-
- def test_openSSL102SetECDHAutoRaises(self):
- """
- An exception raised by C{SSL_CTX_set_ecdh_auto} under OpenSSL
- 1.0.2 is suppressed because ECDH is best-effort.
- """
- self.libState.setECDHAutoRaises = BaseException
- context = SSL.Context(SSL.SSLv23_METHOD)
- chooser = sslverify._ChooseDiffieHellmanEllipticCurve(
- self.OPENSSL_102,
- openSSLlib=self.lib,
- openSSLcrypto=self.crypto,
- )
- chooser.configureECDHCurve(context)
-
- self.assertEqual(self.libState.ecdhContexts, [context._context])
- self.assertEqual(self.libState.ecdhValues, [True])
- self.assertFalse(self.cryptoState.getEllipticCurveCalls)
-
- def test_openSSL101(self):
- """
- OpenSSL 1.0.1 does not set ECDH curves by default, nor does
- it expose L{SSL_CTX_set_ecdh_auto}. Instead, a single ECDH
- curve can be set with L{OpenSSL.SSL.Context.set_tmp_ecdh}.
- """
- self.cryptoState.getEllipticCurveReturns = curve = "curve object"
- chooser = sslverify._ChooseDiffieHellmanEllipticCurve(
- self.OPENSSL_101,
- openSSLlib=self.lib,
- openSSLcrypto=self.crypto,
- )
- chooser.configureECDHCurve(self.context)
-
- self.assertFalse(self.libState.ecdhContexts)
- self.assertFalse(self.libState.ecdhValues)
- self.assertEqual(
- self.cryptoState.getEllipticCurveCalls,
- [sslverify._defaultCurveName],
- )
- self.assertIs(self.context._ecCurve, curve)
-
- def test_openSSL101SetECDHRaises(self):
- """
- An exception raised by L{OpenSSL.SSL.Context.set_tmp_ecdh}
- under OpenSSL 1.0.1 is suppressed because ECHDE is best-effort.
- """
-
- def set_tmp_ecdh(ctx):
- raise BaseException
-
- self.context.set_tmp_ecdh = set_tmp_ecdh
-
- chooser = sslverify._ChooseDiffieHellmanEllipticCurve(
- self.OPENSSL_101,
- openSSLlib=self.lib,
- openSSLcrypto=self.crypto,
- )
- chooser.configureECDHCurve(self.context)
-
- self.assertFalse(self.libState.ecdhContexts)
- self.assertFalse(self.libState.ecdhValues)
- self.assertEqual(
- self.cryptoState.getEllipticCurveCalls,
- [sslverify._defaultCurveName],
- )
-
- def test_openSSL101NoECC(self):
- """
- Contexts created under an OpenSSL 1.0.1 that doesn't support
- ECC have no configuration applied.
- """
- self.cryptoState.getEllipticCurveRaises = ValueError
- chooser = sslverify._ChooseDiffieHellmanEllipticCurve(
- self.OPENSSL_101,
- openSSLlib=self.lib,
- openSSLcrypto=self.crypto,
- )
- chooser.configureECDHCurve(self.context)
-
- self.assertFalse(self.libState.ecdhContexts)
- self.assertFalse(self.libState.ecdhValues)
- self.assertIsNone(self.context._ecCurve)
-
-
- class KeyPairTests(TestCase):
- """
- Tests for L{sslverify.KeyPair}.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def setUp(self):
- """
- Create test certificate.
- """
- self.sKey = makeCertificate(O=b"Server Test Certificate", CN=b"server")[0]
-
- def test_getstateDeprecation(self):
- """
- L{sslverify.KeyPair.__getstate__} is deprecated.
- """
- self.callDeprecated(
- (Version("Twisted", 15, 0, 0), "a real persistence system"),
- sslverify.KeyPair(self.sKey).__getstate__,
- )
-
- def test_setstateDeprecation(self):
- """
- {sslverify.KeyPair.__setstate__} is deprecated.
- """
- state = sslverify.KeyPair(self.sKey).dump()
- self.callDeprecated(
- (Version("Twisted", 15, 0, 0), "a real persistence system"),
- sslverify.KeyPair(self.sKey).__setstate__,
- state,
- )
-
- def test_noTrailingNewlinePemCert(self):
- noTrailingNewlineKeyPemPath = getModule("twisted.test").filePath.sibling(
- "cert.pem.no_trailing_newline"
- )
-
- certPEM = noTrailingNewlineKeyPemPath.getContent()
- ssl.Certificate.loadPEM(certPEM)
-
-
- class SelectVerifyImplementationTests(SynchronousTestCase):
- """
- Tests for L{_selectVerifyImplementation}.
- """
-
- if skipSSL:
- skip = skipSSL
-
- def test_dependencyMissing(self):
- """
- If I{service_identity} cannot be imported then
- L{_selectVerifyImplementation} returns L{simpleVerifyHostname} and
- L{SimpleVerificationError}.
- """
- with SetAsideModule("service_identity"):
- sys.modules["service_identity"] = None
-
- result = sslverify._selectVerifyImplementation()
- expected = (
- sslverify.simpleVerifyHostname,
- sslverify.simpleVerifyIPAddress,
- sslverify.SimpleVerificationError,
- )
- self.assertEqual(expected, result)
-
- test_dependencyMissing.suppress = [ # type: ignore[attr-defined]
- util.suppress(
- message=(
- "You do not have a working installation of the "
- "service_identity module"
- ),
- ),
- ]
-
- def test_dependencyMissingWarning(self):
- """
- If I{service_identity} cannot be imported then
- L{_selectVerifyImplementation} emits a L{UserWarning} advising the user
- of the exact error.
- """
- with SetAsideModule("service_identity"):
- sys.modules["service_identity"] = None
-
- sslverify._selectVerifyImplementation()
-
- [warning] = list(
- warning
- for warning in self.flushWarnings()
- if warning["category"] == UserWarning
- )
-
- expectedMessage = (
- "You do not have a working installation of the "
- "service_identity module: "
- "'import of service_identity halted; None in sys.modules'. "
- "Please install it from "
- "<https://pypi.python.org/pypi/service_identity> "
- "and make sure all of its dependencies are satisfied. "
- "Without the service_identity module, Twisted can perform only"
- " rudimentary TLS client hostname verification. Many valid "
- "certificate/hostname mappings may be rejected."
- )
-
- self.assertEqual(warning["message"], expectedMessage)
- # Make sure we're abusing the warning system to a sufficient
- # degree: there is no filename or line number that makes sense for
- # this warning to "blame" for the problem. It is a system
- # misconfiguration. So the location information should be blank
- # (or as blank as we can make it).
- self.assertEqual(warning["filename"], "")
- self.assertEqual(warning["lineno"], 0)
|