Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PyNaCl-1.6.2-py3.11-linux-x86_64.egg/nacl/public.py: 39%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

116 statements  

1# Copyright 2013 Donald Stufft and individual contributors 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14from __future__ import annotations 

15 

16from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar 

17 

18import nacl.bindings 

19from nacl import encoding 

20from nacl import exceptions as exc 

21from nacl.encoding import Encoder 

22from nacl.utils import EncryptedMessage, StringFixer, random 

23 

24if TYPE_CHECKING: 

25 from typing_extensions import Self 

26 

27 

28class PublicKey(encoding.Encodable, StringFixer): 

29 """ 

30 The public key counterpart to an Curve25519 :class:`nacl.public.PrivateKey` 

31 for encrypting messages. 

32 

33 :param public_key: [:class:`bytes`] Encoded Curve25519 public key 

34 :param encoder: A class that is able to decode the `public_key` 

35 

36 :cvar SIZE: The size that the public key is required to be 

37 """ 

38 

39 SIZE: ClassVar[int] = nacl.bindings.crypto_box_PUBLICKEYBYTES 

40 

41 def __init__( 

42 self, 

43 public_key: bytes, 

44 encoder: encoding.Encoder = encoding.RawEncoder, 

45 ): 

46 self._public_key = encoder.decode(public_key) 

47 if not isinstance(self._public_key, bytes): 

48 raise exc.TypeError("PublicKey must be created from 32 bytes") 

49 

50 if len(self._public_key) != self.SIZE: 

51 raise exc.ValueError( 

52 f"The public key must be exactly {self.SIZE} bytes long" 

53 ) 

54 

55 def __bytes__(self) -> bytes: 

56 return self._public_key 

57 

58 def __hash__(self) -> int: 

59 return hash(bytes(self)) 

60 

61 def __eq__(self, other: object) -> bool: 

62 if not isinstance(other, self.__class__): 

63 return False 

64 return nacl.bindings.sodium_memcmp(bytes(self), bytes(other)) 

65 

66 def __ne__(self, other: object) -> bool: 

67 return not (self == other) 

68 

69 

70class PrivateKey(encoding.Encodable, StringFixer): 

71 """ 

72 Private key for decrypting messages using the Curve25519 algorithm. 

73 

74 .. warning:: This **must** be protected and remain secret. Anyone who 

75 knows the value of your :class:`~nacl.public.PrivateKey` can decrypt 

76 any message encrypted by the corresponding 

77 :class:`~nacl.public.PublicKey` 

78 

79 :param private_key: The private key used to decrypt messages 

80 :param encoder: The encoder class used to decode the given keys 

81 

82 :cvar SIZE: The size that the private key is required to be 

83 :cvar SEED_SIZE: The size that the seed used to generate the 

84 private key is required to be 

85 """ 

86 

87 SIZE: ClassVar[int] = nacl.bindings.crypto_box_SECRETKEYBYTES 

88 SEED_SIZE: ClassVar[int] = nacl.bindings.crypto_box_SEEDBYTES 

89 

90 def __init__( 

91 self, 

92 private_key: bytes, 

93 encoder: encoding.Encoder = encoding.RawEncoder, 

94 ): 

95 # Decode the secret_key 

96 private_key = encoder.decode(private_key) 

97 # verify the given secret key type and size are correct 

98 if not ( 

99 isinstance(private_key, bytes) and len(private_key) == self.SIZE 

100 ): 

101 raise exc.TypeError( 

102 f"PrivateKey must be created from a {self.SIZE} bytes long raw secret key" 

103 ) 

104 

105 raw_public_key = nacl.bindings.crypto_scalarmult_base(private_key) 

106 

107 self._private_key = private_key 

108 self.public_key = PublicKey(raw_public_key) 

109 

110 @classmethod 

111 def from_seed( 

112 cls, 

113 seed: bytes, 

114 encoder: encoding.Encoder = encoding.RawEncoder, 

115 ) -> PrivateKey: 

