Initial commit - 611 cybersecurity skills across all subdomains

This commit is contained in:
mukul975
2026-02-25 10:47:44 +01:00
commit 22a7ab1462
1765 changed files with 280648 additions and 0 deletions
@@ -0,0 +1,63 @@
---
name: implementing-digital-signatures-with-ed25519
description: Ed25519 is a high-performance digital signature algorithm using the Edwards curve Curve25519. It provides 128-bit security with 64-byte signatures and 32-byte keys, offering significant advantages ove
domain: cybersecurity
subdomain: cryptography
tags: [cryptography, digital-signatures, ed25519, authentication, integrity]
version: "1.0"
author: mahipal
license: MIT
---
# Implementing Digital Signatures with Ed25519
## Overview
Ed25519 is a high-performance digital signature algorithm using the Edwards curve Curve25519. It provides 128-bit security with 64-byte signatures and 32-byte keys, offering significant advantages over RSA and ECDSA including deterministic signatures (no random nonce needed), resistance to side-channel attacks, and fast verification. This skill covers implementing Ed25519 for document signing, code signing, and API authentication.
## Objectives
- Generate Ed25519 key pairs for signing
- Sign messages and files with Ed25519
- Verify signatures against public keys
- Implement multi-signature verification
- Build a simple code signing system
- Compare Ed25519 performance with RSA and ECDSA
## Key Concepts
### Ed25519 vs RSA vs ECDSA
| Property | Ed25519 | RSA-3072 | ECDSA P-256 |
|----------|---------|----------|-------------|
| Security | 128-bit | 128-bit | 128-bit |
| Public key size | 32 bytes | 384 bytes | 64 bytes |
| Signature size | 64 bytes | 384 bytes | 64 bytes |
| Key generation | ~50 us | ~100 ms | ~1 ms |
| Sign | ~70 us | ~5 ms | ~200 us |
| Verify | ~200 us | ~200 us | ~500 us |
| Deterministic | Yes | No (PSS) | No (unless RFC 6979) |
### Key Properties
- **Deterministic**: Same message + key always produces same signature
- **Collision-resistant**: No separate hash function needed
- **Side-channel resistant**: Constant-time implementation
- **Small keys**: 32 bytes each (public and private)
## Security Considerations
- Ed25519 does not support key recovery from signatures
- Verify the full message, not a hash (Ed25519 hashes internally)
- Public keys must be validated before use (check for low-order points)
- Private keys should be stored encrypted at rest
- Ed25519 is not yet approved for all NIST use cases (Ed448 is preferred for federal)
## Validation Criteria
- [ ] Key pair generation produces valid Ed25519 keys
- [ ] Signature verification succeeds for valid message
- [ ] Signature verification fails for tampered message
- [ ] Signature verification fails for wrong public key
- [ ] Deterministic: same input produces same signature
- [ ] File signing and verification works correctly
- [ ] Performance meets or exceeds RSA-3072
@@ -0,0 +1,34 @@
# Ed25519 Digital Signatures Template
## Quick Reference
```python
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
# Generate
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()
# Sign
signature = private_key.sign(b"message data")
# Verify
public_key.verify(signature, b"message data") # raises InvalidSignature on failure
```
## Key Formats
| Format | Private Key Size | Public Key Size | Signature Size |
|--------|-----------------|-----------------|----------------|
| Raw | 32 bytes | 32 bytes | 64 bytes |
| PEM (PKCS#8) | ~119 bytes | ~90 bytes | N/A |
| SSH | ~83 bytes | ~51 bytes | ~83 bytes |
## Use Cases
- API request authentication (sign request body)
- Software/code signing
- Document signing
- Git commit signing (ssh-ed25519)
- JWT signing (EdDSA algorithm)
- Certificate signing (X.509 with Ed25519)
@@ -0,0 +1,36 @@
# Standards and References - Digital Signatures with Ed25519
## Primary Standards
### RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA)
- **URL**: https://www.rfc-editor.org/rfc/rfc8032
- **Description**: Defines Ed25519 and Ed448 signature algorithms
### RFC 8709 - Ed25519 and Ed448 Public Key Algorithms for SSH
- **URL**: https://www.rfc-editor.org/rfc/rfc8709
- **Description**: SSH key format for Ed25519
### NIST FIPS 186-5 - Digital Signature Standard
- **URL**: https://csrc.nist.gov/publications/detail/fips/186/5/final
- **Description**: Includes EdDSA as approved signature algorithm
### RFC 7748 - Elliptic Curves for Security
- **URL**: https://www.rfc-editor.org/rfc/rfc7748
- **Description**: Defines Curve25519 and Curve448
## Python Libraries
### cryptography (pyca/cryptography)
- **Ed25519**: `cryptography.hazmat.primitives.asymmetric.ed25519`
- **Docs**: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ed25519/
### PyNaCl (libsodium)
- **URL**: https://pynacl.readthedocs.io/
- **Ed25519**: `nacl.signing`
- **Docs**: https://pynacl.readthedocs.io/en/latest/signing/
## Related
### Daniel J. Bernstein et al. - High-speed high-security signatures
- **URL**: https://ed25519.cr.yp.to/
- **Description**: Original Ed25519 paper and reference implementation
@@ -0,0 +1,66 @@
# Workflows - Digital Signatures with Ed25519
## Workflow 1: Key Generation and Storage
```
[Generate Ed25519 Key Pair]
(32-byte private seed -> 32-byte public key)
|
[Serialize Private Key (PKCS#8 PEM)]
[Serialize Public Key (SubjectPublicKeyInfo PEM)]
|
[Encrypt Private Key with Passphrase]
|
[Store with Metadata]
(key_id, fingerprint, creation_date)
```
## Workflow 2: Sign Document
```
[Document to Sign]
|
[Load Private Key (decrypt passphrase)]
|
[Ed25519 Sign]
(deterministic: SHA-512 internal hash)
|
[Output: 64-byte Signature]
|
[Create Signature File]
(signature + public key reference + metadata)
```
## Workflow 3: Verify Signature
```
[Document + Signature + Public Key]
|
[Load Public Key]
|
[Ed25519 Verify]
|
[Valid?]
YES -> Accept document as authentic
NO -> Reject (tampering detected)
```
## Workflow 4: Code Signing System
```
[Build Artifact] (binary, package, container)
|
[Hash Artifact] (SHA-256)
|
[Create Signing Manifest]
(artifact_name, hash, timestamp, signer_id)
|
[Sign Manifest with Ed25519]
|
[Distribute: Artifact + Manifest + Signature + Public Key]
|
[Recipient Verifies]:
1. Verify signature on manifest
2. Hash artifact and compare to manifest
3. Check signer identity against trust store
```
@@ -0,0 +1,319 @@
#!/usr/bin/env python3
"""
Ed25519 Digital Signature Tool
Implements Ed25519 key generation, signing, verification, and a
simple code signing system.
Requirements:
pip install cryptography
Usage:
python process.py generate --output ./keys
python process.py sign --key ./keys/private.pem --input document.pdf
python process.py verify --key ./keys/public.pem --input document.pdf --signature document.pdf.sig
python process.py code-sign --key ./keys/private.pem --artifact ./build/app.zip
python process.py benchmark
"""
import os
import sys
import json
import time
import hashlib
import argparse
import logging
import datetime
import base64
from pathlib import Path
from typing import Dict, Optional, Tuple
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives import serialization
from cryptography.exceptions import InvalidSignature
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
def generate_ed25519_keypair(
output_dir: str, passphrase: Optional[str] = None
) -> Dict:
"""Generate an Ed25519 key pair."""
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
if passphrase:
enc = serialization.BestAvailableEncryption(passphrase.encode())
else:
enc = serialization.NoEncryption()
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=enc,
)
(output_path / "private.pem").write_bytes(private_pem)
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
(output_path / "public.pem").write_bytes(public_pem)
# Compute fingerprint
public_raw = public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
fingerprint = hashlib.sha256(public_raw).hexdigest()
metadata = {
"algorithm": "Ed25519",
"public_key_hex": public_raw.hex(),
"fingerprint_sha256": fingerprint,
"created_at": datetime.datetime.utcnow().isoformat() + "Z",
"private_key_path": str(output_path / "private.pem"),
"public_key_path": str(output_path / "public.pem"),
}
(output_path / "key_metadata.json").write_text(json.dumps(metadata, indent=2))
logger.info(f"Ed25519 key pair generated in {output_dir}")
logger.info(f"Fingerprint: {fingerprint}")
return metadata
def load_private_key(path: str, passphrase: Optional[str] = None) -> Ed25519PrivateKey:
"""Load Ed25519 private key from PEM file."""
data = Path(path).read_bytes()
pwd = passphrase.encode() if passphrase else None
key = serialization.load_pem_private_key(data, password=pwd)
if not isinstance(key, Ed25519PrivateKey):
raise TypeError("Key is not Ed25519")
return key
def load_public_key(path: str) -> Ed25519PublicKey:
"""Load Ed25519 public key from PEM file."""
data = Path(path).read_bytes()
key = serialization.load_pem_public_key(data)
if not isinstance(key, Ed25519PublicKey):
raise TypeError("Key is not Ed25519")
return key
def sign_data(data: bytes, private_key: Ed25519PrivateKey) -> bytes:
"""Sign data with Ed25519."""
return private_key.sign(data)
def verify_data(data: bytes, signature: bytes, public_key: Ed25519PublicKey) -> bool:
"""Verify Ed25519 signature."""
try:
public_key.verify(signature, data)
return True
except InvalidSignature:
return False
def sign_file(key_path: str, input_path: str, passphrase: Optional[str] = None) -> Dict:
"""Sign a file and save the signature."""
private_key = load_private_key(key_path, passphrase)
data = Path(input_path).read_bytes()
signature = sign_data(data, private_key)
sig_path = input_path + ".sig"
Path(sig_path).write_bytes(signature)
# Also save base64 signature for text-friendly contexts
sig_b64_path = input_path + ".sig.b64"
Path(sig_b64_path).write_text(base64.b64encode(signature).decode())
file_hash = hashlib.sha256(data).hexdigest()
logger.info(f"Signed {input_path} ({len(data)} bytes)")
return {
"file": input_path,
"signature_file": sig_path,
"signature_b64_file": sig_b64_path,
"signature_hex": signature.hex(),
"file_sha256": file_hash,
"algorithm": "Ed25519",
}
def verify_file(key_path: str, input_path: str, sig_path: str) -> Dict:
"""Verify a file's Ed25519 signature."""
public_key = load_public_key(key_path)
data = Path(input_path).read_bytes()
signature = Path(sig_path).read_bytes()
# Handle base64 encoded signatures
if len(signature) != 64:
try:
signature = base64.b64decode(signature)
except Exception:
pass
valid = verify_data(data, signature, public_key)
logger.info(f"Verification: {'VALID' if valid else 'INVALID'}")
return {
"file": input_path,
"valid": valid,
"file_sha256": hashlib.sha256(data).hexdigest(),
"algorithm": "Ed25519",
}
def code_sign(key_path: str, artifact_path: str, passphrase: Optional[str] = None) -> Dict:
"""Create a code signing manifest for an artifact."""
private_key = load_private_key(key_path, passphrase)
data = Path(artifact_path).read_bytes()
public_raw = private_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
manifest = {
"artifact": Path(artifact_path).name,
"size": len(data),
"sha256": hashlib.sha256(data).hexdigest(),
"sha512": hashlib.sha512(data).hexdigest(),
"signer_public_key": public_raw.hex(),
"signer_fingerprint": hashlib.sha256(public_raw).hexdigest(),
"signed_at": datetime.datetime.utcnow().isoformat() + "Z",
"algorithm": "Ed25519",
}
manifest_json = json.dumps(manifest, indent=2, sort_keys=True).encode()
signature = sign_data(manifest_json, private_key)
signed_manifest = {
**manifest,
"signature": base64.b64encode(signature).decode(),
}
manifest_path = artifact_path + ".manifest.json"
Path(manifest_path).write_text(json.dumps(signed_manifest, indent=2))
logger.info(f"Code signed: {artifact_path}")
return signed_manifest
def verify_code_signature(manifest_path: str, artifact_path: str) -> Dict:
"""Verify a code signing manifest."""
signed_manifest = json.loads(Path(manifest_path).read_text())
signature = base64.b64decode(signed_manifest["signature"])
public_raw = bytes.fromhex(signed_manifest["signer_public_key"])
public_key = Ed25519PublicKey.from_public_bytes(public_raw)
manifest_copy = {k: v for k, v in signed_manifest.items() if k != "signature"}
manifest_json = json.dumps(manifest_copy, indent=2, sort_keys=True).encode()
sig_valid = verify_data(manifest_json, signature, public_key)
data = Path(artifact_path).read_bytes()
hash_valid = hashlib.sha256(data).hexdigest() == signed_manifest["sha256"]
return {
"artifact": artifact_path,
"signature_valid": sig_valid,
"hash_valid": hash_valid,
"overall_valid": sig_valid and hash_valid,
"signer_fingerprint": signed_manifest["signer_fingerprint"],
}
def benchmark():
"""Benchmark Ed25519 operations."""
print("=== Ed25519 Benchmark ===\n")
# Key generation
count = 1000
start = time.time()
for _ in range(count):
Ed25519PrivateKey.generate()
elapsed = time.time() - start
print(f"Key generation: {count / elapsed:.0f} keys/s ({elapsed / count * 1e6:.1f} us/key)")
# Signing
key = Ed25519PrivateKey.generate()
message = b"Benchmark message for Ed25519 signing performance test." * 10
count = 5000
start = time.time()
for _ in range(count):
key.sign(message)
elapsed = time.time() - start
print(f"Signing: {count / elapsed:.0f} sigs/s ({elapsed / count * 1e6:.1f} us/sig)")
# Verification
public_key = key.public_key()
signature = key.sign(message)
count = 2000
start = time.time()
for _ in range(count):
public_key.verify(signature, message)
elapsed = time.time() - start
print(f"Verification: {count / elapsed:.0f} verifs/s ({elapsed / count * 1e6:.1f} us/verify)")
def main():
parser = argparse.ArgumentParser(description="Ed25519 Digital Signature Tool")
subparsers = parser.add_subparsers(dest="command")
gen = subparsers.add_parser("generate", help="Generate Ed25519 key pair")
gen.add_argument("--output", "-o", default="./keys", help="Output directory")
gen.add_argument("--passphrase", "-p", help="Passphrase for private key")
sig = subparsers.add_parser("sign", help="Sign a file")
sig.add_argument("--key", required=True, help="Private key path")
sig.add_argument("--input", "-i", required=True, help="File to sign")
sig.add_argument("--passphrase", "-p", help="Key passphrase")
ver = subparsers.add_parser("verify", help="Verify signature")
ver.add_argument("--key", required=True, help="Public key path")
ver.add_argument("--input", "-i", required=True, help="File to verify")
ver.add_argument("--signature", "-s", required=True, help="Signature file")
cs = subparsers.add_parser("code-sign", help="Code sign an artifact")
cs.add_argument("--key", required=True, help="Private key path")
cs.add_argument("--artifact", required=True, help="Artifact to sign")
cs.add_argument("--passphrase", "-p", help="Key passphrase")
csv = subparsers.add_parser("code-verify", help="Verify code signature")
csv.add_argument("--manifest", required=True, help="Manifest file path")
csv.add_argument("--artifact", required=True, help="Artifact file path")
subparsers.add_parser("benchmark", help="Benchmark Ed25519 performance")
args = parser.parse_args()
if args.command == "generate":
result = generate_ed25519_keypair(args.output, args.passphrase)
print(json.dumps(result, indent=2))
elif args.command == "sign":
result = sign_file(args.key, args.input, args.passphrase)
print(json.dumps(result, indent=2))
elif args.command == "verify":
result = verify_file(args.key, args.input, args.signature)
print(json.dumps(result, indent=2))
elif args.command == "code-sign":
result = code_sign(args.key, args.artifact, args.passphrase)
print(json.dumps(result, indent=2))
elif args.command == "code-verify":
result = verify_code_signature(args.manifest, args.artifact)
print(json.dumps(result, indent=2))
elif args.command == "benchmark":
benchmark()
else:
parser.print_help()
if __name__ == "__main__":
main()