Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/nacl/bindings/crypto_box.py: 20%

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

108 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 Tuple 

15 

16from nacl import exceptions as exc 

17from nacl._sodium import ffi, lib 

18from nacl.exceptions import ensure 

19 

20 

21__all__ = ["crypto_box_keypair", "crypto_box"] 

22 

23 

24crypto_box_SECRETKEYBYTES: int = lib.crypto_box_secretkeybytes() 

25crypto_box_PUBLICKEYBYTES: int = lib.crypto_box_publickeybytes() 

26crypto_box_SEEDBYTES: int = lib.crypto_box_seedbytes() 

27crypto_box_NONCEBYTES: int = lib.crypto_box_noncebytes() 

28crypto_box_ZEROBYTES: int = lib.crypto_box_zerobytes() 

29crypto_box_BOXZEROBYTES: int = lib.crypto_box_boxzerobytes() 

30crypto_box_BEFORENMBYTES: int = lib.crypto_box_beforenmbytes() 

31crypto_box_SEALBYTES: int = lib.crypto_box_sealbytes() 

32 

33 

34def crypto_box_keypair() -> Tuple[bytes, bytes]: 

35 """ 

36 Returns a randomly generated public and secret key. 

37 

38 :rtype: (bytes(public_key), bytes(secret_key)) 

39 """ 

40 pk = ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES) 

41 sk = ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES) 

42 

43 rc = lib.crypto_box_keypair(pk, sk) 

44 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError) 

45 

46 return ( 

47 ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:], 

48 ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:], 

49 ) 

50 

51 

52def crypto_box_seed_keypair(seed: bytes) -> Tuple[bytes, bytes]: 

53 """ 

54 Returns a (public, secret) keypair deterministically generated 

55 from an input ``seed``. 

56 

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

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

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

60 

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

62 Anyone who knows the seed is really in possession of 

63 the corresponding PrivateKey. 

64 

65 

66 :param seed: bytes 

67 :rtype: (bytes(public_key), bytes(secret_key)) 

68 """ 

69 ensure(isinstance(seed, bytes), "seed must be bytes", raising=TypeError) 

70 

71 if len(seed) != crypto_box_SEEDBYTES: 

72 raise exc.ValueError("Invalid seed") 

73 

74 pk = ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES) 

75 sk = ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES) 

76 

77 rc = lib.crypto_box_seed_keypair(pk, sk, seed) 

78 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError) 

79 

80 return ( 

81 ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:], 

82 ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:], 

83 ) 

84 

85 

86def crypto_box(message: bytes, nonce: bytes, pk: bytes, sk: bytes) -> bytes: 

87 """ 

88 Encrypts and returns a message ``message`` using the secret key ``sk``, 

89 public key ``pk``, and the nonce ``nonce``. 

90 

91 :param message: bytes 

92 :param nonce: bytes 

93 :param pk: bytes 

94 :param sk: bytes 

95 :rtype: bytes 

96 """ 

97 if len(nonce) != crypto_box_NONCEBYTES: 

98 raise exc.ValueError("Invalid nonce size") 

99 

100 if len(pk) != crypto_box_PUBLICKEYBYTES: 

101 raise exc.ValueError("Invalid public key") 

102 

103 if len(sk) != crypto_box_SECRETKEYBYTES: 

104 raise exc.ValueError("Invalid secret key") 

105 

106 padded = (b"\x00" * crypto_box_ZEROBYTES) + message 

107 ciphertext = ffi.new("unsigned char[]", len(padded)) 

108 

109 rc = lib.crypto_box(ciphertext, padded, len(padded), nonce, pk, sk) 

110 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError) 

111 

112 return ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:] 

113 

114 

115def crypto_box_open( 

116 ciphertext: bytes, nonce: bytes, pk: bytes, sk: bytes 

117) -> bytes: 

118 """ 

119 Decrypts and returns an encrypted message ``ciphertext``, using the secret 

120 key ``sk``, public key ``pk``, and the nonce ``nonce``. 

121 

122 :param ciphertext: bytes 

123 :param nonce: bytes 

124 :param pk: bytes 

125 :param sk: bytes 

126 :rtype: bytes 

127 """ 

128 if len(nonce) != crypto_box_NONCEBYTES: 

129 raise exc.ValueError("Invalid nonce size") 

130 

131 if len(pk) != crypto_box_PUBLICKEYBYTES: 

132 raise exc.ValueError("Invalid public key") 

133 

134 if len(sk) != crypto_box_SECRETKEYBYTES: 

135 raise exc.ValueError("Invalid secret key") 

136 

137 padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext 

138 plaintext = ffi.new("unsigned char[]", len(padded)) 

139 

140 res = lib.crypto_box_open(plaintext, padded, len(padded), nonce, pk, sk) 

141 ensure( 

142 res == 0, 

143 "An error occurred trying to decrypt the message", 

144 raising=exc.CryptoError, 

145 ) 

146 

147 return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:] 

148 

149 

150def crypto_box_beforenm(pk: bytes, sk: bytes) -> bytes: 

151 """ 

152 Computes and returns the shared key for the public key ``pk`` and the 

153 secret key ``sk``. This can be used to speed up operations where the same 

154 set of keys is going to be used multiple times. 

155 

156 :param pk: bytes 

157 :param sk: bytes 

158 :rtype: bytes 

159 """ 

160 if len(pk) != crypto_box_PUBLICKEYBYTES: 

161 raise exc.ValueError("Invalid public key") 

162 

163 if len(sk) != crypto_box_SECRETKEYBYTES: 

164 raise exc.ValueError("Invalid secret key") 

165 

166 k = ffi.new("unsigned char[]", crypto_box_BEFORENMBYTES) 

167 