116 """ 

117 Generate a PrivateKey using a deterministic construction 

118 starting from a caller-provided seed 

119 

120 .. warning:: The seed **must** be high-entropy; therefore, 

121 its generator **must** be a cryptographic quality 

122 random function like, for example, :func:`~nacl.utils.random`. 

123 

124 .. warning:: The seed **must** be protected and remain secret. 

125 Anyone who knows the seed is really in possession of 

126 the corresponding PrivateKey. 

127 

128 :param seed: The seed used to generate the private key 

129 :rtype: :class:`~nacl.public.PrivateKey` 

130 """ 

131 # decode the seed 

132 seed = encoder.decode(seed) 

133 # Verify the given seed type and size are correct 

134 if not (isinstance(seed, bytes) and len(seed) == cls.SEED_SIZE): 

135 raise exc.TypeError( 

136 f"PrivateKey seed must be a {cls.SEED_SIZE} bytes long binary sequence" 

137 ) 

138 # generate a raw key pair from the given seed 

139 _raw_pk, raw_sk = nacl.bindings.crypto_box_seed_keypair(seed) 

140 # construct a instance from the raw secret key 

141 return cls(raw_sk) 

142 

143 def __bytes__(self) -> bytes: 

144 return self._private_key 

145 

146 def __hash__(self) -> int: 

147 return hash((type(self), bytes(self.public_key))) 

148 

149 def __eq__(self, other: object) -> bool: 

150 if not isinstance(other, self.__class__): 

151 return False 

152 return self.public_key == other.public_key 

153 

154 def __ne__(self, other: object) -> bool: 

155 return not (self == other) 

156 

157 @classmethod 

158 def generate(cls) -> PrivateKey: 

159 """ 

160 Generates a random :class:`~nacl.public.PrivateKey` object 

161 

162 :rtype: :class:`~nacl.public.PrivateKey` 

163 """ 

164 return cls(random(PrivateKey.SIZE), encoder=encoding.RawEncoder) 

165 

166 

167class Box(encoding.Encodable, StringFixer): 

168 """ 

169 The Box class boxes and unboxes messages between a pair of keys 

170 

171 The ciphertexts generated by :class:`~nacl.public.Box` include a 16 

172 byte authenticator which is checked as part of the decryption. An invalid 

173 authenticator will cause the decrypt function to raise an exception. The 

174 authenticator is not a signature. Once you've decrypted the message you've 

175 demonstrated the ability to create arbitrary valid message, so messages you 

176 send are repudiable. For non-repudiable messages, sign them after 

177 encryption. 

178 

179 :param private_key: :class:`~nacl.public.PrivateKey` used to encrypt and 

180 decrypt messages 

181 :param public_key: :class:`~nacl.public.PublicKey` used to encrypt and 

182 decrypt messages 

183 

184 :cvar NONCE_SIZE: The size that the nonce is required to be. 

185 """ 

186 

187 NONCE_SIZE: ClassVar[int] = nacl.bindings.crypto_box_NONCEBYTES 

188 _shared_key: bytes 

189 

190 def __init__(self, private_key: PrivateKey, public_key: PublicKey): 

191 if not isinstance(private_key, PrivateKey) or not isinstance( 

192 public_key, PublicKey 

193 ): 

194 raise exc.TypeError( 

195 "Box must be created from a PrivateKey and a PublicKey" 

196 ) 

197 self._shared_key = nacl.bindings.crypto_box_beforenm( 

198 public_key.encode(encoder=encoding.RawEncoder), 

199 private_key.encode(encoder=encoding.RawEncoder), 

200 ) 

201 

202 def __bytes__(self) -> bytes: 

203 return self._shared_key 

204 

205 @classmethod 

206 def decode( 

207 cls, encoded: bytes, encoder: Encoder = encoding.RawEncoder 

208 ) -> Self: 

209 """ 

210 Alternative constructor. Creates a Box from an existing Box's shared key. 

211 """ 

