Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PyNaCl-1.6.0.dev1-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

114 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 typing import ClassVar, Generic, Optional, Type, TypeVar 

15 

16import nacl.bindings 

17from nacl import encoding 

18from nacl import exceptions as exc 

19from nacl.encoding import Encoder 

20from nacl.utils import EncryptedMessage, StringFixer, random 

21 

22 

23class PublicKey(encoding.Encodable, StringFixer): 

24 """ 

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

26 for encrypting messages. 

27 

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

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

30 

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

32 """ 

33 

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

35 

36 def __init__( 

37 self, 

38 public_key: bytes, 

39 encoder: encoding.Encoder = encoding.RawEncoder, 

40 ): 

41 self._public_key = encoder.decode(public_key) 

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

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

44 

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

46 raise exc.ValueError( 

47 "The public key must be exactly {} bytes long".format( 

48 self.SIZE 

49 ) 

50 ) 

51 

52 def __bytes__(self) -> bytes: 

53 return self._public_key 

54 

55 def __hash__(self) -> int: 

56 return hash(bytes(self)) 

57 

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

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

60 return False 

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

62 

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

64 return not (self == other) 

65 

66 

67class PrivateKey(encoding.Encodable, StringFixer): 

68 """ 

69 Private key for decrypting messages using the Curve25519 algorithm. 

70 

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

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

73 any message encrypted by the corresponding 

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

75 

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

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

78 

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

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

81 private key is required to be 

82 """ 

83 

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

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

86 

87 def __init__( 

88 self, 

89 private_key: bytes, 

90 encoder: encoding.Encoder = encoding.RawEncoder, 

91 ): 

92 # Decode the secret_key 

93 private_key = encoder.decode(private_key) 

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

95 if not ( 

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

97 ): 

98 raise exc.TypeError( 

99 ( 

100 "PrivateKey must be created from a {} bytes long raw secret key" 

101 ).format(self.SIZE) 

102 ) 

103 

104 raw_public_key = nacl.bindings.crypto_scalarmult_base(private_key) 

105 

106 self._private_key = private_key 

107 self.public_key = PublicKey(raw_public_key) 

108 

109 @classmethod 

110 def from_seed( 

111 cls, 

112 seed: bytes, 

113 encoder: encoding.Encoder = encoding.RawEncoder, 

114 ) -> "PrivateKey": 

115 """ 

116 Generate a PrivateKey using a deterministic construction 

117 starting from a caller-provided seed 

118 

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

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

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

122 

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

124 Anyone who knows the seed is really in possession of 

125 the corresponding PrivateKey. 

126 

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

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

129 """ 

130 # decode the seed 

131 seed = encoder.decode(seed) 

132 # Verify the given seed type and size are correct 

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

134 raise exc.TypeError( 

135 ( 

136 "PrivateKey seed must be a {} bytes long binary sequence" 

137 ).format(cls.SEED_SIZE) 

138 ) 

139 # generate a raw key pair from the given seed 

140 raw_pk, raw_sk = nacl.bindings.crypto_box_seed_keypair(seed) 

141 # construct a instance from the raw secret key 

142 return cls(raw_sk) 

143 

144 def __bytes__(self) -> bytes: 

145 return self._private_key 

146 

147 def __hash__(self) -> int: 

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

149 

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

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

152 return False 

153 return self.public_key == other.public_key 

154 

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

156 return not (self == other) 

157 

158 @classmethod 

159 def generate(cls) -> "PrivateKey": 

160 """ 

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

162 

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

164 """ 

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

166 

167 

168_Box = TypeVar("_Box", bound="Box") 

169 

170 

171class Box(encoding.Encodable, StringFixer): 

172 """ 

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

174 

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

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

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

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

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

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

181 encryption. 

182 

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

184 decrypt messages 

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

186 decrypt messages 

187 

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

189 """ 

190 

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

192 _shared_key: bytes 

193 

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

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

196 public_key, PublicKey 

197 ): 

198 raise exc.TypeError( 

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

200 ) 

201 self._shared_key = nacl.bindings.crypto_box_beforenm( 

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

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

204 ) 

205 

206 def __bytes__(self) -> bytes: 

207 return self._shared_key 

208 

209 @classmethod 

210 def decode( 

211 cls: Type[_Box], encoded: bytes, encoder: Encoder = encoding.RawEncoder 

212 ) -> _Box: 

213 """ 

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

215 """ 

216 # Create an empty box 

217 box: _Box = cls.__new__(cls) 

218 

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

220 box._shared_key = encoder.decode(encoded) 

221 

222 return box 

223 

224 def encrypt( 

225 self, 

226 plaintext: bytes, 

227 nonce: Optional[bytes] = None, 

228 encoder: encoding.Encoder = encoding.RawEncoder, 

229 ) -> EncryptedMessage: 

