Coverage for mcpgateway / utils / generate_keys.py: 100%
31 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""Location: ./mcpgateway/utils/generate_keys.py
4Copyright 2025
5SPDX-License-Identifier: Apache-2.0
6Authors: Madhav Kandukuri
8Utility to generate Ed25519 key pairs for JWT or signing use.
9Safely writes PEM-formatted private and public keys to disk.
10"""
12# Future
13from __future__ import annotations
15# Standard
16# Logging setup
17import logging
18from pathlib import Path
20# Third-Party
21from cryptography.hazmat.primitives import serialization
22from cryptography.hazmat.primitives.asymmetric import ed25519
24logger = logging.getLogger(__name__)
27def generate_ed25519_keypair(private_path: Path, public_path: Path) -> None:
28 """Generate an Ed25519 key pair and save to PEM files.
30 Args:
31 private_path: Path to save the private key PEM file.
32 public_path: Path to save the public key PEM file.
34 Examples:
35 >>> import io
36 >>> import tempfile
37 >>> from contextlib import redirect_stdout
38 >>> from pathlib import Path
39 >>> from mcpgateway.utils.generate_keys import generate_ed25519_keypair
40 >>> with tempfile.TemporaryDirectory() as d:
41 ... priv = Path(d) / "private.pem"
42 ... pub = Path(d) / "public.pem"
43 ... buf = io.StringIO()
44 ... with redirect_stdout(buf):
45 ... generate_ed25519_keypair(priv, pub)
46 ... (
47 ... priv.exists(),
48 ... pub.exists(),
49 ... "Ed25519 key pair generated" in buf.getvalue(),
50 ... priv.read_text(encoding="utf-8").startswith("-----BEGIN PRIVATE KEY-----"),
51 ... pub.read_text(encoding="utf-8").startswith("-----BEGIN PUBLIC KEY-----"),
52 ... )
53 (True, True, True, True, True)
54 """
55 private_key = ed25519.Ed25519PrivateKey.generate()
56 public_key = private_key.public_key()
58 private_bytes = private_key.private_bytes(
59 encoding=serialization.Encoding.PEM,
60 format=serialization.PrivateFormat.PKCS8,
61 encryption_algorithm=serialization.NoEncryption(),
62 )
64 public_bytes = public_key.public_bytes(
65 encoding=serialization.Encoding.PEM,
66 format=serialization.PublicFormat.SubjectPublicKeyInfo,
67 )
69 private_path.write_bytes(private_bytes)
70 public_path.write_bytes(public_bytes)
72 print(f"✅ Ed25519 key pair generated:\n Private: {private_path}\n Public: {public_path}")
75# ---------------------------------------------------------------------------
76# Simplified generator: return private key PEM only
77# ---------------------------------------------------------------------------
80def generate_ed25519_private_key() -> str:
81 """Generate an Ed25519 private key and return PEM string.
83 Returns:
84 str: PEM-formatted Ed25519 private key.
86 Examples:
87 >>> from mcpgateway.utils.generate_keys import generate_ed25519_private_key
88 >>> pem = generate_ed25519_private_key()
89 >>> pem.startswith("-----BEGIN PRIVATE KEY-----")
90 True
91 >>> pem.strip().endswith("-----END PRIVATE KEY-----")
92 True
93 """
94 private_key = ed25519.Ed25519PrivateKey.generate()
95 private_pem = private_key.private_bytes(
96 encoding=serialization.Encoding.PEM,
97 format=serialization.PrivateFormat.PKCS8,
98 encryption_algorithm=serialization.NoEncryption(),
99 ).decode()
100 return private_pem
103# ---------------------------------------------------------------------------
104# Helper: derive public key from private PEM
105# ---------------------------------------------------------------------------
108def derive_public_key_from_private(private_pem: str) -> str:
109 """Derive the public key PEM from a given Ed25519 private key PEM string.
111 Args:
112 private_pem: PEM-formatted Ed25519 private key string.
114 Returns:
115 str: PEM-formatted Ed25519 public key.
117 Raises:
118 RuntimeError: If the public key cannot be derived.
120 Examples:
121 >>> from mcpgateway.utils.generate_keys import derive_public_key_from_private, generate_ed25519_private_key
122 >>> pub = derive_public_key_from_private(generate_ed25519_private_key())
123 >>> pub.startswith("-----BEGIN PUBLIC KEY-----")
124 True
126 >>> derive_public_key_from_private("not a pem")
127 Traceback (most recent call last):
128 ...
129 RuntimeError: Failed to derive public key from private PEM
130 """
131 try:
132 private_key = serialization.load_pem_private_key(private_pem.encode(), password=None)
133 public_key = private_key.public_key()
134 public_pem = public_key.public_bytes(
135 encoding=serialization.Encoding.PEM,
136 format=serialization.PublicFormat.SubjectPublicKeyInfo,
137 )
138 return public_pem.decode()
139 except Exception as e:
140 logger.error(f"Error deriving public key from private PEM: {e}")
141 raise RuntimeError("Failed to derive public key from private PEM") from e
144def main() -> None:
145 """Command-line interface to generate Ed25519 private key PEM."""
146 private_pem = generate_ed25519_private_key()
147 print("Ed25519 private key generated successfully.\n")
148 print(private_pem)
151if __name__ == "__main__":
152 main()