212 # Create an empty box 

213 box: Self = cls.__new__(cls) 

214 

215 # Assign our decoded value to the shared key of the box 

216 box._shared_key = encoder.decode(encoded) 

217 

218 return box 

219 

220 def encrypt( 

221 self, 

222 plaintext: bytes, 

223 nonce: bytes | None = None, 

224 encoder: encoding.Encoder = encoding.RawEncoder, 

225 ) -> EncryptedMessage: 

226 """ 

227 Encrypts the plaintext message using the given `nonce` (or generates 

228 one randomly if omitted) and returns the ciphertext encoded with the 

229 encoder. 

230 

231 .. warning:: It is **VITALLY** important that the nonce is a nonce, 

232 i.e. it is a number used only once for any given key. If you fail 

233 to do this, you compromise the privacy of the messages encrypted. 

234 

235 :param plaintext: [:class:`bytes`] The plaintext message to encrypt 

236 :param nonce: [:class:`bytes`] The nonce to use in the encryption 

237 :param encoder: The encoder to use to encode the ciphertext 

238 :rtype: [:class:`nacl.utils.EncryptedMessage`] 

239 """ 

240 if nonce is None: 

241 nonce = random(self.NONCE_SIZE) 

242 

243 if len(nonce) != self.NONCE_SIZE: 

244 raise exc.ValueError( 

245 f"The nonce must be exactly {self.NONCE_SIZE} bytes long" 

246 ) 

247 

248 ciphertext = nacl.bindings.crypto_box_easy_afternm( 

249 plaintext, 

250 nonce, 

251 self._shared_key, 

252 ) 

253 

254 encoded_nonce = encoder.encode(nonce) 

255 encoded_ciphertext = encoder.encode(ciphertext) 

256 

257 return EncryptedMessage._from_parts( 

258 encoded_nonce, 

259 encoded_ciphertext, 

260 encoder.encode(nonce + ciphertext), 

261 ) 

262 

263 def decrypt( 

264 self, 

265 ciphertext: bytes, 

266 nonce: bytes | None = None, 

267 encoder: encoding.Encoder = encoding.RawEncoder, 

268 ) -> bytes: 

269 """ 

270 Decrypts the ciphertext using the `nonce` (explicitly, when passed as a 

271 parameter or implicitly, when omitted, as part of the ciphertext) and 

272 returns the plaintext message. 

273 

274 :param ciphertext: [:class:`bytes`] The encrypted message to decrypt 

275 :param nonce: [:class:`bytes`] The nonce used when encrypting the 

276 ciphertext 

277 :param encoder: The encoder used to decode the ciphertext. 

278 :rtype: [:class:`bytes`] 

279 """ 

280 # Decode our ciphertext 

281 ciphertext = encoder.decode(ciphertext) 

282 

283 if nonce is None: 

284 # If we were given the nonce and ciphertext combined, split them. 

285 nonce = ciphertext[: self.NONCE_SIZE] 

286 ciphertext = ciphertext[self.NONCE_SIZE :] 

287 

288 if len(nonce) != self.NONCE_SIZE: 

289 raise exc.ValueError( 

290 f"The nonce must be exactly {self.NONCE_SIZE} bytes long" 

291 ) 

292 

293 plaintext = nacl.bindings.crypto_box_open_easy_afternm( 

294 ciphertext, 

295 nonce, 

296 self._shared_key, 

297 ) 

298 

299 return plaintext 

300 

301 def shared_key(self) -> bytes: 

302 """ 

303 Returns the Curve25519 shared secret, that can then be used as a key in 

304 other symmetric ciphers. 

305 

306 .. warning:: It is **VITALLY** important that you use a nonce with your 

307 symmetric cipher. If you fail to do this, you compromise the 

308 privacy of the messages encrypted. Ensure that the key length of 

309 your cipher is 32 bytes. 

310 :rtype: [:class:`bytes`] 

311 """ 

312 

313 return self._shared_key 

