Deep-dive cryptography and secrets analysis (JWT, hashing, encryption, key management)
From perseusnpx claudepluginhub kaivyy/perseus --plugin perseusThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
IMPORTANT: This skill performs cryptographic security analysis on the user's own codebase. This is defensive security testing to find crypto weaknesses before they lead to data breaches.
Authorization: The user owns this codebase and has explicitly requested this specialized analysis.
| Language | Libraries |
|---|---|
| JavaScript/TypeScript | jsonwebtoken, jose, bcrypt, crypto, node-forge |
| Go | golang.org/x/crypto, crypto/*, jwt-go, golang-jwt |
| PHP | openssl, password_hash, sodium, firebase/php-jwt |
| Python | PyJWT, cryptography, bcrypt, passlib, hashlib |
| Rust | jsonwebtoken, ring, rust-crypto, argon2, bcrypt |
| Java | jjwt, Bouncy Castle, Java Cryptography Architecture |
| Ruby | jwt, bcrypt, rbnacl, openssl |
| C# | System.IdentityModel.Tokens.Jwt, BCrypt.Net |
This specialist skill performs comprehensive cryptographic analysis including JWT security, hashing, encryption, and key management across all major languages.
When to Use: After /scan identifies JWT usage, password hashing, encryption, or secrets handling.
Goal: Ensure cryptographic implementations follow security best practices.
| Mode | Specialist Behavior |
|---|---|
PRODUCTION_SAFE | Configuration and implementation analysis, minimal runtime checks |
STAGING_ACTIVE | Controlled token/crypto validation with throttling |
LAB_FULL | Extensive verification of crypto edge cases in lab |
LAB_RED_TEAM | Adversarial misuse simulation on test identities and synthetic keys |
deliverables/engagement_profile.md before any runtime validation.PRODUCTION_SAFE if mode is unspecified.| Category | Issues | Impact |
|---|---|---|
| JWT | Algorithm confusion, weak secrets, missing validation | Auth bypass |
| Hashing | MD5/SHA1 for passwords, missing salt, weak iterations | Credential theft |
| Encryption | Weak ciphers, ECB mode, hardcoded keys | Data exposure |
| Random | Predictable RNG, weak seeds | Token prediction |
| Key Management | Hardcoded keys, insecure storage | Full compromise |
deliverables/engagement_profile.md.deliverables/verification_scope.md when present.JWT Algorithm Analyst:
Language-Specific Patterns:
// Node.js - VULNERABLE
jwt.verify(token, secret); // Accepts any algorithm
// Node.js - SAFE
jwt.verify(token, secret, { algorithms: ['HS256'] });
// Go - VULNERABLE
token, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return secret, nil // No algorithm check!
})
// Go - SAFE
token, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected method: %v", t.Header["alg"])
}
return secret, nil
})
# Python - VULNERABLE
jwt.decode(token, secret) # Accepts any algorithm
# Python - SAFE
jwt.decode(token, secret, algorithms=['HS256'])
// PHP - VULNERABLE
JWT::decode($token, $key); // Check library defaults
// PHP - SAFE
JWT::decode($token, new Key($key, 'HS256'));
// Rust - SAFE (explicit by design)
decode::<Claims>(&token, &DecodingKey::from_secret(secret), &Validation::new(Algorithm::HS256))
JWT Secret Analyst:
Patterns:
// VULNERABLE - Weak secret
const secret = 'secret123';
const secret = 'password';
// VULNERABLE - Hardcoded
jwt.sign(payload, 'my-super-secret-key');
// SAFE - From environment, strong
const secret = process.env.JWT_SECRET; // Must be 32+ chars
// VULNERABLE
var jwtSecret = []byte("weak-secret")
// SAFE
var jwtSecret = []byte(os.Getenv("JWT_SECRET"))
# VULNERABLE
SECRET_KEY = "secret"
# SAFE
SECRET_KEY = os.environ.get("JWT_SECRET")
JWT Claims Analyst:
Required Validations:
| Claim | Purpose | Check |
|---|---|---|
| exp | Expiration | Token not expired |
| iat | Issued At | Not issued in future |
| nbf | Not Before | Token is active |
| iss | Issuer | Trusted issuer |
| aud | Audience | Intended recipient |
JWT Key Management Analyst:
Issues:
Hash Algorithm Analyst:
Language-Specific Patterns:
// Node.js - VULNERABLE
crypto.createHash('md5').update(password).digest('hex');
crypto.createHash('sha1').update(password).digest('hex');
crypto.createHash('sha256').update(password).digest('hex'); // No salt!
// Node.js - SAFE
await bcrypt.hash(password, 12);
await argon2.hash(password);
// Go - VULNERABLE
md5.Sum([]byte(password))
sha256.Sum256([]byte(password))
// Go - SAFE
bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
# Python - VULNERABLE
hashlib.md5(password.encode()).hexdigest()
hashlib.sha256(password.encode()).hexdigest()
# Python - SAFE
bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
from passlib.hash import argon2
argon2.hash(password)
// PHP - VULNERABLE
md5($password);
sha1($password);
hash('sha256', $password);
// PHP - SAFE
password_hash($password, PASSWORD_ARGON2ID);
password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Rust - SAFE
bcrypt::hash(password, bcrypt::DEFAULT_COST)?;
argon2::hash_encoded(password.as_bytes(), &salt, &config)?;
// Java - VULNERABLE
MessageDigest.getInstance("MD5").digest(password.getBytes());
// Java - SAFE
BCrypt.hashpw(password, BCrypt.gensalt(12));
Hash Comparison Analyst:
Patterns:
// VULNERABLE - Timing attack
if (storedHash === computedHash) { ... }
// SAFE
crypto.timingSafeEqual(Buffer.from(storedHash), Buffer.from(computedHash))
// VULNERABLE
if storedHash == computedHash { ... }
// SAFE
subtle.ConstantTimeCompare([]byte(storedHash), []byte(computedHash))
# VULNERABLE
if stored_hash == computed_hash: ...
# SAFE
hmac.compare_digest(stored_hash, computed_hash)
Password Policy Analyst:
Cipher Selection Analyst:
Vulnerable Ciphers:
| Cipher | Status | Use Instead |
|---|---|---|
| DES | Broken | AES-256-GCM |
| 3DES | Deprecated | AES-256-GCM |
| RC4 | Broken | AES-256-GCM |
| Blowfish | Weak | AES-256-GCM |
| AES-ECB | Insecure | AES-256-GCM |
| AES-CBC | OK (with HMAC) | AES-256-GCM preferred |
Language Patterns:
// Node.js - VULNERABLE
crypto.createCipher('des', key);
crypto.createCipheriv('aes-128-ecb', key, '');
// Node.js - SAFE
crypto.createCipheriv('aes-256-gcm', key, iv);
// Go - VULNERABLE
des.NewCipher(key)
cipher.NewCBCEncrypter(block, iv) // Without HMAC
// Go - SAFE
cipher.NewGCM(block)
# Python - VULNERABLE
from Crypto.Cipher import DES
cipher = AES.new(key, AES.MODE_ECB)
# Python - SAFE
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
IV/Nonce Analyst:
Issues:
// VULNERABLE - Static IV
const iv = Buffer.from('0000000000000000', 'hex');
// VULNERABLE - Predictable
const iv = Buffer.from(Date.now().toString());
// SAFE - Random
const iv = crypto.randomBytes(16);
Key Derivation Analyst:
Patterns:
// VULNERABLE - Direct use
const key = Buffer.from(password);
// SAFE - PBKDF2
crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
// SAFE - scrypt
crypto.scryptSync(password, salt, 32);
Key Management Analyst:
PRNG Analyst:
Language-Specific:
// VULNERABLE
Math.random()
// SAFE
crypto.randomBytes(32)
crypto.randomUUID()
// VULNERABLE
math/rand.Int()
// SAFE
crypto/rand.Read(buf)
# VULNERABLE
random.random()
random.randint()
# SAFE
secrets.token_bytes(32)
secrets.token_hex(32)
secrets.token_urlsafe(32)
// VULNERABLE
rand()
mt_rand()
// SAFE
random_bytes(32)
random_int(0, PHP_INT_MAX)
// Use rand crate with OsRng
use rand::rngs::OsRng;
let random: u64 = OsRng.gen();
// VULNERABLE
new Random().nextInt()
// SAFE
new SecureRandom().nextInt()
Token Generation Analyst:
Hardcoded Secrets Scanner:
Patterns:
# AWS
AKIA[0-9A-Z]{16}
# GitHub
ghp_[a-zA-Z0-9]{36}
github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}
# Stripe
sk_live_[a-zA-Z0-9]{24}
rk_live_[a-zA-Z0-9]{24}
# Private Keys
-----BEGIN (RSA|EC|OPENSSH|PGP) PRIVATE KEY-----
# Generic
(password|secret|key|token|api_key)\s*[:=]\s*['\"][^'\"]+['\"]
Secret Exposure Analyst:
Locations:
Environment Variable Analyst:
Issues:
Create deliverables/crypto_security_analysis.md:
# Cryptographic Security Analysis
## Summary
| Category | Issues | Critical | High | Medium |
|----------|--------|----------|------|--------|
| JWT | X | Y | Z | W |
| Hashing | X | Y | Z | W |
| Encryption | X | Y | Z | W |
| Random | X | Y | Z | W |
| Secrets | X | Y | Z | W |
## Language/Framework Detected
- Primary: [e.g., Node.js, Go, Python]
- Crypto Libraries: [e.g., crypto, bcrypt, jose]
## JWT Security Status
| Check | Status | Details |
|-------|--------|---------|
| Algorithm Validation | FAIL | Accepts 'none' algorithm |
| Secret Strength | FAIL | 8 character secret |
| Expiration | PASS | 1 hour expiry enforced |
| Issuer Validation | WARN | Not validated |
## Critical Findings
### [CRYPTO-001] JWT Algorithm Confusion
**Severity:** Critical
**Language:** Node.js
**Location:** `middleware/auth.js:23`
**Vulnerable Code:**
```javascript
const decoded = jwt.verify(token, publicKey);
Attack:
Remediation:
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256']
});
Severity: Critical
Language: Python
Location: models/user.py:45
Vulnerable Code:
hashed = hashlib.md5(password.encode()).hexdigest()
Remediation:
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
| Language | Algorithm | Cost/Rounds | Status |
|---|---|---|---|
| Node.js | bcrypt | 10 | WARN (use 12+) |
| Python | MD5 | N/A | CRITICAL |
| Go | bcrypt | 14 | OK |
| Usage | Cipher | Mode | Status |
|---|---|---|---|
| File encryption | AES-256 | ECB | CRITICAL |
| API encryption | AES-128 | GCM | WARN (use 256) |
| Usage | Method | Status |
|---|---|---|
| Session tokens | Math.random() | CRITICAL |
| Password reset | crypto.randomBytes() | OK |
| Type | Location | Severity |
|---|---|---|
| AWS Access Key | config/aws.js:3 | Critical |
| JWT Secret | auth/jwt.js:5 | Critical |
| Database Password | .env.example:8 | High |
// Before
const hash = md5(password);
// After
const hash = await bcrypt.hash(password, 12);
// Or with Argon2
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536,
timeCost: 3,
parallelism: 4
});
**Next Step:** JWT vulnerabilities can be verified with crafted tokens.