ssh.py 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621
  1. # This file is dual licensed under the terms of the Apache License, Version
  2. # 2.0, and the BSD License. See the LICENSE file in the root of this repository
  3. # for complete details.
  4. from __future__ import annotations
  5. import binascii
  6. import enum
  7. import os
  8. import re
  9. import typing
  10. import warnings
  11. from base64 import encodebytes as _base64_encode
  12. from dataclasses import dataclass
  13. from cryptography import utils
  14. from cryptography.exceptions import UnsupportedAlgorithm
  15. from cryptography.hazmat.primitives import hashes
  16. from cryptography.hazmat.primitives.asymmetric import (
  17. dsa,
  18. ec,
  19. ed25519,
  20. padding,
  21. rsa,
  22. )
  23. from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
  24. from cryptography.hazmat.primitives.ciphers import (
  25. AEADDecryptionContext,
  26. Cipher,
  27. algorithms,
  28. modes,
  29. )
  30. from cryptography.hazmat.primitives.serialization import (
  31. Encoding,
  32. KeySerializationEncryption,
  33. NoEncryption,
  34. PrivateFormat,
  35. PublicFormat,
  36. _KeySerializationEncryption,
  37. )
  38. try:
  39. from bcrypt import kdf as _bcrypt_kdf
  40. _bcrypt_supported = True
  41. except ImportError:
  42. _bcrypt_supported = False
  43. def _bcrypt_kdf(
  44. password: bytes,
  45. salt: bytes,
  46. desired_key_bytes: int,
  47. rounds: int,
  48. ignore_few_rounds: bool = False,
  49. ) -> bytes:
  50. raise UnsupportedAlgorithm("Need bcrypt module")
  51. _SSH_ED25519 = b"ssh-ed25519"
  52. _SSH_RSA = b"ssh-rsa"
  53. _SSH_DSA = b"ssh-dss"
  54. _ECDSA_NISTP256 = b"ecdsa-sha2-nistp256"
  55. _ECDSA_NISTP384 = b"ecdsa-sha2-nistp384"
  56. _ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
  57. _CERT_SUFFIX = b"-cert-v01@openssh.com"
  58. # U2F application string suffixed pubkey
  59. _SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com"
  60. _SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com"
  61. # These are not key types, only algorithms, so they cannot appear
  62. # as a public key type
  63. _SSH_RSA_SHA256 = b"rsa-sha2-256"
  64. _SSH_RSA_SHA512 = b"rsa-sha2-512"
  65. _SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)")
  66. _SK_MAGIC = b"openssh-key-v1\0"
  67. _SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
  68. _SK_END = b"-----END OPENSSH PRIVATE KEY-----"
  69. _BCRYPT = b"bcrypt"
  70. _NONE = b"none"
  71. _DEFAULT_CIPHER = b"aes256-ctr"
  72. _DEFAULT_ROUNDS = 16
  73. # re is only way to work on bytes-like data
  74. _PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL)
  75. # padding for max blocksize
  76. _PADDING = memoryview(bytearray(range(1, 1 + 16)))
  77. @dataclass
  78. class _SSHCipher:
  79. alg: type[algorithms.AES]
  80. key_len: int
  81. mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM]
  82. block_len: int
  83. iv_len: int
  84. tag_len: int | None
  85. is_aead: bool
  86. # ciphers that are actually used in key wrapping
  87. _SSH_CIPHERS: dict[bytes, _SSHCipher] = {
  88. b"aes256-ctr": _SSHCipher(
  89. alg=algorithms.AES,
  90. key_len=32,
  91. mode=modes.CTR,
  92. block_len=16,
  93. iv_len=16,
  94. tag_len=None,
  95. is_aead=False,
  96. ),
  97. b"aes256-cbc": _SSHCipher(
  98. alg=algorithms.AES,
  99. key_len=32,
  100. mode=modes.CBC,
  101. block_len=16,
  102. iv_len=16,
  103. tag_len=None,
  104. is_aead=False,
  105. ),
  106. b"aes256-gcm@openssh.com": _SSHCipher(
  107. alg=algorithms.AES,
  108. key_len=32,
  109. mode=modes.GCM,
  110. block_len=16,
  111. iv_len=12,
  112. tag_len=16,
  113. is_aead=True,
  114. ),
  115. }
  116. # map local curve name to key type
  117. _ECDSA_KEY_TYPE = {
  118. "secp256r1": _ECDSA_NISTP256,
  119. "secp384r1": _ECDSA_NISTP384,
  120. "secp521r1": _ECDSA_NISTP521,
  121. }
  122. def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes:
  123. if isinstance(key, ec.EllipticCurvePrivateKey):
  124. key_type = _ecdsa_key_type(key.public_key())
  125. elif isinstance(key, ec.EllipticCurvePublicKey):
  126. key_type = _ecdsa_key_type(key)
  127. elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
  128. key_type = _SSH_RSA
  129. elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)):
  130. key_type = _SSH_DSA
  131. elif isinstance(
  132. key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey)
  133. ):
  134. key_type = _SSH_ED25519
  135. else:
  136. raise ValueError("Unsupported key type")
  137. return key_type
  138. def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes:
  139. """Return SSH key_type and curve_name for private key."""
  140. curve = public_key.curve
  141. if curve.name not in _ECDSA_KEY_TYPE:
  142. raise ValueError(
  143. f"Unsupported curve for ssh private key: {curve.name!r}"
  144. )
  145. return _ECDSA_KEY_TYPE[curve.name]
  146. def _ssh_pem_encode(
  147. data: utils.Buffer,
  148. prefix: bytes = _SK_START + b"\n",
  149. suffix: bytes = _SK_END + b"\n",
  150. ) -> bytes:
  151. return b"".join([prefix, _base64_encode(data), suffix])
  152. def _check_block_size(data: utils.Buffer, block_len: int) -> None:
  153. """Require data to be full blocks"""
  154. if not data or len(data) % block_len != 0:
  155. raise ValueError("Corrupt data: missing padding")
  156. def _check_empty(data: utils.Buffer) -> None:
  157. """All data should have been parsed."""
  158. if data:
  159. raise ValueError("Corrupt data: unparsed data")
  160. def _init_cipher(
  161. ciphername: bytes,
  162. password: bytes | None,
  163. salt: bytes,
  164. rounds: int,
  165. ) -> Cipher[modes.CBC | modes.CTR | modes.GCM]:
  166. """Generate key + iv and return cipher."""
  167. if not password:
  168. raise TypeError(
  169. "Key is password-protected, but password was not provided."
  170. )
  171. ciph = _SSH_CIPHERS[ciphername]
  172. seed = _bcrypt_kdf(
  173. password, salt, ciph.key_len + ciph.iv_len, rounds, True
  174. )
  175. return Cipher(
  176. ciph.alg(seed[: ciph.key_len]),
  177. ciph.mode(seed[ciph.key_len :]),
  178. )
  179. def _get_u32(data: memoryview) -> tuple[int, memoryview]:
  180. """Uint32"""
  181. if len(data) < 4:
  182. raise ValueError("Invalid data")
  183. return int.from_bytes(data[:4], byteorder="big"), data[4:]
  184. def _get_u64(data: memoryview) -> tuple[int, memoryview]:
  185. """Uint64"""
  186. if len(data) < 8:
  187. raise ValueError("Invalid data")
  188. return int.from_bytes(data[:8], byteorder="big"), data[8:]
  189. def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]:
  190. """Bytes with u32 length prefix"""
  191. n, data = _get_u32(data)
  192. if n > len(data):
  193. raise ValueError("Invalid data")
  194. return data[:n], data[n:]
  195. def _get_mpint(data: memoryview) -> tuple[int, memoryview]:
  196. """Big integer."""
  197. val, data = _get_sshstr(data)
  198. if val and val[0] > 0x7F:
  199. raise ValueError("Invalid data")
  200. return int.from_bytes(val, "big"), data
  201. def _to_mpint(val: int) -> bytes:
  202. """Storage format for signed bigint."""
  203. if val < 0:
  204. raise ValueError("negative mpint not allowed")
  205. if not val:
  206. return b""
  207. nbytes = (val.bit_length() + 8) // 8
  208. return utils.int_to_bytes(val, nbytes)
  209. class _FragList:
  210. """Build recursive structure without data copy."""
  211. flist: list[utils.Buffer]
  212. def __init__(self, init: list[utils.Buffer] | None = None) -> None:
  213. self.flist = []
  214. if init:
  215. self.flist.extend(init)
  216. def put_raw(self, val: utils.Buffer) -> None:
  217. """Add plain bytes"""
  218. self.flist.append(val)
  219. def put_u32(self, val: int) -> None:
  220. """Big-endian uint32"""
  221. self.flist.append(val.to_bytes(length=4, byteorder="big"))
  222. def put_u64(self, val: int) -> None:
  223. """Big-endian uint64"""
  224. self.flist.append(val.to_bytes(length=8, byteorder="big"))
  225. def put_sshstr(self, val: bytes | _FragList) -> None:
  226. """Bytes prefixed with u32 length"""
  227. if isinstance(val, (bytes, memoryview, bytearray)):
  228. self.put_u32(len(val))
  229. self.flist.append(val)
  230. else:
  231. self.put_u32(val.size())
  232. self.flist.extend(val.flist)
  233. def put_mpint(self, val: int) -> None:
  234. """Big-endian bigint prefixed with u32 length"""
  235. self.put_sshstr(_to_mpint(val))
  236. def size(self) -> int:
  237. """Current number of bytes"""
  238. return sum(map(len, self.flist))
  239. def render(self, dstbuf: memoryview, pos: int = 0) -> int:
  240. """Write into bytearray"""
  241. for frag in self.flist:
  242. flen = len(frag)
  243. start, pos = pos, pos + flen
  244. dstbuf[start:pos] = frag
  245. return pos
  246. def tobytes(self) -> bytes:
  247. """Return as bytes"""
  248. buf = memoryview(bytearray(self.size()))
  249. self.render(buf)
  250. return buf.tobytes()
  251. class _SSHFormatRSA:
  252. """Format for RSA keys.
  253. Public:
  254. mpint e, n
  255. Private:
  256. mpint n, e, d, iqmp, p, q
  257. """
  258. def get_public(
  259. self, data: memoryview
  260. ) -> tuple[tuple[int, int], memoryview]:
  261. """RSA public fields"""
  262. e, data = _get_mpint(data)
  263. n, data = _get_mpint(data)
  264. return (e, n), data
  265. def load_public(
  266. self, data: memoryview
  267. ) -> tuple[rsa.RSAPublicKey, memoryview]:
  268. """Make RSA public key from data."""
  269. (e, n), data = self.get_public(data)
  270. public_numbers = rsa.RSAPublicNumbers(e, n)
  271. public_key = public_numbers.public_key()
  272. return public_key, data
  273. def load_private(
  274. self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
  275. ) -> tuple[rsa.RSAPrivateKey, memoryview]:
  276. """Make RSA private key from data."""
  277. n, data = _get_mpint(data)
  278. e, data = _get_mpint(data)
  279. d, data = _get_mpint(data)
  280. iqmp, data = _get_mpint(data)
  281. p, data = _get_mpint(data)
  282. q, data = _get_mpint(data)
  283. if (e, n) != pubfields:
  284. raise ValueError("Corrupt data: rsa field mismatch")
  285. dmp1 = rsa.rsa_crt_dmp1(d, p)
  286. dmq1 = rsa.rsa_crt_dmq1(d, q)
  287. public_numbers = rsa.RSAPublicNumbers(e, n)
  288. private_numbers = rsa.RSAPrivateNumbers(
  289. p, q, d, dmp1, dmq1, iqmp, public_numbers
  290. )
  291. private_key = private_numbers.private_key(
  292. unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation
  293. )
  294. return private_key, data
  295. def encode_public(
  296. self, public_key: rsa.RSAPublicKey, f_pub: _FragList
  297. ) -> None:
  298. """Write RSA public key"""
  299. pubn = public_key.public_numbers()
  300. f_pub.put_mpint(pubn.e)
  301. f_pub.put_mpint(pubn.n)
  302. def encode_private(
  303. self, private_key: rsa.RSAPrivateKey, f_priv: _FragList
  304. ) -> None:
  305. """Write RSA private key"""
  306. private_numbers = private_key.private_numbers()
  307. public_numbers = private_numbers.public_numbers
  308. f_priv.put_mpint(public_numbers.n)
  309. f_priv.put_mpint(public_numbers.e)
  310. f_priv.put_mpint(private_numbers.d)
  311. f_priv.put_mpint(private_numbers.iqmp)
  312. f_priv.put_mpint(private_numbers.p)
  313. f_priv.put_mpint(private_numbers.q)
  314. class _SSHFormatDSA:
  315. """Format for DSA keys.
  316. Public:
  317. mpint p, q, g, y
  318. Private:
  319. mpint p, q, g, y, x
  320. """
  321. def get_public(self, data: memoryview) -> tuple[tuple, memoryview]:
  322. """DSA public fields"""
  323. p, data = _get_mpint(data)
  324. q, data = _get_mpint(data)
  325. g, data = _get_mpint(data)
  326. y, data = _get_mpint(data)
  327. return (p, q, g, y), data
  328. def load_public(
  329. self, data: memoryview
  330. ) -> tuple[dsa.DSAPublicKey, memoryview]:
  331. """Make DSA public key from data."""
  332. (p, q, g, y), data = self.get_public(data)
  333. parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
  334. public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
  335. self._validate(public_numbers)
  336. public_key = public_numbers.public_key()
  337. return public_key, data
  338. def load_private(
  339. self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
  340. ) -> tuple[dsa.DSAPrivateKey, memoryview]:
  341. """Make DSA private key from data."""
  342. (p, q, g, y), data = self.get_public(data)
  343. x, data = _get_mpint(data)
  344. if (p, q, g, y) != pubfields:
  345. raise ValueError("Corrupt data: dsa field mismatch")
  346. parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
  347. public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
  348. self._validate(public_numbers)
  349. private_numbers = dsa.DSAPrivateNumbers(x, public_numbers)
  350. private_key = private_numbers.private_key()
  351. return private_key, data
  352. def encode_public(
  353. self, public_key: dsa.DSAPublicKey, f_pub: _FragList
  354. ) -> None:
  355. """Write DSA public key"""
  356. public_numbers = public_key.public_numbers()
  357. parameter_numbers = public_numbers.parameter_numbers
  358. self._validate(public_numbers)
  359. f_pub.put_mpint(parameter_numbers.p)
  360. f_pub.put_mpint(parameter_numbers.q)
  361. f_pub.put_mpint(parameter_numbers.g)
  362. f_pub.put_mpint(public_numbers.y)
  363. def encode_private(
  364. self, private_key: dsa.DSAPrivateKey, f_priv: _FragList
  365. ) -> None:
  366. """Write DSA private key"""
  367. self.encode_public(private_key.public_key(), f_priv)
  368. f_priv.put_mpint(private_key.private_numbers().x)
  369. def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None:
  370. parameter_numbers = public_numbers.parameter_numbers
  371. if parameter_numbers.p.bit_length() != 1024:
  372. raise ValueError("SSH supports only 1024 bit DSA keys")
  373. class _SSHFormatECDSA:
  374. """Format for ECDSA keys.
  375. Public:
  376. str curve
  377. bytes point
  378. Private:
  379. str curve
  380. bytes point
  381. mpint secret
  382. """
  383. def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve):
  384. self.ssh_curve_name = ssh_curve_name
  385. self.curve = curve
  386. def get_public(
  387. self, data: memoryview
  388. ) -> tuple[tuple[memoryview, memoryview], memoryview]:
  389. """ECDSA public fields"""
  390. curve, data = _get_sshstr(data)
  391. point, data = _get_sshstr(data)
  392. if curve != self.ssh_curve_name:
  393. raise ValueError("Curve name mismatch")
  394. if len(point) == 0:
  395. raise ValueError("Invalid EC point: empty data")
  396. if point[0] != 4:
  397. raise NotImplementedError("Need uncompressed point")
  398. return (curve, point), data
  399. def load_public(
  400. self, data: memoryview
  401. ) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
  402. """Make ECDSA public key from data."""
  403. (_, point), data = self.get_public(data)
  404. public_key = ec.EllipticCurvePublicKey.from_encoded_point(
  405. self.curve, point.tobytes()
  406. )
  407. return public_key, data
  408. def load_private(
  409. self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
  410. ) -> tuple[ec.EllipticCurvePrivateKey, memoryview]:
  411. """Make ECDSA private key from data."""
  412. (curve_name, point), data = self.get_public(data)
  413. secret, data = _get_mpint(data)
  414. if (curve_name, point) != pubfields:
  415. raise ValueError("Corrupt data: ecdsa field mismatch")
  416. private_key = ec.derive_private_key(secret, self.curve)
  417. return private_key, data
  418. def encode_public(
  419. self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList
  420. ) -> None:
  421. """Write ECDSA public key"""
  422. point = public_key.public_bytes(
  423. Encoding.X962, PublicFormat.UncompressedPoint
  424. )
  425. f_pub.put_sshstr(self.ssh_curve_name)
  426. f_pub.put_sshstr(point)
  427. def encode_private(
  428. self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList
  429. ) -> None:
  430. """Write ECDSA private key"""
  431. public_key = private_key.public_key()
  432. private_numbers = private_key.private_numbers()
  433. self.encode_public(public_key, f_priv)
  434. f_priv.put_mpint(private_numbers.private_value)
  435. class _SSHFormatEd25519:
  436. """Format for Ed25519 keys.
  437. Public:
  438. bytes point
  439. Private:
  440. bytes point
  441. bytes secret_and_point
  442. """
  443. def get_public(
  444. self, data: memoryview
  445. ) -> tuple[tuple[memoryview], memoryview]:
  446. """Ed25519 public fields"""
  447. point, data = _get_sshstr(data)
  448. return (point,), data
  449. def load_public(
  450. self, data: memoryview
  451. ) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
  452. """Make Ed25519 public key from data."""
  453. (point,), data = self.get_public(data)
  454. public_key = ed25519.Ed25519PublicKey.from_public_bytes(
  455. point.tobytes()
  456. )
  457. return public_key, data
  458. def load_private(
  459. self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool
  460. ) -> tuple[ed25519.Ed25519PrivateKey, memoryview]:
  461. """Make Ed25519 private key from data."""
  462. (point,), data = self.get_public(data)
  463. keypair, data = _get_sshstr(data)
  464. secret = keypair[:32]
  465. point2 = keypair[32:]
  466. if point != point2 or (point,) != pubfields:
  467. raise ValueError("Corrupt data: ed25519 field mismatch")
  468. private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret)
  469. return private_key, data
  470. def encode_public(
  471. self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList
  472. ) -> None:
  473. """Write Ed25519 public key"""
  474. raw_public_key = public_key.public_bytes(
  475. Encoding.Raw, PublicFormat.Raw
  476. )
  477. f_pub.put_sshstr(raw_public_key)
  478. def encode_private(
  479. self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList
  480. ) -> None:
  481. """Write Ed25519 private key"""
  482. public_key = private_key.public_key()
  483. raw_private_key = private_key.private_bytes(
  484. Encoding.Raw, PrivateFormat.Raw, NoEncryption()
  485. )
  486. raw_public_key = public_key.public_bytes(
  487. Encoding.Raw, PublicFormat.Raw
  488. )
  489. f_keypair = _FragList([raw_private_key, raw_public_key])
  490. self.encode_public(public_key, f_priv)
  491. f_priv.put_sshstr(f_keypair)
  492. def load_application(data) -> tuple[memoryview, memoryview]:
  493. """
  494. U2F application strings
  495. """
  496. application, data = _get_sshstr(data)
  497. if not application.tobytes().startswith(b"ssh:"):
  498. raise ValueError(
  499. "U2F application string does not start with b'ssh:' "
  500. f"({application})"
  501. )
  502. return application, data
  503. class _SSHFormatSKEd25519:
  504. """
  505. The format of a sk-ssh-ed25519@openssh.com public key is:
  506. string "sk-ssh-ed25519@openssh.com"
  507. string public key
  508. string application (user-specified, but typically "ssh:")
  509. """
  510. def load_public(
  511. self, data: memoryview
  512. ) -> tuple[ed25519.Ed25519PublicKey, memoryview]:
  513. """Make Ed25519 public key from data."""
  514. public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data)
  515. _, data = load_application(data)
  516. return public_key, data
  517. def get_public(self, data: memoryview) -> typing.NoReturn:
  518. # Confusingly `get_public` is an entry point used by private key
  519. # loading.
  520. raise UnsupportedAlgorithm(
  521. "sk-ssh-ed25519 private keys cannot be loaded"
  522. )
  523. class _SSHFormatSKECDSA:
  524. """
  525. The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
  526. string "sk-ecdsa-sha2-nistp256@openssh.com"
  527. string curve name
  528. ec_point Q
  529. string application (user-specified, but typically "ssh:")
  530. """
  531. def load_public(
  532. self, data: memoryview
  533. ) -> tuple[ec.EllipticCurvePublicKey, memoryview]:
  534. """Make ECDSA public key from data."""
  535. public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data)
  536. _, data = load_application(data)
  537. return public_key, data
  538. def get_public(self, data: memoryview) -> typing.NoReturn:
  539. # Confusingly `get_public` is an entry point used by private key
  540. # loading.
  541. raise UnsupportedAlgorithm(
  542. "sk-ecdsa-sha2-nistp256 private keys cannot be loaded"
  543. )
  544. _KEY_FORMATS = {
  545. _SSH_RSA: _SSHFormatRSA(),
  546. _SSH_DSA: _SSHFormatDSA(),
  547. _SSH_ED25519: _SSHFormatEd25519(),
  548. _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
  549. _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
  550. _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
  551. _SK_SSH_ED25519: _SSHFormatSKEd25519(),
  552. _SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(),
  553. }
  554. def _lookup_kformat(key_type: utils.Buffer):
  555. """Return valid format or throw error"""
  556. if not isinstance(key_type, bytes):
  557. key_type = memoryview(key_type).tobytes()
  558. if key_type in _KEY_FORMATS:
  559. return _KEY_FORMATS[key_type]
  560. raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}")
  561. SSHPrivateKeyTypes = typing.Union[
  562. ec.EllipticCurvePrivateKey,
  563. rsa.RSAPrivateKey,
  564. dsa.DSAPrivateKey,
  565. ed25519.Ed25519PrivateKey,
  566. ]
  567. def load_ssh_private_key(
  568. data: utils.Buffer,
  569. password: bytes | None,
  570. backend: typing.Any = None,
  571. *,
  572. unsafe_skip_rsa_key_validation: bool = False,
  573. ) -> SSHPrivateKeyTypes:
  574. """Load private key from OpenSSH custom encoding."""
  575. utils._check_byteslike("data", data)
  576. if password is not None:
  577. utils._check_bytes("password", password)
  578. m = _PEM_RC.search(data)
  579. if not m:
  580. raise ValueError("Not OpenSSH private key format")
  581. p1 = m.start(1)
  582. p2 = m.end(1)
  583. data = binascii.a2b_base64(memoryview(data)[p1:p2])
  584. if not data.startswith(_SK_MAGIC):
  585. raise ValueError("Not OpenSSH private key format")
  586. data = memoryview(data)[len(_SK_MAGIC) :]
  587. # parse header
  588. ciphername, data = _get_sshstr(data)
  589. kdfname, data = _get_sshstr(data)
  590. kdfoptions, data = _get_sshstr(data)
  591. nkeys, data = _get_u32(data)
  592. if nkeys != 1:
  593. raise ValueError("Only one key supported")
  594. # load public key data
  595. pubdata, data = _get_sshstr(data)
  596. pub_key_type, pubdata = _get_sshstr(pubdata)
  597. kformat = _lookup_kformat(pub_key_type)
  598. pubfields, pubdata = kformat.get_public(pubdata)
  599. _check_empty(pubdata)
  600. if ciphername != _NONE or kdfname != _NONE:
  601. ciphername_bytes = ciphername.tobytes()
  602. if ciphername_bytes not in _SSH_CIPHERS:
  603. raise UnsupportedAlgorithm(
  604. f"Unsupported cipher: {ciphername_bytes!r}"
  605. )
  606. if kdfname != _BCRYPT:
  607. raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}")
  608. blklen = _SSH_CIPHERS[ciphername_bytes].block_len
  609. tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len
  610. # load secret data
  611. edata, data = _get_sshstr(data)
  612. # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for
  613. # information about how OpenSSH handles AEAD tags
  614. if _SSH_CIPHERS[ciphername_bytes].is_aead:
  615. tag = bytes(data)
  616. if len(tag) != tag_len:
  617. raise ValueError("Corrupt data: invalid tag length for cipher")
  618. else:
  619. _check_empty(data)
  620. _check_block_size(edata, blklen)
  621. salt, kbuf = _get_sshstr(kdfoptions)
  622. rounds, kbuf = _get_u32(kbuf)
  623. _check_empty(kbuf)
  624. ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds)
  625. dec = ciph.decryptor()
  626. edata = memoryview(dec.update(edata))
  627. if _SSH_CIPHERS[ciphername_bytes].is_aead:
  628. assert isinstance(dec, AEADDecryptionContext)
  629. _check_empty(dec.finalize_with_tag(tag))
  630. else:
  631. # _check_block_size requires data to be a full block so there
  632. # should be no output from finalize
  633. _check_empty(dec.finalize())
  634. else:
  635. if password:
  636. raise TypeError(
  637. "Password was given but private key is not encrypted."
  638. )
  639. # load secret data
  640. edata, data = _get_sshstr(data)
  641. _check_empty(data)
  642. blklen = 8
  643. _check_block_size(edata, blklen)
  644. ck1, edata = _get_u32(edata)
  645. ck2, edata = _get_u32(edata)
  646. if ck1 != ck2:
  647. raise ValueError("Corrupt data: broken checksum")
  648. # load per-key struct
  649. key_type, edata = _get_sshstr(edata)
  650. if key_type != pub_key_type:
  651. raise ValueError("Corrupt data: key type mismatch")
  652. private_key, edata = kformat.load_private(
  653. edata,
  654. pubfields,
  655. unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
  656. )
  657. # We don't use the comment
  658. _, edata = _get_sshstr(edata)
  659. # yes, SSH does padding check *after* all other parsing is done.
  660. # need to follow as it writes zero-byte padding too.
  661. if edata != _PADDING[: len(edata)]:
  662. raise ValueError("Corrupt data: invalid padding")
  663. if isinstance(private_key, dsa.DSAPrivateKey):
  664. warnings.warn(
  665. "SSH DSA keys are deprecated and will be removed in a future "
  666. "release.",
  667. utils.DeprecatedIn40,
  668. stacklevel=2,
  669. )
  670. return private_key
  671. def _serialize_ssh_private_key(
  672. private_key: SSHPrivateKeyTypes,
  673. password: bytes,
  674. encryption_algorithm: KeySerializationEncryption,
  675. ) -> bytes:
  676. """Serialize private key with OpenSSH custom encoding."""
  677. utils._check_bytes("password", password)
  678. if isinstance(private_key, dsa.DSAPrivateKey):
  679. warnings.warn(
  680. "SSH DSA key support is deprecated and will be "
  681. "removed in a future release",
  682. utils.DeprecatedIn40,
  683. stacklevel=4,
  684. )
  685. key_type = _get_ssh_key_type(private_key)
  686. kformat = _lookup_kformat(key_type)
  687. # setup parameters
  688. f_kdfoptions = _FragList()
  689. if password:
  690. ciphername = _DEFAULT_CIPHER
  691. blklen = _SSH_CIPHERS[ciphername].block_len
  692. kdfname = _BCRYPT
  693. rounds = _DEFAULT_ROUNDS
  694. if (
  695. isinstance(encryption_algorithm, _KeySerializationEncryption)
  696. and encryption_algorithm._kdf_rounds is not None
  697. ):
  698. rounds = encryption_algorithm._kdf_rounds
  699. salt = os.urandom(16)
  700. f_kdfoptions.put_sshstr(salt)
  701. f_kdfoptions.put_u32(rounds)
  702. ciph = _init_cipher(ciphername, password, salt, rounds)
  703. else:
  704. ciphername = kdfname = _NONE
  705. blklen = 8
  706. ciph = None
  707. nkeys = 1
  708. checkval = os.urandom(4)
  709. comment = b""
  710. # encode public and private parts together
  711. f_public_key = _FragList()
  712. f_public_key.put_sshstr(key_type)
  713. kformat.encode_public(private_key.public_key(), f_public_key)
  714. f_secrets = _FragList([checkval, checkval])
  715. f_secrets.put_sshstr(key_type)
  716. kformat.encode_private(private_key, f_secrets)
  717. f_secrets.put_sshstr(comment)
  718. f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)])
  719. # top-level structure
  720. f_main = _FragList()
  721. f_main.put_raw(_SK_MAGIC)
  722. f_main.put_sshstr(ciphername)
  723. f_main.put_sshstr(kdfname)
  724. f_main.put_sshstr(f_kdfoptions)
  725. f_main.put_u32(nkeys)
  726. f_main.put_sshstr(f_public_key)
  727. f_main.put_sshstr(f_secrets)
  728. # copy result info bytearray
  729. slen = f_secrets.size()
  730. mlen = f_main.size()
  731. buf = memoryview(bytearray(mlen + blklen))
  732. f_main.render(buf)
  733. ofs = mlen - slen
  734. # encrypt in-place
  735. if ciph is not None:
  736. ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
  737. return _ssh_pem_encode(buf[:mlen])
  738. SSHPublicKeyTypes = typing.Union[
  739. ec.EllipticCurvePublicKey,
  740. rsa.RSAPublicKey,
  741. dsa.DSAPublicKey,
  742. ed25519.Ed25519PublicKey,
  743. ]
  744. SSHCertPublicKeyTypes = typing.Union[
  745. ec.EllipticCurvePublicKey,
  746. rsa.RSAPublicKey,
  747. ed25519.Ed25519PublicKey,
  748. ]
  749. class SSHCertificateType(enum.Enum):
  750. USER = 1
  751. HOST = 2
  752. class SSHCertificate:
  753. def __init__(
  754. self,
  755. _nonce: memoryview,
  756. _public_key: SSHPublicKeyTypes,
  757. _serial: int,
  758. _cctype: int,
  759. _key_id: memoryview,
  760. _valid_principals: list[bytes],
  761. _valid_after: int,
  762. _valid_before: int,
  763. _critical_options: dict[bytes, bytes],
  764. _extensions: dict[bytes, bytes],
  765. _sig_type: memoryview,
  766. _sig_key: memoryview,
  767. _inner_sig_type: memoryview,
  768. _signature: memoryview,
  769. _tbs_cert_body: memoryview,
  770. _cert_key_type: bytes,
  771. _cert_body: memoryview,
  772. ):
  773. self._nonce = _nonce
  774. self._public_key = _public_key
  775. self._serial = _serial
  776. try:
  777. self._type = SSHCertificateType(_cctype)
  778. except ValueError:
  779. raise ValueError("Invalid certificate type")
  780. self._key_id = _key_id
  781. self._valid_principals = _valid_principals
  782. self._valid_after = _valid_after
  783. self._valid_before = _valid_before
  784. self._critical_options = _critical_options
  785. self._extensions = _extensions
  786. self._sig_type = _sig_type
  787. self._sig_key = _sig_key
  788. self._inner_sig_type = _inner_sig_type
  789. self._signature = _signature
  790. self._cert_key_type = _cert_key_type
  791. self._cert_body = _cert_body
  792. self._tbs_cert_body = _tbs_cert_body
  793. @property
  794. def nonce(self) -> bytes:
  795. return bytes(self._nonce)
  796. def public_key(self) -> SSHCertPublicKeyTypes:
  797. # make mypy happy until we remove DSA support entirely and
  798. # the underlying union won't have a disallowed type
  799. return typing.cast(SSHCertPublicKeyTypes, self._public_key)
  800. @property
  801. def serial(self) -> int:
  802. return self._serial
  803. @property
  804. def type(self) -> SSHCertificateType:
  805. return self._type
  806. @property
  807. def key_id(self) -> bytes:
  808. return bytes(self._key_id)
  809. @property
  810. def valid_principals(self) -> list[bytes]:
  811. return self._valid_principals
  812. @property
  813. def valid_before(self) -> int:
  814. return self._valid_before
  815. @property
  816. def valid_after(self) -> int:
  817. return self._valid_after
  818. @property
  819. def critical_options(self) -> dict[bytes, bytes]:
  820. return self._critical_options
  821. @property
  822. def extensions(self) -> dict[bytes, bytes]:
  823. return self._extensions
  824. def signature_key(self) -> SSHCertPublicKeyTypes:
  825. sigformat = _lookup_kformat(self._sig_type)
  826. signature_key, sigkey_rest = sigformat.load_public(self._sig_key)
  827. _check_empty(sigkey_rest)
  828. return signature_key
  829. def public_bytes(self) -> bytes:
  830. return (
  831. bytes(self._cert_key_type)
  832. + b" "
  833. + binascii.b2a_base64(bytes(self._cert_body), newline=False)
  834. )
  835. def verify_cert_signature(self) -> None:
  836. signature_key = self.signature_key()
  837. if isinstance(signature_key, ed25519.Ed25519PublicKey):
  838. signature_key.verify(
  839. bytes(self._signature), bytes(self._tbs_cert_body)
  840. )
  841. elif isinstance(signature_key, ec.EllipticCurvePublicKey):
  842. # The signature is encoded as a pair of big-endian integers
  843. r, data = _get_mpint(self._signature)
  844. s, data = _get_mpint(data)
  845. _check_empty(data)
  846. computed_sig = asym_utils.encode_dss_signature(r, s)
  847. hash_alg = _get_ec_hash_alg(signature_key.curve)
  848. signature_key.verify(
  849. computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg)
  850. )
  851. else:
  852. assert isinstance(signature_key, rsa.RSAPublicKey)
  853. if self._inner_sig_type == _SSH_RSA:
  854. hash_alg = hashes.SHA1()
  855. elif self._inner_sig_type == _SSH_RSA_SHA256:
  856. hash_alg = hashes.SHA256()
  857. else:
  858. assert self._inner_sig_type == _SSH_RSA_SHA512
  859. hash_alg = hashes.SHA512()
  860. signature_key.verify(
  861. bytes(self._signature),
  862. bytes(self._tbs_cert_body),
  863. padding.PKCS1v15(),
  864. hash_alg,
  865. )
  866. def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm:
  867. if isinstance(curve, ec.SECP256R1):
  868. return hashes.SHA256()
  869. elif isinstance(curve, ec.SECP384R1):
  870. return hashes.SHA384()
  871. else:
  872. assert isinstance(curve, ec.SECP521R1)
  873. return hashes.SHA512()
  874. def _load_ssh_public_identity(
  875. data: utils.Buffer,
  876. _legacy_dsa_allowed=False,
  877. ) -> SSHCertificate | SSHPublicKeyTypes:
  878. utils._check_byteslike("data", data)
  879. m = _SSH_PUBKEY_RC.match(data)
  880. if not m:
  881. raise ValueError("Invalid line format")
  882. key_type = orig_key_type = m.group(1)
  883. key_body = m.group(2)
  884. with_cert = False
  885. if key_type.endswith(_CERT_SUFFIX):
  886. with_cert = True
  887. key_type = key_type[: -len(_CERT_SUFFIX)]
  888. if key_type == _SSH_DSA and not _legacy_dsa_allowed:
  889. raise UnsupportedAlgorithm(
  890. "DSA keys aren't supported in SSH certificates"
  891. )
  892. kformat = _lookup_kformat(key_type)
  893. try:
  894. rest = memoryview(binascii.a2b_base64(key_body))
  895. except (TypeError, binascii.Error):
  896. raise ValueError("Invalid format")
  897. if with_cert:
  898. cert_body = rest
  899. inner_key_type, rest = _get_sshstr(rest)
  900. if inner_key_type != orig_key_type:
  901. raise ValueError("Invalid key format")
  902. if with_cert:
  903. nonce, rest = _get_sshstr(rest)
  904. public_key, rest = kformat.load_public(rest)
  905. if with_cert:
  906. serial, rest = _get_u64(rest)
  907. cctype, rest = _get_u32(rest)
  908. key_id, rest = _get_sshstr(rest)
  909. principals, rest = _get_sshstr(rest)
  910. valid_principals = []
  911. while principals:
  912. principal, principals = _get_sshstr(principals)
  913. valid_principals.append(bytes(principal))
  914. valid_after, rest = _get_u64(rest)
  915. valid_before, rest = _get_u64(rest)
  916. crit_options, rest = _get_sshstr(rest)
  917. critical_options = _parse_exts_opts(crit_options)
  918. exts, rest = _get_sshstr(rest)
  919. extensions = _parse_exts_opts(exts)
  920. # Get the reserved field, which is unused.
  921. _, rest = _get_sshstr(rest)
  922. sig_key_raw, rest = _get_sshstr(rest)
  923. sig_type, sig_key = _get_sshstr(sig_key_raw)
  924. if sig_type == _SSH_DSA and not _legacy_dsa_allowed:
  925. raise UnsupportedAlgorithm(
  926. "DSA signatures aren't supported in SSH certificates"
  927. )
  928. # Get the entire cert body and subtract the signature
  929. tbs_cert_body = cert_body[: -len(rest)]
  930. signature_raw, rest = _get_sshstr(rest)
  931. _check_empty(rest)
  932. inner_sig_type, sig_rest = _get_sshstr(signature_raw)
  933. # RSA certs can have multiple algorithm types
  934. if (
  935. sig_type == _SSH_RSA
  936. and inner_sig_type
  937. not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA]
  938. ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type):
  939. raise ValueError("Signature key type does not match")
  940. signature, sig_rest = _get_sshstr(sig_rest)
  941. _check_empty(sig_rest)
  942. return SSHCertificate(
  943. nonce,
  944. public_key,
  945. serial,
  946. cctype,
  947. key_id,
  948. valid_principals,
  949. valid_after,
  950. valid_before,
  951. critical_options,
  952. extensions,
  953. sig_type,
  954. sig_key,
  955. inner_sig_type,
  956. signature,
  957. tbs_cert_body,
  958. orig_key_type,
  959. cert_body,
  960. )
  961. else:
  962. _check_empty(rest)
  963. return public_key
  964. def load_ssh_public_identity(
  965. data: utils.Buffer,
  966. ) -> SSHCertificate | SSHPublicKeyTypes:
  967. return _load_ssh_public_identity(data)
  968. def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]:
  969. result: dict[bytes, bytes] = {}
  970. last_name = None
  971. while exts_opts:
  972. name, exts_opts = _get_sshstr(exts_opts)
  973. bname: bytes = bytes(name)
  974. if bname in result:
  975. raise ValueError("Duplicate name")
  976. if last_name is not None and bname < last_name:
  977. raise ValueError("Fields not lexically sorted")
  978. value, exts_opts = _get_sshstr(exts_opts)
  979. if len(value) > 0:
  980. value, extra = _get_sshstr(value)
  981. if len(extra) > 0:
  982. raise ValueError("Unexpected extra data after value")
  983. result[bname] = bytes(value)
  984. last_name = bname
  985. return result
  986. def ssh_key_fingerprint(
  987. key: SSHPublicKeyTypes,
  988. hash_algorithm: hashes.MD5 | hashes.SHA256,
  989. ) -> bytes:
  990. if not isinstance(hash_algorithm, (hashes.MD5, hashes.SHA256)):
  991. raise TypeError("hash_algorithm must be either MD5 or SHA256")
  992. key_type = _get_ssh_key_type(key)
  993. kformat = _lookup_kformat(key_type)
  994. f_pub = _FragList()
  995. f_pub.put_sshstr(key_type)
  996. kformat.encode_public(key, f_pub)
  997. ssh_binary_data = f_pub.tobytes()
  998. # Hash the binary data
  999. hash_obj = hashes.Hash(hash_algorithm)
  1000. hash_obj.update(ssh_binary_data)
  1001. return hash_obj.finalize()
  1002. def load_ssh_public_key(
  1003. data: utils.Buffer, backend: typing.Any = None
  1004. ) -> SSHPublicKeyTypes:
  1005. cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True)
  1006. public_key: SSHPublicKeyTypes
  1007. if isinstance(cert_or_key, SSHCertificate):
  1008. public_key = cert_or_key.public_key()
  1009. else:
  1010. public_key = cert_or_key
  1011. if isinstance(public_key, dsa.DSAPublicKey):
  1012. warnings.warn(
  1013. "SSH DSA keys are deprecated and will be removed in a future "
  1014. "release.",
  1015. utils.DeprecatedIn40,
  1016. stacklevel=2,
  1017. )
  1018. return public_key
  1019. def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes:
  1020. """One-line public key format for OpenSSH"""
  1021. if isinstance(public_key, dsa.DSAPublicKey):
  1022. warnings.warn(
  1023. "SSH DSA key support is deprecated and will be "
  1024. "removed in a future release",
  1025. utils.DeprecatedIn40,
  1026. stacklevel=4,
  1027. )
  1028. key_type = _get_ssh_key_type(public_key)
  1029. kformat = _lookup_kformat(key_type)
  1030. f_pub = _FragList()
  1031. f_pub.put_sshstr(key_type)
  1032. kformat.encode_public(public_key, f_pub)
  1033. pub = binascii.b2a_base64(f_pub.tobytes()).strip()
  1034. return b"".join([key_type, b" ", pub])
  1035. SSHCertPrivateKeyTypes = typing.Union[
  1036. ec.EllipticCurvePrivateKey,
  1037. rsa.RSAPrivateKey,
  1038. ed25519.Ed25519PrivateKey,
  1039. ]
  1040. # This is an undocumented limit enforced in the openssh codebase for sshd and
  1041. # ssh-keygen, but it is undefined in the ssh certificates spec.
  1042. _SSHKEY_CERT_MAX_PRINCIPALS = 256
  1043. class SSHCertificateBuilder:
  1044. def __init__(
  1045. self,
  1046. _public_key: SSHCertPublicKeyTypes | None = None,
  1047. _serial: int | None = None,
  1048. _type: SSHCertificateType | None = None,
  1049. _key_id: bytes | None = None,
  1050. _valid_principals: list[bytes] = [],
  1051. _valid_for_all_principals: bool = False,
  1052. _valid_before: int | None = None,
  1053. _valid_after: int | None = None,
  1054. _critical_options: list[tuple[bytes, bytes]] = [],
  1055. _extensions: list[tuple[bytes, bytes]] = [],
  1056. ):
  1057. self._public_key = _public_key
  1058. self._serial = _serial
  1059. self._type = _type
  1060. self._key_id = _key_id
  1061. self._valid_principals = _valid_principals
  1062. self._valid_for_all_principals = _valid_for_all_principals
  1063. self._valid_before = _valid_before
  1064. self._valid_after = _valid_after
  1065. self._critical_options = _critical_options
  1066. self._extensions = _extensions
  1067. def public_key(
  1068. self, public_key: SSHCertPublicKeyTypes
  1069. ) -> SSHCertificateBuilder:
  1070. if not isinstance(
  1071. public_key,
  1072. (
  1073. ec.EllipticCurvePublicKey,
  1074. rsa.RSAPublicKey,
  1075. ed25519.Ed25519PublicKey,
  1076. ),
  1077. ):
  1078. raise TypeError("Unsupported key type")
  1079. if self._public_key is not None:
  1080. raise ValueError("public_key already set")
  1081. return SSHCertificateBuilder(
  1082. _public_key=public_key,
  1083. _serial=self._serial,
  1084. _type=self._type,
  1085. _key_id=self._key_id,
  1086. _valid_principals=self._valid_principals,
  1087. _valid_for_all_principals=self._valid_for_all_principals,
  1088. _valid_before=self._valid_before,
  1089. _valid_after=self._valid_after,
  1090. _critical_options=self._critical_options,
  1091. _extensions=self._extensions,
  1092. )
  1093. def serial(self, serial: int) -> SSHCertificateBuilder:
  1094. if not isinstance(serial, int):
  1095. raise TypeError("serial must be an integer")
  1096. if not 0 <= serial < 2**64:
  1097. raise ValueError("serial must be between 0 and 2**64")
  1098. if self._serial is not None:
  1099. raise ValueError("serial already set")
  1100. return SSHCertificateBuilder(
  1101. _public_key=self._public_key,
  1102. _serial=serial,
  1103. _type=self._type,
  1104. _key_id=self._key_id,
  1105. _valid_principals=self._valid_principals,
  1106. _valid_for_all_principals=self._valid_for_all_principals,
  1107. _valid_before=self._valid_before,
  1108. _valid_after=self._valid_after,
  1109. _critical_options=self._critical_options,
  1110. _extensions=self._extensions,
  1111. )
  1112. def type(self, type: SSHCertificateType) -> SSHCertificateBuilder:
  1113. if not isinstance(type, SSHCertificateType):
  1114. raise TypeError("type must be an SSHCertificateType")
  1115. if self._type is not None:
  1116. raise ValueError("type already set")
  1117. return SSHCertificateBuilder(
  1118. _public_key=self._public_key,
  1119. _serial=self._serial,
  1120. _type=type,
  1121. _key_id=self._key_id,
  1122. _valid_principals=self._valid_principals,
  1123. _valid_for_all_principals=self._valid_for_all_principals,
  1124. _valid_before=self._valid_before,
  1125. _valid_after=self._valid_after,
  1126. _critical_options=self._critical_options,
  1127. _extensions=self._extensions,
  1128. )
  1129. def key_id(self, key_id: bytes) -> SSHCertificateBuilder:
  1130. if not isinstance(key_id, bytes):
  1131. raise TypeError("key_id must be bytes")
  1132. if self._key_id is not None:
  1133. raise ValueError("key_id already set")
  1134. return SSHCertificateBuilder(
  1135. _public_key=self._public_key,
  1136. _serial=self._serial,
  1137. _type=self._type,
  1138. _key_id=key_id,
  1139. _valid_principals=self._valid_principals,
  1140. _valid_for_all_principals=self._valid_for_all_principals,
  1141. _valid_before=self._valid_before,
  1142. _valid_after=self._valid_after,
  1143. _critical_options=self._critical_options,
  1144. _extensions=self._extensions,
  1145. )
  1146. def valid_principals(
  1147. self, valid_principals: list[bytes]
  1148. ) -> SSHCertificateBuilder:
  1149. if self._valid_for_all_principals:
  1150. raise ValueError(
  1151. "Principals can't be set because the cert is valid "
  1152. "for all principals"
  1153. )
  1154. if (
  1155. not all(isinstance(x, bytes) for x in valid_principals)
  1156. or not valid_principals
  1157. ):
  1158. raise TypeError(
  1159. "principals must be a list of bytes and can't be empty"
  1160. )
  1161. if self._valid_principals:
  1162. raise ValueError("valid_principals already set")
  1163. if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS:
  1164. raise ValueError(
  1165. "Reached or exceeded the maximum number of valid_principals"
  1166. )
  1167. return SSHCertificateBuilder(
  1168. _public_key=self._public_key,
  1169. _serial=self._serial,
  1170. _type=self._type,
  1171. _key_id=self._key_id,
  1172. _valid_principals=valid_principals,
  1173. _valid_for_all_principals=self._valid_for_all_principals,
  1174. _valid_before=self._valid_before,
  1175. _valid_after=self._valid_after,
  1176. _critical_options=self._critical_options,
  1177. _extensions=self._extensions,
  1178. )
  1179. def valid_for_all_principals(self):
  1180. if self._valid_principals:
  1181. raise ValueError(
  1182. "valid_principals already set, can't set "
  1183. "valid_for_all_principals"
  1184. )
  1185. if self._valid_for_all_principals:
  1186. raise ValueError("valid_for_all_principals already set")
  1187. return SSHCertificateBuilder(
  1188. _public_key=self._public_key,
  1189. _serial=self._serial,
  1190. _type=self._type,
  1191. _key_id=self._key_id,
  1192. _valid_principals=self._valid_principals,
  1193. _valid_for_all_principals=True,
  1194. _valid_before=self._valid_before,
  1195. _valid_after=self._valid_after,
  1196. _critical_options=self._critical_options,
  1197. _extensions=self._extensions,
  1198. )
  1199. def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder:
  1200. if not isinstance(valid_before, (int, float)):
  1201. raise TypeError("valid_before must be an int or float")
  1202. valid_before = int(valid_before)
  1203. if valid_before < 0 or valid_before >= 2**64:
  1204. raise ValueError("valid_before must [0, 2**64)")
  1205. if self._valid_before is not None:
  1206. raise ValueError("valid_before already set")
  1207. return SSHCertificateBuilder(
  1208. _public_key=self._public_key,
  1209. _serial=self._serial,
  1210. _type=self._type,
  1211. _key_id=self._key_id,
  1212. _valid_principals=self._valid_principals,
  1213. _valid_for_all_principals=self._valid_for_all_principals,
  1214. _valid_before=valid_before,
  1215. _valid_after=self._valid_after,
  1216. _critical_options=self._critical_options,
  1217. _extensions=self._extensions,
  1218. )
  1219. def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder:
  1220. if not isinstance(valid_after, (int, float)):
  1221. raise TypeError("valid_after must be an int or float")
  1222. valid_after = int(valid_after)
  1223. if valid_after < 0 or valid_after >= 2**64:
  1224. raise ValueError("valid_after must [0, 2**64)")
  1225. if self._valid_after is not None:
  1226. raise ValueError("valid_after already set")
  1227. return SSHCertificateBuilder(
  1228. _public_key=self._public_key,
  1229. _serial=self._serial,
  1230. _type=self._type,
  1231. _key_id=self._key_id,
  1232. _valid_principals=self._valid_principals,
  1233. _valid_for_all_principals=self._valid_for_all_principals,
  1234. _valid_before=self._valid_before,
  1235. _valid_after=valid_after,
  1236. _critical_options=self._critical_options,
  1237. _extensions=self._extensions,
  1238. )
  1239. def add_critical_option(
  1240. self, name: bytes, value: bytes
  1241. ) -> SSHCertificateBuilder:
  1242. if not isinstance(name, bytes) or not isinstance(value, bytes):
  1243. raise TypeError("name and value must be bytes")
  1244. # This is O(n**2)
  1245. if name in [name for name, _ in self._critical_options]:
  1246. raise ValueError("Duplicate critical option name")
  1247. return SSHCertificateBuilder(
  1248. _public_key=self._public_key,
  1249. _serial=self._serial,
  1250. _type=self._type,
  1251. _key_id=self._key_id,
  1252. _valid_principals=self._valid_principals,
  1253. _valid_for_all_principals=self._valid_for_all_principals,
  1254. _valid_before=self._valid_before,
  1255. _valid_after=self._valid_after,
  1256. _critical_options=[*self._critical_options, (name, value)],
  1257. _extensions=self._extensions,
  1258. )
  1259. def add_extension(
  1260. self, name: bytes, value: bytes
  1261. ) -> SSHCertificateBuilder:
  1262. if not isinstance(name, bytes) or not isinstance(value, bytes):
  1263. raise TypeError("name and value must be bytes")
  1264. # This is O(n**2)
  1265. if name in [name for name, _ in self._extensions]:
  1266. raise ValueError("Duplicate extension name")
  1267. return SSHCertificateBuilder(
  1268. _public_key=self._public_key,
  1269. _serial=self._serial,
  1270. _type=self._type,
  1271. _key_id=self._key_id,
  1272. _valid_principals=self._valid_principals,
  1273. _valid_for_all_principals=self._valid_for_all_principals,
  1274. _valid_before=self._valid_before,
  1275. _valid_after=self._valid_after,
  1276. _critical_options=self._critical_options,
  1277. _extensions=[*self._extensions, (name, value)],
  1278. )
  1279. def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate:
  1280. if not isinstance(
  1281. private_key,
  1282. (
  1283. ec.EllipticCurvePrivateKey,
  1284. rsa.RSAPrivateKey,
  1285. ed25519.Ed25519PrivateKey,
  1286. ),
  1287. ):
  1288. raise TypeError("Unsupported private key type")
  1289. if self._public_key is None:
  1290. raise ValueError("public_key must be set")
  1291. # Not required
  1292. serial = 0 if self._serial is None else self._serial
  1293. if self._type is None:
  1294. raise ValueError("type must be set")
  1295. # Not required
  1296. key_id = b"" if self._key_id is None else self._key_id
  1297. # A zero length list is valid, but means the certificate
  1298. # is valid for any principal of the specified type. We require
  1299. # the user to explicitly set valid_for_all_principals to get
  1300. # that behavior.
  1301. if not self._valid_principals and not self._valid_for_all_principals:
  1302. raise ValueError(
  1303. "valid_principals must be set if valid_for_all_principals "
  1304. "is False"
  1305. )
  1306. if self._valid_before is None:
  1307. raise ValueError("valid_before must be set")
  1308. if self._valid_after is None:
  1309. raise ValueError("valid_after must be set")
  1310. if self._valid_after > self._valid_before:
  1311. raise ValueError("valid_after must be earlier than valid_before")
  1312. # lexically sort our byte strings
  1313. self._critical_options.sort(key=lambda x: x[0])
  1314. self._extensions.sort(key=lambda x: x[0])
  1315. key_type = _get_ssh_key_type(self._public_key)
  1316. cert_prefix = key_type + _CERT_SUFFIX
  1317. # Marshal the bytes to be signed
  1318. nonce = os.urandom(32)
  1319. kformat = _lookup_kformat(key_type)
  1320. f = _FragList()
  1321. f.put_sshstr(cert_prefix)
  1322. f.put_sshstr(nonce)
  1323. kformat.encode_public(self._public_key, f)
  1324. f.put_u64(serial)
  1325. f.put_u32(self._type.value)
  1326. f.put_sshstr(key_id)
  1327. fprincipals = _FragList()
  1328. for p in self._valid_principals:
  1329. fprincipals.put_sshstr(p)
  1330. f.put_sshstr(fprincipals.tobytes())
  1331. f.put_u64(self._valid_after)
  1332. f.put_u64(self._valid_before)
  1333. fcrit = _FragList()
  1334. for name, value in self._critical_options:
  1335. fcrit.put_sshstr(name)
  1336. if len(value) > 0:
  1337. foptval = _FragList()
  1338. foptval.put_sshstr(value)
  1339. fcrit.put_sshstr(foptval.tobytes())
  1340. else:
  1341. fcrit.put_sshstr(value)
  1342. f.put_sshstr(fcrit.tobytes())
  1343. fext = _FragList()
  1344. for name, value in self._extensions:
  1345. fext.put_sshstr(name)
  1346. if len(value) > 0:
  1347. fextval = _FragList()
  1348. fextval.put_sshstr(value)
  1349. fext.put_sshstr(fextval.tobytes())
  1350. else:
  1351. fext.put_sshstr(value)
  1352. f.put_sshstr(fext.tobytes())
  1353. f.put_sshstr(b"") # RESERVED FIELD
  1354. # encode CA public key
  1355. ca_type = _get_ssh_key_type(private_key)
  1356. caformat = _lookup_kformat(ca_type)
  1357. caf = _FragList()
  1358. caf.put_sshstr(ca_type)
  1359. caformat.encode_public(private_key.public_key(), caf)
  1360. f.put_sshstr(caf.tobytes())
  1361. # Sigs according to the rules defined for the CA's public key
  1362. # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA,
  1363. # and RFC8032 for Ed25519).
  1364. if isinstance(private_key, ed25519.Ed25519PrivateKey):
  1365. signature = private_key.sign(f.tobytes())
  1366. fsig = _FragList()
  1367. fsig.put_sshstr(ca_type)
  1368. fsig.put_sshstr(signature)
  1369. f.put_sshstr(fsig.tobytes())
  1370. elif isinstance(private_key, ec.EllipticCurvePrivateKey):
  1371. hash_alg = _get_ec_hash_alg(private_key.curve)
  1372. signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg))
  1373. r, s = asym_utils.decode_dss_signature(signature)
  1374. fsig = _FragList()
  1375. fsig.put_sshstr(ca_type)
  1376. fsigblob = _FragList()
  1377. fsigblob.put_mpint(r)
  1378. fsigblob.put_mpint(s)
  1379. fsig.put_sshstr(fsigblob.tobytes())
  1380. f.put_sshstr(fsig.tobytes())
  1381. else:
  1382. assert isinstance(private_key, rsa.RSAPrivateKey)
  1383. # Just like Golang, we're going to use SHA512 for RSA
  1384. # https://cs.opensource.google/go/x/crypto/+/refs/tags/
  1385. # v0.4.0:ssh/certs.go;l=445
  1386. # RFC 8332 defines SHA256 and 512 as options
  1387. fsig = _FragList()
  1388. fsig.put_sshstr(_SSH_RSA_SHA512)
  1389. signature = private_key.sign(
  1390. f.tobytes(), padding.PKCS1v15(), hashes.SHA512()
  1391. )
  1392. fsig.put_sshstr(signature)
  1393. f.put_sshstr(fsig.tobytes())
  1394. cert_data = binascii.b2a_base64(f.tobytes()).strip()
  1395. # load_ssh_public_identity returns a union, but this is
  1396. # guaranteed to be an SSHCertificate, so we cast to make
  1397. # mypy happy.
  1398. return typing.cast(
  1399. SSHCertificate,
  1400. load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])),
  1401. )