230 """ 

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

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

233 encoder. 

234 

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

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

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

238 

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

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

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

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

243 """ 

244 if nonce is None: 

245 nonce = random(self.NONCE_SIZE) 

246 

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

248 raise exc.ValueError( 

249 "The nonce must be exactly %s bytes long" % self.NONCE_SIZE 

250 ) 

251 

252 ciphertext = nacl.bindings.crypto_box_easy_afternm( 

253 plaintext, 

254 nonce, 

255 self._shared_key, 

256 ) 

257 

258 encoded_nonce = encoder.encode(nonce) 

259 encoded_ciphertext = encoder.encode(ciphertext) 

260 

261 return EncryptedMessage._from_parts( 

262 encoded_nonce, 

263 encoded_ciphertext, 

264 encoder.encode(nonce + ciphertext), 

265 ) 

266 

267 def decrypt( 

268 self, 

269 ciphertext: bytes, 

270 nonce: Optional[bytes] = None, 

271 encoder: encoding.Encoder = encoding.RawEncoder, 

272 ) -> bytes: 

273 """ 

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

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

276 returns the plaintext message. 

277 

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

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

280 ciphertext 

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

282 :rtype: [:class:`bytes`] 

283 """ 

284 # Decode our ciphertext 

285 ciphertext = encoder.decode(ciphertext) 

286 

287 if nonce is None: 

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

289 nonce = ciphertext[: self.NONCE_SIZE] 

290 ciphertext = ciphertext[self.NONCE_SIZE :] 

291 

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

293 raise exc.ValueError( 

294 "The nonce must be exactly %s bytes long" % self.NONCE_SIZE 

295 ) 

296 

297 plaintext = nacl.bindings.crypto_box_open_easy_afternm( 

298 ciphertext, 

299 nonce, 

300 self._shared_key, 

301 ) 

302 

303 return plaintext 

304 

305 def shared_key(self) -> bytes: 

306 """ 

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

308 other symmetric ciphers. 

309 

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

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

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

313 your cipher is 32 bytes. 

314 :rtype: [:class:`bytes`] 

315 """ 

316 

317 return self._shared_key 

318 

319 

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

321 

322 

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

324 """ 

325 The SealedBox class boxes and unboxes messages addressed to 

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

327 whose private part will be discarded just after encrypting 

328 a single plaintext message. 

329 

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

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

332 ciphertext. 

333 

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

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

336 to decrypt messages. 

337 

338 .. versionadded:: 1.2 

339 """ 

340 

341 _public_key: bytes 

342 _private_key: Optional[bytes] 

343 

344 def __init__(self, recipient_key: _Key): 

345 if isinstance(recipient_key, PublicKey): 

346 self._public_key = recipient_key.encode( 

347 encoder=encoding.RawEncoder 

348 ) 

349 self._private_key = None 

350 elif isinstance(recipient_key, PrivateKey): 

351 self._private_key = recipient_key.encode( 

352 encoder=encoding.RawEncoder 

353 ) 

354 self._public_key = recipient_key.public_key.encode( 

355 encoder=encoding.RawEncoder 

356 ) 

357 else: 

358 raise exc.TypeError( 

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

360 ) 

361 

362 def __bytes__(self) -> bytes: 

363 return self._public_key 

364 

365 def encrypt( 

366 self, 

367 plaintext: bytes, 

368 encoder: encoding.Encoder = encoding.RawEncoder, 

369 ) -> bytes: 

370 """ 

371 Encrypts the plaintext message using a random-generated ephemeral 

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

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

374 encoded with the encoder. 

375 

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

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

378 decrypt the generated ciphertext. 

379 

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

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

382 :return bytes: encoded ciphertext 

383 """ 

384 

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

386 

387 encoded_ciphertext = encoder.encode(ciphertext) 

388 

389 return encoded_ciphertext 

390 

391 def decrypt( 

392 self: "SealedBox[PrivateKey]", 

393 ciphertext: bytes, 

394 encoder: encoding.Encoder = encoding.RawEncoder, 

395 ) -> bytes: 

396 """ 

397 Decrypts the ciphertext using the ephemeral public key enclosed 

398 in the ciphertext and the SealedBox private key, returning 

399 the plaintext message. 

400 

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

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

403 :return bytes: The original plaintext 

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

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

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

407 """ 

408 # Decode our ciphertext 

409 ciphertext = encoder.decode(ciphertext) 

410 

411 if self._private_key is None: 

412 raise TypeError( 

413 "SealedBoxes created with a public key cannot decrypt" 

414 ) 

415 plaintext = nacl.bindings.crypto_box_seal_open( 

416 ciphertext, 

417 self._public_key, 

418 self._private_key, 

419 ) 

420 

421 return plaintext