168 rc = lib.crypto_box_beforenm(k, pk, sk) 

169 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError) 

170 

171 return ffi.buffer(k, crypto_box_BEFORENMBYTES)[:] 

172 

173 

174def crypto_box_afternm(message: bytes, nonce: bytes, k: bytes) -> bytes: 

175 """ 

176 Encrypts and returns the message ``message`` using the shared key ``k`` and 

177 the nonce ``nonce``. 

178 

179 :param message: bytes 

180 :param nonce: bytes 

181 :param k: bytes 

182 :rtype: bytes 

183 """ 

184 if len(nonce) != crypto_box_NONCEBYTES: 

185 raise exc.ValueError("Invalid nonce") 

186 

187 if len(k) != crypto_box_BEFORENMBYTES: 

188 raise exc.ValueError("Invalid shared key") 

189 

190 padded = b"\x00" * crypto_box_ZEROBYTES + message 

191 ciphertext = ffi.new("unsigned char[]", len(padded)) 

192 

193 rc = lib.crypto_box_afternm(ciphertext, padded, len(padded), nonce, k) 

194 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError) 

195 

196 return ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:] 

197 

198 

199def crypto_box_open_afternm( 

200 ciphertext: bytes, nonce: bytes, k: bytes 

201) -> bytes: 

202 """ 

203 Decrypts and returns the encrypted message ``ciphertext``, using the shared 

204 key ``k`` and the nonce ``nonce``. 

205 

206 :param ciphertext: bytes 

207 :param nonce: bytes 

208 :param k: bytes 

209 :rtype: bytes 

210 """ 

211 if len(nonce) != crypto_box_NONCEBYTES: 

212 raise exc.ValueError("Invalid nonce") 

213 

214 if len(k) != crypto_box_BEFORENMBYTES: 

215 raise exc.ValueError("Invalid shared key") 

216 

217 padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext 

218 plaintext = ffi.new("unsigned char[]", len(padded)) 

219 

220 res = lib.crypto_box_open_afternm(plaintext, padded, len(padded), nonce, k) 

221 ensure( 

222 res == 0, 

223 "An error occurred trying to decrypt the message", 

224 raising=exc.CryptoError, 

225 ) 

226 

227 return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:] 

228 

229 

230def crypto_box_seal(message: bytes, pk: bytes) -> bytes: 

231 """ 

232 Encrypts and returns a message ``message`` using an ephemeral secret key 

233 and the public key ``pk``. 

234 The ephemeral public key, which is embedded in the sealed box, is also 

235 used, in combination with ``pk``, to derive the nonce needed for the 

236 underlying box construct. 

237 

238 :param message: bytes 

239 :param pk: bytes 

240 :rtype: bytes 

241 

242 .. versionadded:: 1.2 

243 """ 

244 ensure( 

245 isinstance(message, bytes), 

246 "input message must be bytes", 

247 raising=TypeError, 

248 ) 

249 

250 ensure( 

251 isinstance(pk, bytes), "public key must be bytes", raising=TypeError 

252 ) 

253 

254 if len(pk) != crypto_box_PUBLICKEYBYTES: 

255 raise exc.ValueError("Invalid public key") 

256 

257 _mlen = len(message) 

258 _clen = crypto_box_SEALBYTES + _mlen 

259 

260 ciphertext = ffi.new("unsigned char[]", _clen) 

261 

262 rc = lib.crypto_box_seal(ciphertext, message, _mlen, pk) 

263 ensure(rc == 0, "Unexpected library error", raising=exc.RuntimeError) 

264 

265 return ffi.buffer(ciphertext, _clen)[:] 

266 

267 

268def crypto_box_seal_open(ciphertext: bytes, pk: bytes, sk: bytes) -> bytes: 

269 """ 

270 Decrypts and returns an encrypted message ``ciphertext``, using the 

271 recipent's secret key ``sk`` and the sender's ephemeral public key 

272 embedded in the sealed box. The box contruct nonce is derived from 

273 the recipient's public key ``pk`` and the sender's public key. 

274 

275 :param ciphertext: bytes 

276 :param pk: bytes 

277 :param sk: bytes 

278 :rtype: bytes 

279 

280 .. versionadded:: 1.2 

281 """ 

282 ensure( 

283 isinstance(ciphertext, bytes), 

284 "input ciphertext must be bytes", 

285 raising=TypeError, 

286 ) 

287 

288 ensure( 

289 isinstance(pk, bytes), "public key must be bytes", raising=TypeError 

290 ) 

291 

292 ensure( 

293 isinstance(sk, bytes), "secret key must be bytes", raising=TypeError 

294 ) 

295 

296 if len(pk) != crypto_box_PUBLICKEYBYTES: 

297 raise exc.ValueError("Invalid public key") 

298 

299 if len(sk) != crypto_box_SECRETKEYBYTES: 

300 raise exc.ValueError("Invalid secret key") 

301 

302 _clen = len(ciphertext) 

303 

304 ensure( 

305 _clen >= crypto_box_SEALBYTES, 

306 ("Input cyphertext must be at least {} long").format( 

307 crypto_box_SEALBYTES 

308 ), 

309 raising=exc.TypeError, 

310 ) 

311 

312 _mlen = _clen - crypto_box_SEALBYTES 

313 

314 # zero-length malloc results are implementation.dependent 

315 plaintext = ffi.new("unsigned char[]", max(1, _mlen)) 

316 

317 res = lib.crypto_box_seal_open(plaintext, ciphertext, _clen, pk, sk) 

318 ensure( 

319 res == 0, 

320 "An error occurred trying to decrypt the message", 

321 raising=exc.CryptoError, 

322 ) 

323 

324 return ffi.buffer(plaintext, _mlen)[:]