Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

r""" 

Blum-Goldwasser Probabilistic Encryption 

 

The Blum-Goldwasser probabilistic public-key encryption scheme. This scheme 

was originally described in [BG1985]_. See also section 8.7.2 

of [MvOV1996]_ and the :wikipedia:`Blum-Goldwasser_cryptosystem` 

on this scheme. 

 

AUTHORS: 

 

- Mike Hogan and David Joyner (2009-9-19): initial procedural version 

released as public domain software. 

 

- Minh Van Nguyen (2009-12): integrate into Sage as a class and relicense 

under the GPLv2+. Complete rewrite of the original version to follow 

the description contained in [MvOV1996]_. 

""" 

 

#***************************************************************************** 

# Copyright (c) 2009, 2010 Mike Hogan 

# Copyright (c) 2009, 2010 David Joyner <wdjoyner@gmail.com> 

# Copyright (c) 2009, 2010 Minh Van Nguyen <nguyenminh2@gmail.com> 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

from six.moves import range 

 

from operator import xor 

 

from sage.crypto.cryptosystem import PublicKeyCryptosystem 

from sage.crypto.util import is_blum_prime 

from sage.crypto.util import least_significant_bits 

from sage.crypto.util import random_blum_prime 

from sage.functions.log import log 

from sage.functions.other import Function_floor 

from sage.monoids.string_monoid import BinaryStrings 

from sage.arith.all import gcd, power_mod, xgcd 

from sage.rings.finite_rings.integer_mod import Mod as mod 

from sage.rings.finite_rings.integer_mod_ring import IntegerModFactory 

 

floor = Function_floor() 

IntegerModRing = IntegerModFactory("IntegerModRing") 

 

class BlumGoldwasser(PublicKeyCryptosystem): 

r""" 

The Blum-Goldwasser probabilistic public-key encryption scheme. 

 

The Blum-Goldwasser encryption and decryption algorithms as described in 

:func:`encrypt() <BlumGoldwasser.encrypt>` and 

:func:`decrypt() <BlumGoldwasser.decrypt>`, respectively, make use of the 

least significant bit of a binary string. A related concept is the `k` 

least significant bits of a binary string. For example, given a positive 

integer `n`, let `b = b_0 b_1 \cdots b_{m-1}` be the binary representation 

of `n` so that `b` is a binary string of length `m`. Then the least 

significant bit of `n` is `b_{m-1}`. If `0 < k \leq m`, then the `k` least 

significant bits of `n` are `b_{m-1-k} b_{m-k} \cdots b_{m-1}`. The least 

significant bit of an integer is also referred to as its parity bit, 

because this bit determines whether the integer is even or odd. In the 

following example, we obtain the least significant bit of an integer:: 

 

sage: n = 123 

sage: b = n.binary(); b 

'1111011' 

sage: n % 2 

1 

sage: b[-1] 

'1' 

 

Now find the 4 least significant bits of the integer `n = 123`:: 

 

sage: b 

'1111011' 

sage: b[-4:] 

'1011' 

 

The last two examples could be worked through as follows:: 

 

sage: from sage.crypto.util import least_significant_bits 

sage: least_significant_bits(123, 1) 

[1] 

sage: least_significant_bits(123, 4) 

[1, 0, 1, 1] 

 

EXAMPLES: 

 

The following encryption/decryption example is taken from Example 8.57, 

pages 309--310 of [MvOV1996]_:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser(); bg 

The Blum-Goldwasser public-key encryption scheme. 

sage: p = 499; q = 547 

sage: pubkey = bg.public_key(p, q); pubkey 

272953 

sage: prikey = bg.private_key(p, q); prikey 

(499, 547, -57, 52) 

sage: P = "10011100000100001100" 

sage: C = bg.encrypt(P, pubkey, seed=159201); C 

([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) 

sage: M = bg.decrypt(C, prikey); M 

[[1, 0, 0, 1], [1, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]] 

sage: M = "".join(str(x) for x in flatten(M)); M 

'10011100000100001100' 

sage: M == P 

True 

 

Generate a pair of random public/private keys. Use the public key to 

encrypt a plaintext. Then decrypt the resulting ciphertext using the 

private key. Finally, compare the decrypted message with the original 

plaintext. :: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: from sage.crypto.util import bin_to_ascii 

sage: bg = BlumGoldwasser() 

sage: pubkey, prikey = bg.random_key(10**4, 10**6) 

sage: P = "A fixed plaintext." 

sage: C = bg.encrypt(P, pubkey) 

sage: M = bg.decrypt(C, prikey) 

sage: bin_to_ascii(flatten(M)) == P 

True 

 

If `(p, q, a, b)` is a private key, then `n = pq` is the corresponding 

public key. Furthermore, we have `\gcd(p, q) = ap + bq = 1`. :: 

 

sage: p, q, a, b = prikey 

sage: pubkey == p * q 

True 

sage: gcd(p, q) == a*p + b*q == 1 

True 

""" 

 

