or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

encryption-decryption.mdgpg-instance.mdindex.mdkey-discovery.mdkey-management.mdkeyserver-operations.mdsigning-verification.md

key-discovery.mddocs/

0

# Key Discovery and Analysis

1

2

Advanced key discovery, scanning, and analysis operations for examining keys without importing them, understanding key structures, and managing trust relationships.

3

4

## Capabilities

5

6

### Key Scanning Without Import

7

8

Examine key files and data without importing them into the keyring, allowing for key analysis and validation before commitment.

9

10

```python { .api }

11

def scan_keys(self, filename):

12

"""

13

Scan keys from a file without importing them to the keyring.

14

15

Parameters:

16

- filename (str): Path to key file to scan

17

18

Returns:

19

ScanKeys: List of key information found in the file

20

"""

21

22

def scan_keys_mem(self, key_data):

23

"""

24

Scan keys from memory without importing them to the keyring.

25

26

Parameters:

27

- key_data (str): ASCII-armored or binary key data to scan

28

29

Returns:

30

ScanKeys: List of key information found in the data

31

"""

32

```

33

34

### Trust Management

35

36

Manage trust levels in the web of trust system for establishing key authenticity and reliability.

37

38

```python { .api }

39

def trust_keys(self, fingerprints, trustlevel):

40

"""

41

Set trust level for one or more keys in the web of trust.

42

43

Parameters:

44

- fingerprints (str|list): Key fingerprints to set trust for

45

- trustlevel (str): Trust level to assign

46

'1' = I don't know or won't say

47

'2' = I do NOT trust

48

'3' = I trust marginally

49

'4' = I trust fully

50

'5' = I trust ultimately (own keys)

51

52

Returns:

53

TrustResult: Result with trust operation status

54

"""

55

```

56

57

### Advanced Key Operations

58

59

Perform specialized key operations including subkey management and key relationship analysis.

60

61

```python { .api }

62

def add_subkey(self, master_key, master_passphrase=None, algorithm='rsa',

63

usage='encrypt', expire='-'):

64

"""

65

Add a subkey to an existing master key.

66

67

Parameters:

68

- master_key (str): Master key fingerprint or ID

69

- master_passphrase (str): Master key passphrase

70

- algorithm (str): Subkey algorithm ('rsa', 'dsa', 'ecdsa', 'eddsa')

71

- usage (str): Subkey usage ('encrypt', 'sign', 'auth')

72

- expire (str): Expiration ('-' for same as master, '0' for no expiration)

73

74

Returns:

75

AddSubkey: Result with new subkey information

76

"""

77

```

78

79

## Result Types

80

81

```python { .api }

82

class ScanKeys(ListKeys):

83

# Inherits from ListKeys - same structure but for scanned keys

84

# List of key dictionaries containing detailed key information:

85

# - type: Key type ('pub', 'sec', 'sub', 'ssb')

86

# - length: Key length in bits

87

# - algo: Algorithm number

88

# - keyid: Key ID (short form)

89

# - date: Creation date

90

# - expires: Expiration date (if any)

91

# - dummy: Dummy key indicator

92

# - ownertrust: Owner trust level

93

# - sig_class: Signature class

94

# - capabilities: Key capabilities string

95

# - issuer: Key issuer

96

# - flag: Key flags

97

# - token: Token serial number

98

# - hash_algo: Hash algorithm number

99

# - curve: Curve name for ECC keys

100

# - fingerprint: Full key fingerprint

101

# - uids: List of user IDs associated with the key

102

# - sigs: List of signatures on the key (if requested)

103

# - subkeys: List of subkeys under this master key

104

pass

105

106

class TrustResult(StatusHandler):

107

# Result of trust level setting operations

108

pass

109

110

class AddSubkey(StatusHandler):

111

type: str # Subkey type/algorithm

112

fingerprint: str # New subkey fingerprint

113

```

114

115

## Usage Examples

116

117

### Key File Analysis

118

119

```python

120

import gnupg

121

122

gpg = gnupg.GPG()

123

124

# Scan a key file without importing

125

scanned_keys = gpg.scan_keys('/path/to/keyfile.asc')

126

127

print(f"Found {len(scanned_keys)} keys in file:")

128

for key in scanned_keys:

129

print(f"\nKey ID: {key['keyid']}")

130

print(f"Fingerprint: {key['fingerprint']}")

131

print(f"Algorithm: {key['algo']} ({key['length']} bits)")

132

print(f"Created: {key['date']}")

133

if key['expires']:

134

print(f"Expires: {key['expires']}")

135

136

print(f"User IDs:")

137

for uid in key['uids']:

138

print(f" - {uid}")

139

140

print(f"Capabilities: {key['capabilities']}")

141

142

if key['subkeys']:

143

print(f"Subkeys:")

144

for subkey in key['subkeys']:

145

print(f" - {subkey['keyid']} ({subkey['length']} bits, {subkey['capabilities']})")

146

```

147

148