314 

315 

316_Key = TypeVar("_Key", PublicKey, PrivateKey) 

317 

318 

319class SealedBox(encoding.Encodable, StringFixer, Generic[_Key]): 

320 """ 

321 The SealedBox class boxes and unboxes messages addressed to 

322 a specified key-pair by using ephemeral sender's key pairs, 

323 whose private part will be discarded just after encrypting 

324 a single plaintext message. 

325 

326 The ciphertexts generated by :class:`~nacl.public.SecretBox` include 

327 the public part of the ephemeral key before the :class:`~nacl.public.Box` 

328 ciphertext. 

329 

330 :param recipient_key: a :class:`~nacl.public.PublicKey` used to encrypt 

331 messages and derive nonces, or a :class:`~nacl.public.PrivateKey` used 

332 to decrypt messages. 

333 

334 .. versionadded:: 1.2 

335 """ 

336 

337 _public_key: bytes 

338 _private_key: bytes | None 

339 

340 def __init__(self, recipient_key: _Key): 

341 if isinstance(recipient_key, PublicKey): 

342 self._public_key = recipient_key.encode( 

343 encoder=encoding.RawEncoder 

344 ) 

345 self._private_key = None 

346 elif isinstance(recipient_key, PrivateKey): 

347 self._private_key = recipient_key.encode( 

348 encoder=encoding.RawEncoder 

349 ) 

350 self._public_key = recipient_key.public_key.encode( 

351 encoder=encoding.RawEncoder 

352 ) 

353 else: 

354 raise exc.TypeError( 

355 "SealedBox must be created from a PublicKey or a PrivateKey" 

356 ) 

357 

358 def __bytes__(self) -> bytes: 

359 return self._public_key 

360 

361 def encrypt( 

362 self, 

363 plaintext: bytes, 

364 encoder: encoding.Encoder = encoding.RawEncoder, 

365 ) -> bytes: 

366 """ 

367 Encrypts the plaintext message using a random-generated ephemeral 

368 key pair and returns a "composed ciphertext", containing both 

369 the public part of the key pair and the ciphertext proper, 

370 encoded with the encoder. 

371 

372 The private part of the ephemeral key-pair will be scrubbed before 

373 returning the ciphertext, therefore, the sender will not be able to 

374 decrypt the generated ciphertext. 

375 

376 :param plaintext: [:class:`bytes`] The plaintext message to encrypt 

377 :param encoder: The encoder to use to encode the ciphertext 

378 :return bytes: encoded ciphertext 

379 """ 

380 

381 ciphertext = nacl.bindings.crypto_box_seal(plaintext, self._public_key) 

382 

383 encoded_ciphertext = encoder.encode(ciphertext) 

384 

385 return encoded_ciphertext 

386 

387 def decrypt( 

388 self: SealedBox[PrivateKey], 

389 ciphertext: bytes, 

390 encoder: encoding.Encoder = encoding.RawEncoder, 

391 ) -> bytes: 

392 """ 

393 Decrypts the ciphertext using the ephemeral public key enclosed 

394 in the ciphertext and the SealedBox private key, returning 

395 the plaintext message. 

396 

397 :param ciphertext: [:class:`bytes`] The encrypted message to decrypt 

398 :param encoder: The encoder used to decode the ciphertext. 

399 :return bytes: The original plaintext 

400 :raises TypeError: if this SealedBox was created with a 

401 :class:`~nacl.public.PublicKey` rather than a 

402 :class:`~nacl.public.PrivateKey`. 

403 """ 

404 # Decode our ciphertext 

405 ciphertext = encoder.decode(ciphertext) 

406 

407 if self._private_key is None: 

408 raise TypeError( 

409 "SealedBoxes created with a public key cannot decrypt" 

410 ) 

411 plaintext = nacl.bindings.crypto_box_seal_open( 

412 ciphertext, 

413 self._public_key, 

414 self._private_key, 

415 ) 

416 

417 return plaintext