or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

curves.mdecdh.mdeddsa.mdencoding.mdindex.mdkeys-signatures.mdmathematical-functions.md

ecdh.mddocs/

0

# ECDH Key Exchange

1

2

Elliptic Curve Diffie-Hellman (ECDH) key agreement protocol implementation for secure shared secret generation between parties. ECDH allows two parties to establish a shared secret over an insecure channel by exchanging public keys and combining them with their private keys.

3

4

## Capabilities

5

6

### ECDH Class

7

8

The ECDH class provides a complete implementation of the elliptic curve Diffie-Hellman key agreement protocol.

9

10

#### Initialization and Curve Management

11

12

```python { .api }

13

class ECDH:

14

def __init__(self, curve=None, private_key=None, public_key=None):

15

"""

16

Initialize ECDH instance.

17

18

Parameters:

19

- curve: Curve object, elliptic curve to use

20

- private_key: SigningKey object, existing private key

21

- public_key: VerifyingKey object, existing public key

22

"""

23

24

def set_curve(self, key_curve):

25

"""

26

Set the elliptic curve for ECDH operations.

27

28

Parameters:

29

- key_curve: Curve object, curve to use for operations

30

"""

31

```

32

33

#### Private Key Management

34

35

```python { .api }

36

def generate_private_key(self):

37

"""

38

Generate a new random private key and return corresponding public key.

39

40

Returns:

41

VerifyingKey object, the generated public key

42

43

Raises:

44

NoCurveError: if curve is not set

45

"""

46

47

def load_private_key(self, private_key):

48

"""

49

Load private key from SigningKey object.

50

51

Parameters:

52

- private_key: SigningKey object

53

54

Returns:

55

VerifyingKey object, corresponding public key

56

57

Raises:

58

InvalidCurveError: if key curve doesn't match set curve

59

"""

60

61

def load_private_key_bytes(self, private_key):

62

"""

63

Load private key from raw byte string.

64

65

Parameters:

66

- private_key: bytes, raw private key

67

68

Returns:

69

VerifyingKey object, corresponding public key

70

71

Raises:

72

NoCurveError: if curve is not set

73

"""

74

75

def load_private_key_der(self, private_key_der):

76

"""

77

Load private key from DER-encoded bytes.

78

79

Parameters:

80

- private_key_der: bytes, DER-encoded private key

81

82

Returns:

83

VerifyingKey object, corresponding public key

84

"""

85

86

def load_private_key_pem(self, private_key_pem):

87

"""

88

Load private key from PEM-encoded string or bytes.

89

90

Parameters:

91

- private_key_pem: str or bytes, PEM-encoded private key

92

93

Returns:

94

VerifyingKey object, corresponding public key

95

"""

96

97

def get_public_key(self):

98

"""

99

Get the current public key.

100

101

Returns:

102

VerifyingKey object, current public key

103

104

Raises:

105

NoKeyError: if no private key is loaded

106

"""

107

```

108

109

#### Remote Public Key Management

110

111

```python { .api }

112

def load_received_public_key(self, public_key):

113

"""

114

Load the other party's public key from VerifyingKey object.

115

116

Parameters:

117

- public_key: VerifyingKey object

118

119

Raises:

120

InvalidCurveError: if key curve doesn't match set curve

121

"""

122

123

def load_received_public_key_bytes(self, public_key_str, valid_encodings=None):

124

"""

125

Load the other party's public key from raw bytes.

126

127

Parameters:

128

- public_key_str: bytes, raw public key

129

- valid_encodings: list of str, acceptable point encodings or None

130

131

Raises:

132

NoCurveError: if curve is not set

133

"""

134

135

def load_received_public_key_der(self, public_key_der):

136

"""

137

Load the other party's public key from DER-encoded bytes.

138

139

Parameters:

140

- public_key_der: bytes, DER-encoded public key

141

"""

142

143

def load_received_public_key_pem(self, public_key_pem):

144

"""

145

Load the other party's public key from PEM-encoded string or bytes.

146

147

Parameters:

148

- public_key_pem: str or bytes, PEM-encoded public key

149

"""

150

```

151

152

#### Shared Secret Generation

153

154

```python { .api }

155

def generate_sharedsecret(self):

156

"""

157

Generate shared secret as integer.

158

159

Returns:

160

int, shared secret value

161

162

Raises:

163

NoKeyError: if private key or received public key not loaded

164

InvalidSharedSecretError: if computed secret is invalid (point at infinity)

165

"""

166

167

def generate_sharedsecret_bytes(self):

168

"""

169

Generate shared secret as byte string.

170

171

Returns:

172

bytes, shared secret as raw bytes

173

174

Raises:

175

NoKeyError: if private key or received public key not loaded

176

InvalidSharedSecretError: if computed secret is invalid (point at infinity)

177

"""

178

```

179

180

## Exception Classes

181

182

```python { .api }

183

class NoKeyError(Exception):

184

"""Raised when a required key is not set but needed for operation."""

185

186

class NoCurveError(Exception):

187

"""Raised when curve is not set but needed for operation."""

188

189

class InvalidCurveError(Exception):

190

"""Raised when public and private keys use different curves."""

191

192

class InvalidSharedSecretError(Exception):

193

"""Raised when shared secret computation results in point at infinity."""

194

```