### Key Data Analysis from Memory

149

150

```python

151

# Analyze key data received over network or from database

152

key_data = """-----BEGIN PGP PUBLIC KEY BLOCK-----

153

154

mQENBF... (key data here) ...

155

-----END PGP PUBLIC KEY BLOCK-----"""

156

157

scanned = gpg.scan_keys_mem(key_data)

158

159

def analyze_key_strength(key_info):

160

"""Analyze key strength and provide recommendations."""

161

162

analysis = {

163

'strength': 'unknown',

164

'recommendations': [],

165

'warnings': []

166

}

167

168

# Check key length

169

length = key_info.get('length', 0)

170

algo = key_info.get('algo', 0)

171

172

if algo == 1: # RSA

173

if length < 2048:

174

analysis['strength'] = 'weak'

175

analysis['warnings'].append(f'RSA key length {length} is too short')

176

analysis['recommendations'].append('Use RSA 2048 bits or higher')

177

elif length < 3072:

178

analysis['strength'] = 'acceptable'

179

else:

180

analysis['strength'] = 'strong'

181

elif algo in [16, 20]: # ElGamal

182

if length < 2048:

183

analysis['strength'] = 'weak'

184

analysis['warnings'].append(f'ElGamal key length {length} is too short')

185

elif algo == 17: # DSA

186

if length < 2048:

187

analysis['strength'] = 'weak'

188

analysis['warnings'].append('DSA-1024 is deprecated')

189

analysis['recommendations'].append('Use RSA-2048 or higher')

190

191

# Check expiration

192

if not key_info.get('expires'):

193

analysis['recommendations'].append('Consider setting an expiration date')

194

else:

195

from datetime import datetime

196

try:

197

exp_date = datetime.strptime(key_info['expires'], '%Y-%m-%d')

198

if exp_date < datetime.now():

199

analysis['warnings'].append('Key has expired')

200

except:

201

pass

202

203

return analysis

204

205

# Analyze each scanned key

206

for key in scanned:

207

analysis = analyze_key_strength(key)

208

print(f"\nKey {key['keyid']} analysis:")

209

print(f"Strength: {analysis['strength']}")

210

211

for warning in analysis['warnings']:

212

print(f"⚠️ Warning: {warning}")

213

214

for rec in analysis['recommendations']:

215

print(f"💡 Recommendation: {rec}")

216

```

217

218

### Trust Management

219

220

```python

221

# Set trust levels for keys

222

def setup_trust_relationships(gpg_instance):

223

"""Set up trust relationships for a set of known keys."""

224

225

trust_assignments = {

226

# Ultimate trust (own keys)

227

'5': ['ABCD1234DEADBEEF5678CAFE'], # Your own key

228

229

# Full trust (fully trusted colleagues)

230

'4': [

231

'BEEF5678CAFE1234DEAD9876', # Alice's key

232

'CAFE1234DEAD5678BEEF9876', # Bob's key

233

],

234

235

# Marginal trust (somewhat trusted)

236

'3': [

237

'DEAD5678BEEF1234CAFE9876', # Charlie's key

238

],

239

240

# No trust

241

'2': [

242

'BADD0G15DEAD5678BEEF1234', # Revoked or compromised key

243

]

244

}

245

246

for trust_level, fingerprints in trust_assignments.items():

247

if fingerprints:

248

result = gpg_instance.trust_keys(fingerprints, trust_level)

249

if result.status == 'success':

250

level_names = {'2': 'no trust', '3': 'marginal', '4': 'full', '5': 'ultimate'}

251

print(f"Set {level_names.get(trust_level, trust_level)} trust for {len(fingerprints)} keys")

252

253

# Apply trust settings

254

setup_trust_relationships(gpg)

255

256

# Check current trust levels by listing keys

257

keys = gpg.list_keys()

258

for key in keys:

259

trust = key.get('ownertrust', 'unknown')

260

print(f"Key {key['keyid']}: trust level {trust}")

261

```

262

263

### Subkey Management

264

265

```python

266

# Add encryption subkey to existing signing key

267

master_fingerprint = 'DEADBEEFCAFEBABE1234567890ABCDEF12345678'

268

269

# Add RSA encryption subkey

270

result = gpg.add_subkey(

271

master_key=master_fingerprint,

272

master_passphrase='master_key_passphrase',

273

algorithm='rsa',

274

usage='encrypt'

275

)

276

277

if result.fingerprint:

278

print(f"Added encryption subkey: {result.fingerprint}")

279

else:

280

print(f"Failed to add subkey: {result.status}")

281

282

# Add authentication subkey for SSH usage

283

auth_result = gpg.add_subkey(

284

master_key=master_fingerprint,

285

master_passphrase='master_key_passphrase',

286

algorithm='rsa',

287

usage='auth',

288

expire='2y' # Expire in 2 years

289

)

290

291

# Add ECC subkey for modern cryptography

292

ecc_result = gpg.add_subkey(

293

master_key=master_fingerprint,

294

master_passphrase='master_key_passphrase',

295

algorithm='ecdsa',

296

usage='sign'

297

)

298

```