def __init__(self): 

""" 

Construct the Blum-Goldwasser public-key encryption scheme. 

 

OUTPUT: 

 

- A ``BlumGoldwasser`` object representing the Blum-Goldwasser 

public-key encryption scheme. 

 

See the class docstring of ``BlumGoldwasser`` for detailed 

documentation. 

 

EXAMPLES:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: bg == loads(dumps(bg)) 

True 

""" 

# no internal data for now; nothing to initialize 

pass 

 

def __eq__(self, other): 

""" 

Compare this ``BlumGoldwasser`` object with ``other``. 

 

INPUT: 

 

- ``other`` -- a ``BlumGoldwasser`` object. 

 

OUTPUT: 

 

- ``True`` if both ``self`` and ``other`` are ``BlumGoldwasser`` 

objects. ``False`` otherwise. 

 

Two objects are ``BlumGoldwasser`` objects if their string 

representations are the same. 

 

EXAMPLES:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg1 = BlumGoldwasser() 

sage: bg2 = BlumGoldwasser() 

sage: bg1 == bg2 

True 

""" 

return repr(self) == repr(other) 

 

def __repr__(self): 

""" 

A string representation of this Blum-Goldwasser public-key encryption 

scheme. 

 

OUTPUT: 

 

- A string representation of this Blum-Goldwasser public-key 

encryption scheme. 

 

EXAMPLES:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: BlumGoldwasser() 

The Blum-Goldwasser public-key encryption scheme. 

""" 

return "The Blum-Goldwasser public-key encryption scheme." 

 

def decrypt(self, C, K): 

r""" 

Apply the Blum-Goldwasser scheme to decrypt the ciphertext ``C`` 

using the private key ``K``. 

 

INPUT: 

 

- ``C`` -- a ciphertext resulting from encrypting a plaintext using 

the Blum-Goldwasser encryption algorithm. The ciphertext `C` must 

be of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` 

is a sub-block of binary string and `x_{t+1}` is the result of the 

`t+1`-th iteration of the Blum-Blum-Shub algorithm. 

 

- ``K`` -- a private key `(p, q, a, b)` where `p` and `q` are 

distinct Blum primes and `\gcd(p, q) = ap + bq = 1`. 

 

OUTPUT: 

 

- The plaintext resulting from decrypting the ciphertext ``C`` using 

the Blum-Goldwasser decryption algorithm. 

 

ALGORITHM: 

 

The Blum-Goldwasser decryption algorithm is described in Algorithm 

8.56, page 309 of [MvOV1996]_. The algorithm works as follows: 

 

#. Let `C` be the ciphertext `C = (c_1, c_2, \dots, c_t, x_{t+1})`. 

Then `t` is the number of ciphertext sub-blocks and `h` is the 

length of each binary string sub-block `c_i`. 

#. Let `(p, q, a, b)` be the private key whose corresponding 

public key is `n = pq`. Note that `\gcd(p, q) = ap + bq = 1`. 

#. Compute `d_1 = ((p + 1) / 4)^{t+1} \bmod{(p - 1)}`. 

#. Compute `d_2 = ((q + 1) / 4)^{t+1} \bmod{(q - 1)}`. 

#. Let `u = x_{t+1}^{d_1} \bmod p`. 

#. Let `v = x_{t+1}^{d_2} \bmod q`. 

#. Compute `x_0 = vap + ubq \bmod n`. 

#. For `i` from 1 to `t`, do: 

 

#. Compute `x_i = x_{t-1}^2 \bmod n`. 

#. Let `p_i` be the `h` least significant bits of `x_i`. 

#. Compute `m_i = p_i \oplus c_i`. 

 

#. The plaintext is `m = m_1 m_2 \cdots m_t`. 

 

EXAMPLES: 

 

The following decryption example is taken from Example 8.57, pages 

309--310 of [MvOV1996]_. Here we decrypt a binary string:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: p = 499; q = 547 

sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) 

sage: K = bg.private_key(p, q); K 

(499, 547, -57, 52) 

sage: P = bg.decrypt(C, K); P 

[[1, 0, 0, 1], [1, 1, 0, 0], [0, 0, 0, 1], [0, 0, 0, 0], [1, 1, 0, 0]] 

 

Convert the plaintext sub-blocks into a binary string:: 

 

sage: bin = BinaryStrings() 

sage: bin(flatten(P)) 

10011100000100001100 

 

Decrypt a longer ciphertext and convert the resulting plaintext 

into an ASCII string:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: from sage.crypto.util import bin_to_ascii 

sage: bg = BlumGoldwasser() 

sage: p = 78307; q = 412487 

sage: K = bg.private_key(p, q) 

sage: C = ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ 

....: [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ 

....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ 

....: [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ 

....: [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ 

....: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ 

....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ 

....: [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ 

....: [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ 

....: [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ 

....: [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ 

....: [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ 

....: [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) 

sage: P = bg.decrypt(C, K) 

sage: bin_to_ascii(flatten(P)) 

'Blum-Goldwasser encryption' 

 

TESTS: 

 

The private key `K = (p, q, a, b)` must be such that `p` and `q` are 

distinct Blum primes. Even if `p` and `q` pass this criterion, they 

must also satisfy the requirement `\gcd(p, q) = ap + bq = 1`. :: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: C = ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) 

sage: K = (7, 7, 1, 2) 

sage: bg.decrypt(C, K) 

Traceback (most recent call last): 

... 

ValueError: p and q must be distinct Blum primes. 

sage: K = (7, 23, 1, 2) 

sage: bg.decrypt(C, K) 

Traceback (most recent call last): 

... 

ValueError: a and b must satisfy gcd(p, q) = ap + bq = 1. 

sage: K = (11, 29, 8, -3) 

sage: bg.decrypt(C, K) 

Traceback (most recent call last): 

... 

ValueError: p and q must be distinct Blum primes. 

""" 