195

196

## Usage Examples

197

198

### Basic ECDH Key Exchange

199

200

```python

201

from ecdsa import ECDH, NIST256p

202

203

# Party A setup

204

alice = ECDH(curve=NIST256p)

205

alice_public_key = alice.generate_private_key()

206

207

# Party B setup

208

bob = ECDH(curve=NIST256p)

209

bob_public_key = bob.generate_private_key()

210

211

# Exchange public keys (over insecure channel)

212

alice.load_received_public_key(bob_public_key)

213

bob.load_received_public_key(alice_public_key)

214

215

# Both parties compute the same shared secret

216

alice_secret = alice.generate_sharedsecret_bytes()

217

bob_secret = bob.generate_sharedsecret_bytes()

218

219

assert alice_secret == bob_secret

220

print(f"Shared secret established: {alice_secret.hex()}")

221

```

222

223

### ECDH with Existing Keys

224

225

```python

226

from ecdsa import ECDH, SigningKey, SECP256k1

227

228

# Use existing private keys

229

alice_private = SigningKey.generate(curve=SECP256k1)

230

bob_private = SigningKey.generate(curve=SECP256k1)

231

232

# Setup ECDH instances with existing keys

233

alice_ecdh = ECDH(curve=SECP256k1)

234

alice_public = alice_ecdh.load_private_key(alice_private)

235

236

bob_ecdh = ECDH(curve=SECP256k1)

237

bob_public = bob_ecdh.load_private_key(bob_private)

238

239

# Exchange public keys

240

alice_ecdh.load_received_public_key(bob_public)

241

bob_ecdh.load_received_public_key(alice_public)

242

243

# Generate shared secrets

244

alice_secret = alice_ecdh.generate_sharedsecret()

245

bob_secret = bob_ecdh.generate_sharedsecret()

246

247

assert alice_secret == bob_secret

248

```

249

250

### ECDH with Key Serialization

251

252

```python

253

from ecdsa import ECDH, NIST384p

254

255

# Alice generates key pair and exports public key

256

alice = ECDH(curve=NIST384p)

257

alice_public_key = alice.generate_private_key()

258

alice_public_pem = alice_public_key.to_pem()

259

260

# Bob generates key pair and exports public key

261

bob = ECDH(curve=NIST384p)

262

bob_public_key = bob.generate_private_key()

263

bob_public_pem = bob_public_key.to_pem()

264

265

# Exchange serialized public keys (e.g., over network)

266

# Alice loads Bob's public key from PEM

267

alice.load_received_public_key_pem(bob_public_pem)

268

269

# Bob loads Alice's public key from PEM

270

bob.load_received_public_key_pem(alice_public_pem)

271

272

# Both parties generate the same shared secret

273

alice_secret = alice.generate_sharedsecret_bytes()

274

bob_secret = bob.generate_sharedsecret_bytes()

275

276

assert alice_secret == bob_secret

277

print(f"ECDH key exchange completed successfully")

278

```

279

280

### Error Handling

281

282

```python

283

from ecdsa import ECDH, NIST256p, NoKeyError, NoCurveError, InvalidCurveError

284

285

try:

286

# Attempt to generate key without setting curve

287

ecdh = ECDH()

288

public_key = ecdh.generate_private_key() # Raises NoCurveError

289

except NoCurveError:

290

print("Must set curve before generating keys")

291

292

try:

293

# Attempt to generate shared secret without loading received key

294

ecdh = ECDH(curve=NIST256p)

295

ecdh.generate_private_key()

296

secret = ecdh.generate_sharedsecret() # Raises NoKeyError

297

except NoKeyError:

298

print("Must load received public key before generating shared secret")

299

300

try:

301

# Attempt to use keys from different curves

302

from ecdsa import SECP256k1

303

alice = ECDH(curve=NIST256p)

304

alice_public = alice.generate_private_key()

305

306

bob = ECDH(curve=SECP256k1)

307

bob_public = bob.generate_private_key()

308

309

alice.load_received_public_key(bob_public) # Raises InvalidCurveError

310

except InvalidCurveError:

311

print("Both parties must use the same curve")

312

```

313

314

### Different Curves Support

315

316

```python

317

from ecdsa import ECDH, SECP256k1, Ed25519, BRAINPOOLP384r1

318

319

# Bitcoin's secp256k1 curve

320

bitcoin_ecdh = ECDH(curve=SECP256k1)

321

bitcoin_public = bitcoin_ecdh.generate_private_key()

322

323

# Edwards curve (note: Ed25519 typically used for EdDSA, not ECDH)

324

# Standard ECDH is usually done with Weierstrass curves

325

edwards_ecdh = ECDH(curve=Ed25519)

326

edwards_public = edwards_ecdh.generate_private_key()

327

328

# Brainpool curve

329

brainpool_ecdh = ECDH(curve=BRAINPOOLP384r1)

330

brainpool_public = brainpool_ecdh.generate_private_key()

331

332

print(f"Generated keys for different curves:")

333

print(f"- Bitcoin secp256k1: {len(bitcoin_public.to_string())} bytes")

334

print(f"- Edwards Ed25519: {len(edwards_public.to_string())} bytes")

335

print(f"- Brainpool P384r1: {len(brainpool_public.to_string())} bytes")

336

```