299

300

### Key Validation and Health Checks

301

302

```python

303

def validate_key_health(gpg_instance, key_identifier):

304

"""Comprehensive key health check and validation."""

305

306

# Get detailed key information

307

keys = gpg_instance.list_keys(keys=[key_identifier])

308

if not keys:

309

return {'status': 'not_found', 'message': 'Key not found in keyring'}

310

311

key = keys[0]

312

health_report = {

313

'key_id': key['keyid'],

314

'fingerprint': key['fingerprint'],

315

'status': 'healthy',

316

'issues': [],

317

'recommendations': []

318

}

319

320

# Check if key is expired

321

if key.get('expires'):

322

from datetime import datetime, timedelta

323

try:

324

exp_date = datetime.strptime(key['expires'], '%Y-%m-%d')

325

now = datetime.now()

326

327

if exp_date < now:

328

health_report['status'] = 'expired'

329

health_report['issues'].append('Key has expired')

330

elif exp_date < now + timedelta(days=30):

331

health_report['issues'].append('Key expires within 30 days')

332

health_report['recommendations'].append('Consider extending expiration')

333

except:

334

pass

335

336

# Check key strength

337

length = key.get('length', 0)

338

if length < 2048:

339

health_report['issues'].append(f'Key length {length} is weak by current standards')

340

health_report['recommendations'].append('Consider generating a new 2048+ bit key')

341

342

# Check for revocation

343

if key.get('revoked'):

344

health_report['status'] = 'revoked'

345

health_report['issues'].append('Key has been revoked')

346

347

# Check subkey health

348

subkey_issues = []

349

for subkey in key.get('subkeys', []):

350

if subkey.get('expires'):

351

try:

352

sub_exp = datetime.strptime(subkey['expires'], '%Y-%m-%d')

353

if sub_exp < datetime.now():

354

subkey_issues.append(f"Subkey {subkey['keyid']} has expired")

355

except:

356

pass

357

358

if subkey.get('length', 0) < 2048:

359

subkey_issues.append(f"Subkey {subkey['keyid']} has weak length")

360

361

if subkey_issues:

362

health_report['issues'].extend(subkey_issues)

363

health_report['recommendations'].append('Consider adding new subkeys')

364

365

# Check user ID validity

366

if not key.get('uids'):

367

health_report['issues'].append('No user IDs found')

368

else:

369

# Check for email validation (basic check)

370

valid_emails = []

371

for uid in key['uids']:

372

if '@' in uid and '.' in uid.split('@')[1]:

373

valid_emails.append(uid)

374

375

if not valid_emails:

376

health_report['recommendations'].append('Consider adding a valid email address')

377

378

return health_report

379

380

# Usage

381

health = validate_key_health(gpg, 'user@example.com')

382

print(f"Key health status: {health['status']}")

383

384

if health['issues']:

385

print("Issues found:")

386

for issue in health['issues']:

387

print(f" ⚠️ {issue}")

388

389

if health['recommendations']:

390

print("Recommendations:")

391

for rec in health['recommendations']:

392

print(f" 💡 {rec}")

393

```

394

395

### Advanced Key Discovery

396

397

```python

398

def discover_key_relationships(gpg_instance, target_keyid):

399

"""Discover relationships and signatures for a given key."""

400

401

# Get key with signature information

402

keys = gpg_instance.list_keys(keys=[target_keyid], sigs=True)

403

if not keys:

404

return None

405

406

key = keys[0]

407

relationships = {

408

'key_id': key['keyid'],

409

'fingerprint': key['fingerprint'],

410

'signed_by': [],

411

'certifications': [],

412

'cross_signatures': []

413

}

414

415

# Analyze signatures

416

for sig in key.get('sigs', []):

417

sig_info = {

418

'signer_id': sig.get('keyid'),

419

'signer_uid': sig.get('uid'),

420

'sig_class': sig.get('sig_class'),

421

'creation_date': sig.get('date')

422

}

423

424

if sig.get('sig_class') == '10': # Generic certification

425

relationships['certifications'].append(sig_info)

426

elif sig.get('sig_class') == '13': # Positive certification

427

relationships['signed_by'].append(sig_info)

428

elif sig.get('sig_class') == '18': # Subkey binding signature

429

relationships['cross_signatures'].append(sig_info)

430

431

return relationships

432

433

# Analyze key relationships

434

relationships = discover_key_relationships(gpg, 'alice@example.com')

435

if relationships:

436

print(f"Key relationships for {relationships['key_id']}:")

437

print(f"Certified by {len(relationships['signed_by'])} entities")

438

print(f"Has {len(relationships['certifications'])} certifications")

439

440

if relationships['signed_by']:

441

print("Signed by:")

442

for signer in relationships['signed_by'][:5]: # Show first 5

443

print(f" - {signer['signer_id']} ({signer['creation_date']})")

444

```