# ciphertext 

c = C[0] 

xt1 = C[-1] 

# number of ciphertext sub-blocks 

t = len(c) 

# length of each ciphertext sub-block 

h = len(c[0]) 

# private key 

p, q, a, b = K 

# public key 

n = p * q 

# sanity checks 

if p == q: 

raise ValueError("p and q must be distinct Blum primes.") 

if (a*p + b*q) != 1: 

raise ValueError("a and b must satisfy gcd(p, q) = ap + bq = 1.") 

if (not is_blum_prime(p)) or (not is_blum_prime(q)): 

raise ValueError("p and q must be distinct Blum primes.") 

# prepare to decrypt 

d1 = power_mod((p + 1) // 4, t + 1, p - 1) 

d2 = power_mod((q + 1) // 4, t + 1, q - 1) 

u = power_mod(xt1, d1, p) 

v = power_mod(xt1, d2, q) 

x0 = mod(v*a*p + u*b*q, n).lift() 

# perform the decryption 

M = [] 

for i in range(t): 

x1 = power_mod(x0, 2, n) 

p = least_significant_bits(x1, h) 

M.append(list(map(xor, p, c[i]))) 

x0 = x1 

return M 

 

def encrypt(self, P, K, seed=None): 

r""" 

Apply the Blum-Goldwasser scheme to encrypt the plaintext ``P`` using 

the public key ``K``. 

 

INPUT: 

 

- ``P`` -- a non-empty string of plaintext. The string ``""`` is 

an empty string, whereas ``" "`` is a string consisting of one 

white space character. The plaintext can be a binary string or 

a string of ASCII characters. Where ``P`` is an ASCII string, then 

``P`` is first encoded as a binary string prior to encryption. 

 

- ``K`` -- a public key, which is the product of two Blum primes. 

 

- ``seed`` -- (default: ``None``) if `p` and `q` are Blum primes and 

`n = pq` is a public key, then ``seed`` is a quadratic residue in 

the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``, 

then the function would generate its own random quadratic residue 

in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided, 

it is your responsibility to ensure that the seed is a 

quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. 

 

OUTPUT: 

 

- The ciphertext resulting from encrypting ``P`` using the public 

key ``K``. The ciphertext `C` is of the form 

`C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a 

sub-block of binary string and `x_{t+1}` is the result of the 

`t+1`-th iteration of the Blum-Blum-Shub algorithm. 

 

ALGORITHM: 

 

The Blum-Goldwasser encryption algorithm is described in Algorithm 

8.56, page 309 of [MvOV1996]_. The algorithm works as follows: 

 

#. Let `n` be a public key, where `n = pq` is the product of two 

distinct Blum primes `p` and `q`. 

#. Let `k = \lfloor \log_2(n) \rfloor` and 

`h = \lfloor \log_2(k) \rfloor`. 

#. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where 

each `m_i` is a binary string of length `h`. 

#. Choose a random seed `x_0`, which is a quadratic residue in 

the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose 

a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute 

`x_0 = r^2 \bmod n`. 

#. For `i` from 1 to `t`, do: 

 

#. Let `x_i = x_{i-1}^2 \bmod n`. 

#. Let `p_i` be the `h` least significant bits of `x_i`. 

#. Let `c_i = p_i \oplus m_i`. 

 

#. Compute `x_{t+1} = x_t^2 \bmod n`. 

#. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`. 

 

The value `h` in the algorithm is the sub-block length. If the 

binary string representing the message cannot be divided into blocks 

of length `h` each, then other sub-block lengths would be used 

instead. The sub-block lengths to fall back on are in the 

following order: 16, 8, 4, 2, 1. 

 

EXAMPLES: 

 

The following encryption example is taken from Example 8.57, 

pages 309--310 of [MvOV1996]_. Here, we encrypt a binary 

string:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: p = 499; q = 547; n = p * q 

sage: P = "10011100000100001100" 

sage: C = bg.encrypt(P, n, seed=159201); C 

([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) 

 

Convert the ciphertext sub-blocks into a binary string:: 

 

sage: bin = BinaryStrings() 

sage: bin(flatten(C[0])) 

00100000110011100100 

 

Now encrypt an ASCII string. The result is random; no seed is 

provided to the encryption function so the function generates its 

own random seed:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: K = 32300619509 

sage: P = "Blum-Goldwasser encryption" 

sage: bg.encrypt(P, K) # random 

([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ 

[1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ 

[0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ 

[0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ 

[1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ 

[0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ 

[1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ 

[1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ 

[0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ 

[1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ 

[1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ 

[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ 

[0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) 

 

TESTS: 

 

The plaintext cannot be an empty string. :: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: bg.encrypt("", 3) 

Traceback (most recent call last): 

... 

ValueError: The plaintext cannot be an empty string. 

""" 

# sanity check 

if P == "": 

raise ValueError("The plaintext cannot be an empty string.") 

n = K 

k = floor(log(n, base=2)) 

h = floor(log(k, base=2)) 

bin = BinaryStrings() 

M = "" 

try: 

# the plaintext is a binary string 

M = bin(P) 

except TypeError: 

# encode the plaintext as a binary string 

# An exception might be raised here if P cannot be encoded as a 

# binary string. 

M = bin.encoding(P) 

# the number of plaintext sub-blocks; each sub-block has length h 

t = 0 

try: 

# Attempt to use t and h values from the algorithm described 

# in [MvOV1996]. 

t = len(M) / h 

# If the following raises an exception, then we can't use 

# the t and h values specified by [MvOV1996]. 

mod(len(M), t) 

# fall back to using other sub-block lengths 

except TypeError: 

# sub-blocks of length h = 16 

if mod(len(M), 16) == 0: 

h = 16 

t = len(M) // h 

# sub-blocks of length h = 8 

elif mod(len(M), 8) == 0: 

h = 8 

t = len(M) // h 

# sub-blocks of length h = 4 

elif mod(len(M), 4) == 0: 

h = 4 

t = len(M) // h 

# sub-blocks of length h = 2 

elif mod(len(M), 2) == 0: 

h = 2 

t = len(M) // h 

# sub-blocks of length h = 1 

else: 

h = 1 

t = len(M) // h 

# If no seed is provided, select a random seed. 

x0 = seed 

if seed is None: 

zmod = IntegerModRing(n) # K = n = pq 

r = zmod.random_element().lift() 

while gcd(r, n) != 1: 

r = zmod.random_element().lift() 

x0 = power_mod(r, 2, n) 

# perform the encryption 

to_int = lambda x: int(str(x)) 

C = [] 

for i in range(t): 

x1 = power_mod(x0, 2, n) 

p = least_significant_bits(x1, h) 

# xor p with a sub-block of length h. There are t sub-blocks of 

# length h each. 

C.append(list(map(xor, p, [to_int(_) for _ in M[i*h : (i+1)*h]]))) 

x0 = x1 

x1 = power_mod(x0, 2, n) 

return (C, x1) 

 

def private_key(self, p, q): 

r""" 

Return the Blum-Goldwasser private key corresponding to the 

distinct Blum primes ``p`` and ``q``. 

 

INPUT: 

 

- ``p`` -- a Blum prime. 

 

- ``q`` -- a Blum prime. 

 

OUTPUT: 

 

- The Blum-Goldwasser private key `(p, q, a, b)` where 

`\gcd(p, q) = ap + bq = 1`. 

 

Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a 

positive prime. Then `p` is a Blum prime if `p` is congruent to 3 

modulo 4, i.e. `p \equiv 3 \pmod{4}`. 

 

EXAMPLES: 

 

Obtain two distinct Blum primes and compute the Blum-Goldwasser 

private key corresponding to those two Blum primes:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: from sage.crypto.util import is_blum_prime 

sage: bg = BlumGoldwasser() 

sage: P = primes_first_n(10); P 

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 

sage: [is_blum_prime(_) for _ in P] 

[False, True, False, True, True, False, False, True, True, False] 

sage: bg.private_key(19, 23) 

(19, 23, -6, 5) 

 

Choose two distinct random Blum primes, compute the Blum-Goldwasser 

private key corresponding to those two primes, and test that the 

resulting private key `(p, q, a, b)` satisfies 

`\gcd(p, q) = ap + bq = 1`:: 

 

sage: from sage.crypto.util import random_blum_prime 

sage: p = random_blum_prime(10**4, 10**5) 

sage: q = random_blum_prime(10**4, 10**5) 

sage: while q == p: 

....: q = random_blum_prime(10**4, 10**5) 

sage: p, q, a, b = bg.private_key(p, q) 

sage: gcd(p, q) == a*p + b*q == 1 

True 

 

TESTS: 

 

Both of the input ``p`` and ``q`` must be distinct Blum primes. :: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: bg.private_key(78307, 78307) 

Traceback (most recent call last): 

... 

ValueError: p and q must be distinct Blum primes. 

sage: bg.private_key(7, 4) 

Traceback (most recent call last): 

... 

ValueError: p and q must be distinct Blum primes. 

""" 

if p == q: 

raise ValueError("p and q must be distinct Blum primes.") 

if is_blum_prime(p) and is_blum_prime(q): 

# here gcd(p, q) = ap + bq = 1 

bezout = xgcd(p, q) 

a = bezout[1] 

b = bezout[2] 

return (p, q, a, b) 

else: 

raise ValueError("p and q must be distinct Blum primes.") 

 

def public_key(self, p, q): 

r""" 

Return the Blum-Goldwasser public key corresponding to the 

distinct Blum primes ``p`` and ``q``. 

 

INPUT: 

 

- ``p`` -- a Blum prime. 

 

- ``q`` -- a Blum prime. 

 

OUTPUT: 

 

- The Blum-Goldwasser public key `n = pq`. 

 

Both ``p`` and ``q`` must be distinct Blum primes. Let `p` be a 

positive prime. Then `p` is a Blum prime if `p` is congruent to 3 

modulo 4, i.e. `p \equiv 3 \pmod{4}`. 

 

EXAMPLES: 

 

Obtain two distinct Blum primes and compute the Blum-Goldwasser 

public key corresponding to those two Blum primes:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: from sage.crypto.util import is_blum_prime 

sage: bg = BlumGoldwasser() 

sage: P = primes_first_n(10); P 

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 

sage: [is_blum_prime(_) for _ in P] 

[False, True, False, True, True, False, False, True, True, False] 

sage: bg.public_key(3, 7) 

21 

 

Choose two distinct random Blum primes, compute the Blum-Goldwasser 

public key corresponding to those two primes, and test that the 

public key factorizes into Blum primes:: 

 

sage: from sage.crypto.util import random_blum_prime 

sage: p = random_blum_prime(10**4, 10**5) 

sage: q = random_blum_prime(10**4, 10**5) 

sage: while q == p: 

....: q = random_blum_prime(10**4, 10**5) 

... 

sage: n = bg.public_key(p, q) 

sage: f = factor(n) 

sage: is_blum_prime(f[0][0]); is_blum_prime(f[1][0]) 

True 

True 

sage: p * q == f[0][0] * f[1][0] 

True 

 

TESTS: 

 

The input ``p`` and ``q`` must be distinct Blum primes. :: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: bg.public_key(3, 3) 

Traceback (most recent call last): 

... 

ValueError: p and q must be distinct Blum primes. 

sage: bg.public_key(23, 29) 

Traceback (most recent call last): 

... 

ValueError: p and q must be distinct Blum primes. 

""" 

if p == q: 

raise ValueError("p and q must be distinct Blum primes.") 

if is_blum_prime(p) and is_blum_prime(q): 

return p * q 

else: 

raise ValueError("p and q must be distinct Blum primes.") 

 

def random_key(self, lbound, ubound, ntries=100): 

r""" 

Return a pair of random public and private keys. 

 

INPUT: 

 

- ``lbound`` -- positive integer; the lower bound on how small each 

random Blum prime `p` and `q` can be. So we have 

``0 < lower_bound <= p, q <= upper_bound``. The lower bound must 

be distinct from the upper bound. 

 

- ``ubound`` -- positive integer; the upper bound on how large each 

random Blum prime `p` and `q` can be. So we have 

``0 < lower_bound <= p, q <= upper_bound``. The lower bound must 

be distinct from the upper bound. 

 

- ``ntries`` -- (default: ``100``) the number of attempts to generate 

a random public/private key pair. If ``ntries`` is a positive 

integer, then perform that many attempts at generating a random 

public/private key pair. 

 

OUTPUT: 

 

- A random public key and its corresponding private key. Each 

randomly chosen `p` and `q` are guaranteed to be Blum primes. The 

public key is `n = pq`, and the private key is `(p, q, a, b)` where 

`\gcd(p, q) = ap + bq = 1`. 

 

ALGORITHM: 

 

The key generation algorithm is described in Algorithm 8.55, 

page 308 of [MvOV1996]_. The algorithm works as follows: 

 

#. Let `p` and `q` be distinct large random primes, each congruent 

to 3 modulo 4. That is, `p` and `q` are Blum primes. 

#. Let `n = pq` be the product of `p` and `q`. 

#. Use the extended Euclidean algorithm to compute integers `a` and 

`b` such that `\gcd(p, q) = ap + bq = 1`. 

#. The public key is `n` and the corresponding private key is 

`(p, q, a, b)`. 

 

.. NOTE:: 

 

Beware that there might not be any primes between the lower and 

upper bounds. So make sure that these two bounds are 

"sufficiently" far apart from each other for there to be primes 

congruent to 3 modulo 4. In particular, there should 

be at least two distinct primes within these bounds, each prime 

being congruent to 3 modulo 4. 

 

EXAMPLES: 

 

Choosing a random pair of public and private keys. We then test to see 

if they satisfy the requirements of the Blum-Goldwasser scheme:: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: from sage.crypto.util import is_blum_prime 

sage: bg = BlumGoldwasser() 

sage: pubkey, prikey = bg.random_key(10**4, 10**5) 

sage: p, q, a, b = prikey 

sage: is_blum_prime(p); is_blum_prime(q) 

True 

True 

sage: p == q 

False 

sage: pubkey == p*q 

True 

sage: gcd(p, q) == a*p + b*q == 1 

True 

 

TESTS: 

 

Make sure that there is at least one Blum prime between the lower and 

upper bounds. In the following example, we have ``lbound=24`` and 

``ubound=30`` with 29 being the only prime within those bounds. But 

29 is not a Blum prime. :: 

 

sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser 

sage: bg = BlumGoldwasser() 

sage: pubkey, privkey = bg.random_key(24, 30) 

Traceback (most recent call last): 

... 

ValueError: No Blum primes within the specified closed interval. 

""" 

# choosing distinct random Blum primes 

p = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) 

q = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) 

while p == q: 

q = random_blum_prime(lbound=lbound, ubound=ubound, ntries=ntries) 

# compute the public key 

n = p * q 

# compute the private key; here gcd(p, q) = 1 = a*p + b*q 

bezout = xgcd(p, q) 

a = bezout[1] 

b = bezout[2] 

return (n, (p, q, a, b))