DEV Community

Daniel Ioni
Daniel Ioni

Posted on

๐Ÿ” PGP & Tor Integration in MyZubster

๐Ÿ” PGP & Tor Integration in MyZubster

Complete technical guide to privacy, encryption, and anonymity in the MyZubster ecosystem
๐Ÿ“Œ What is PGP & Tor Integration?

MyZubster integrates two core privacy technologies:
๐Ÿ›ก๏ธ PGP (Pretty Good Privacy)

End-to-end encryption for messages between users

Digital signatures for order integrity and authentication

Key management for buyers, sellers, and arbitrators

Verification of user identity and order authenticity
Enter fullscreen mode Exit fullscreen mode

๐Ÿง… Tor (The Onion Router)

Anonymity for users (hide IP addresses)

Censorship resistance โ€“ access from anywhere

Orbot integration โ€“ Tor on Android

Hidden services โ€“ optional .onion addresses
Enter fullscreen mode Exit fullscreen mode

๐Ÿงฉ Architecture Overview
text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ MyZubster Privacy Layer โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ PGP Encryption Layer โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Key Pair โ”‚ โ”‚ Encrypt โ”‚ โ”‚ Sign โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Generation โ”‚ โ”‚ Messages โ”‚ โ”‚ Messages โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Decrypt โ”‚ โ”‚ Verify โ”‚ โ”‚ Key Server โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Messages โ”‚ โ”‚ Signatures โ”‚ โ”‚ (Public) โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Tor Anonymity Layer โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ Tor Proxy โ”‚ โ”‚ Onion โ”‚ โ”‚ Orbot โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ (SOCKS5) โ”‚ โ”‚ Routing โ”‚ โ”‚ (Android) โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ“‚ Complete File Structure
text

marketplace/
โ”œโ”€โ”€ services/
โ”‚ โ”œโ”€โ”€ pgpService.js # PGP encryption/decryption
โ”‚ โ””โ”€โ”€ torService.js # Tor proxy configuration
โ”œโ”€โ”€ routes/
โ”‚ โ”œโ”€โ”€ pgp.js # PGP key management routes
โ”‚ โ””โ”€โ”€ messages.js # Encrypted messaging routes
โ”œโ”€โ”€ models/
โ”‚ โ”œโ”€โ”€ User.js # PGP public key storage
โ”‚ โ””โ”€โ”€ Message.js # Encrypted message storage
โ”œโ”€โ”€ middleware/
โ”‚ โ””โ”€โ”€ tor.js # Tor detection middleware
โ”œโ”€โ”€ utils/
โ”‚ โ”œโ”€โ”€ pgpUtils.js # PGP helper functions
โ”‚ โ””โ”€โ”€ torUtils.js # Tor helper functions
โ””โ”€โ”€ config/
โ””โ”€โ”€ tor.js # Tor configuration

๐Ÿ”ง Complete Code Walkthrough

  1. PGP Service (services/pgpService.js)

File: marketplace/services/pgpService.js
javascript

const openpgp = require('openpgp');
const { createHash } = require('crypto');

// ============================================
// GENERATE PGP KEY PAIR
// ============================================
const generateKeyPair = async (name, email, passphrase) => {
try {
const { privateKey, publicKey } = await openpgp.generateKey({
type: 'rsa',
rsaBits: 4096,
userIDs: [{ name, email }],
passphrase: passphrase,
format: 'armored'
});

return {
  privateKey: privateKey,
  publicKey: publicKey,
  fingerprint: await getFingerprint(publicKey)
};
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error generating PGP keys:', error);
throw error;
}
};

// ============================================
// GET FINGERPRINT FROM PUBLIC KEY
// ============================================
const getFingerprint = async (publicKeyArmored) => {
try {
const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
return publicKey.getFingerprint();
} catch (error) {
console.error('โŒ Error getting fingerprint:', error);
return null;
}
};

// ============================================
// ENCRYPT MESSAGE
// ============================================
const encryptMessage = async (message, publicKeyArmored) => {
try {
const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });

const encrypted = await openpgp.encrypt({
  message: await openpgp.createMessage({ text: message }),
  encryptionKeys: publicKey,
  format: 'armored'
});

return encrypted;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error encrypting message:', error);
throw error;
}
};

// ============================================
// DECRYPT MESSAGE
// ============================================
const decryptMessage = async (encryptedMessage, privateKeyArmored, passphrase) => {
try {
const privateKey = await openpgp.readPrivateKey({
armoredKey: privateKeyArmored
});

const decrypted = await openpgp.decrypt({
  message: await openpgp.readMessage({ armoredMessage: encryptedMessage }),
  decryptionKeys: privateKey,
  format: 'armored'
});

return decrypted.data;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error decrypting message:', error);
throw error;
}
};

// ============================================
// SIGN MESSAGE
// ============================================
const signMessage = async (message, privateKeyArmored, passphrase) => {
try {
const privateKey = await openpgp.readPrivateKey({
armoredKey: privateKeyArmored
});

const signed = await openpgp.sign({
  message: await openpgp.createMessage({ text: message }),
  signingKeys: privateKey,
  format: 'armored'
});

return signed;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error signing message:', error);
throw error;
}
};

// ============================================
// VERIFY SIGNATURE
// ============================================
const verifySignature = async (signedMessage, publicKeyArmored) => {
try {
const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });

const verificationResult = await openpgp.verify({
  message: await openpgp.readMessage({ armoredMessage: signedMessage }),
  verificationKeys: publicKey
});

const { verified } = verificationResult.signatures[0];
await verified; // throw if invalid signature

return true;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error verifying signature:', error);
return false;
}
};

// ============================================
// SIGN ORDER (for escrow)
// ============================================
const signOrder = async (orderData, privateKeyArmored, passphrase) => {
const message = JSON.stringify(orderData);
const signature = await signMessage(message, privateKeyArmored, passphrase);
return signature;
};

// ============================================
// VERIFY ORDER SIGNATURE
// ============================================
const verifyOrderSignature = async (orderData, signature, publicKeyArmored) => {
const message = JSON.stringify(orderData);
const signedMessage = -----BEGIN PGP SIGNED MESSAGE-----\n\n${message}\n${signature};
return await verifySignature(signedMessage, publicKeyArmored);
};

module.exports = {
generateKeyPair,
getFingerprint,
encryptMessage,
decryptMessage,
signMessage,
verifySignature,
signOrder,
verifyOrderSignature
};

  1. PGP Routes (routes/pgp.js)

File: marketplace/routes/pgp.js
javascript

const express = require('express');
const router = express.Router();
const { User } = require('../models');
const auth = require('../middleware/auth');
const {
generateKeyPair,
getFingerprint,
encryptMessage,
decryptMessage,
signMessage,
verifySignature
} = require('../services/pgpService');

// ============================================
// POST /api/pgp/generate
// Generate PGP key pair
// ============================================
router.post('/generate', auth, async (req, res) => {
try {
const { passphrase } = req.body;

if (!passphrase) {
  return res.status(400).json({ error: 'Passphrase required' });
}

const user = await User.findByPk(req.user.id);
if (!user) {
  return res.status(404).json({ error: 'User not found' });
}

// Generate key pair
const { privateKey, publicKey, fingerprint } = await generateKeyPair(
  user.name || user.username || 'MyZubster User',
  user.email,
  passphrase
);

// Update user with PGP keys
user.pgpPublicKey = publicKey;
user.pgpPrivateKey = privateKey; // Encrypted with passphrase
user.pgpFingerprint = fingerprint;
await user.save();

// Return public key only (keep private key secure)
res.json({
  success: true,
  publicKey: publicKey,
  fingerprint: fingerprint,
  message: 'PGP keys generated successfully. Keep your private key and passphrase safe!'
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error generating PGP keys:', error);
res.status(500).json({ error: error.message });
}
});

// ============================================
// GET /api/pgp/public-key/:userId
// Get user's public PGP key
// ============================================
router.get('/public-key/:userId', async (req, res) => {
try {
const user = await User.findByPk(req.params.userId, {
attributes: ['id', 'username', 'pgpPublicKey', 'pgpFingerprint']
});

if (!user) {
  return res.status(404).json({ error: 'User not found' });
}

if (!user.pgpPublicKey) {
  return res.status(404).json({ error: 'User has no PGP key' });
}

res.json({
  userId: user.id,
  username: user.username,
  fingerprint: user.pgpFingerprint,
  publicKey: user.pgpPublicKey
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error fetching public key:', error);
res.status(500).json({ error: error.message });
}
});

// ============================================
// POST /api/pgp/encrypt
// Encrypt message for recipient
// ============================================
router.post('/encrypt', auth, async (req, res) => {
try {
const { recipientUserId, message } = req.body;

if (!recipientUserId || !message) {
  return res.status(400).json({ error: 'Recipient and message required' });
}

// Get recipient's public key
const recipient = await User.findByPk(recipientUserId, {
  attributes: ['id', 'pgpPublicKey']
});

if (!recipient) {
  return res.status(404).json({ error: 'Recipient not found' });
}

if (!recipient.pgpPublicKey) {
  return res.status(400).json({ error: 'Recipient has no PGP key' });
}

// Encrypt message
const encrypted = await encryptMessage(message, recipient.pgpPublicKey);

res.json({
  success: true,
  encryptedMessage: encrypted
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error encrypting message:', error);
res.status(500).json({ error: error.message });
}
});

// ============================================
// POST /api/pgp/decrypt
// Decrypt message
// ============================================
router.post('/decrypt', auth, async (req, res) => {
try {
const { encryptedMessage, passphrase } = req.body;

if (!encryptedMessage || !passphrase) {
  return res.status(400).json({ error: 'Encrypted message and passphrase required' });
}

const user = await User.findByPk(req.user.id);
if (!user) {
  return res.status(404).json({ error: 'User not found' });
}

if (!user.pgpPrivateKey) {
  return res.status(400).json({ error: 'You have no PGP private key' });
}

// Decrypt message
const decrypted = await decryptMessage(
  encryptedMessage,
  user.pgpPrivateKey,
  passphrase
);

res.json({
  success: true,
  decryptedMessage: decrypted
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error decrypting message:', error);
res.status(500).json({ error: error.message });
}
});

// ============================================
// POST /api/pgp/sign
// Sign a message
// ============================================
router.post('/sign', auth, async (req, res) => {
try {
const { message, passphrase } = req.body;

if (!message || !passphrase) {
  return res.status(400).json({ error: 'Message and passphrase required' });
}

const user = await User.findByPk(req.user.id);
if (!user) {
  return res.status(404).json({ error: 'User not found' });
}

if (!user.pgpPrivateKey) {
  return res.status(400).json({ error: 'You have no PGP private key' });
}

// Sign message
const signature = await signMessage(message, user.pgpPrivateKey, passphrase);

res.json({
  success: true,
  signature: signature
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error signing message:', error);
res.status(500).json({ error: error.message });
}
});

// ============================================
// POST /api/pgp/verify
// Verify signature
// ============================================
router.post('/verify', async (req, res) => {
try {
const { signedMessage, userId } = req.body;

if (!signedMessage || !userId) {
  return res.status(400).json({ error: 'Signed message and userId required' });
}

// Get user's public key
const user = await User.findByPk(userId, {
  attributes: ['id', 'pgpPublicKey']
});

if (!user) {
  return res.status(404).json({ error: 'User not found' });
}

if (!user.pgpPublicKey) {
  return res.status(400).json({ error: 'User has no PGP key' });
}

// Verify signature
const isValid = await verifySignature(signedMessage, user.pgpPublicKey);

res.json({
  success: true,
  verified: isValid
});
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error verifying signature:', error);
res.status(500).json({ error: error.message });
}
});

module.exports = router;

  1. Message Model with Encryption (models/Message.js)

File: marketplace/models/Message.js
javascript

const { DataTypes } = require('sequelize');

module.exports = (sequelize) => {
const Message = sequelize.define('Message', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
senderId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
recipientId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
orderId: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'Orders',
key: 'id'
}
},
// Encrypted content
encryptedContent: {
type: DataTypes.TEXT,
allowNull: false
},
// Signature for authenticity
signature: {
type: DataTypes.TEXT,
allowNull: true
},
// Encryption metadata
encryptionFingerprint: {
type: DataTypes.STRING,
allowNull: true
},
isRead: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
readAt: {
type: DataTypes.DATE,
allowNull: true
},
deliveredAt: {
type: DataTypes.DATE,
allowNull: true
}
}, {
timestamps: true,
tableName: 'Messages'
});

// Associations
Message.associate = (models) => {
Message.belongsTo(models.User, { as: 'sender', foreignKey: 'senderId' });
Message.belongsTo(models.User, { as: 'recipient', foreignKey: 'recipientId' });
Message.belongsTo(models.Order, { foreignKey: 'orderId' });
};

return Message;
};

  1. Tor Service (services/torService.js)

File: marketplace/services/torService.js
javascript

const axios = require('axios');
const SocksProxyAgent = require('socks-proxy-agent');
const dns = require('dns');
const { promisify } = require('util');

const TOR_PROXY = process.env.TOR_PROXY || 'socks5://localhost:9050';
const TOR_ENABLED = process.env.TOR_ENABLED === 'true';

// ============================================
// CHECK IF TOR IS RUNNING
// ============================================
const isTorRunning = async () => {
try {
const agent = new SocksProxyAgent(TOR_PROXY);
await axios.get('https://check.torproject.org/api/ip', {
httpAgent: agent,
httpsAgent: agent,
timeout: 5000
});
return true;
} catch (error) {
return false;
}
};

// ============================================
// GET TOR IP ADDRESS
// ============================================
const getTorIp = async () => {
try {
const agent = new SocksProxyAgent(TOR_PROXY);
const response = await axios.get('https://check.torproject.org/api/ip', {
httpAgent: agent,
httpsAgent: agent,
timeout: 10000
});
return response.data;
} catch (error) {
console.error('โŒ Error getting Tor IP:', error);
return null;
}
};

// ============================================
// CHECK IF USER IS USING TOR
// ============================================
const isUsingTor = (req) => {
// Check for Tor headers
const xForwardedFor = req.headers['x-forwarded-for'] || '';
const via = req.headers['via'] || '';
const userAgent = req.headers['user-agent'] || '';

// Tor Browser user agent pattern
const isTorBrowser = userAgent.includes('Tor') ||
userAgent.includes('torbrowser') ||
userAgent.includes('SecurityToken');

// Check for Tor exit node headers
const isTorExit = via.toLowerCase().includes('tor') ||
xForwardedFor.includes('.onion');

return isTorBrowser || isTorExit;
};

// ============================================
// GET ONION ADDRESS FOR SERVICE
// ============================================
const getOnionAddress = async (serviceUrl) => {
try {
const agent = new SocksProxyAgent(TOR_PROXY);
const response = await axios.get(${serviceUrl}/api/onion-address, {
httpAgent: agent,
httpsAgent: agent,
timeout: 10000
});
return response.data.onionAddress;
} catch (error) {
console.error('โŒ Error getting onion address:', error);
return null;
}
};

// ============================================
// RESOLVE .ONION ADDRESS
// ============================================
const resolveOnionAddress = async (onionAddress) => {
try {
// Use Tor's DNS resolver
const agent = new SocksProxyAgent(TOR_PROXY);
const resolver = new dns.Resolver({
timeout: 5000,
tries: 2
});

// .onion addresses are handled by Tor proxy
return onionAddress;
Enter fullscreen mode Exit fullscreen mode

} catch (error) {
console.error('โŒ Error resolving onion address:', error);
return null;
}
};

// ============================================
// TOR PROXY MIDDLEWARE
// ============================================
const torMiddleware = (req, res, next) => {
req.isTor = isUsingTor(req);
req.torIp = req.isTor ? req.ip : null;
next();
};

// ============================================
// TOR REQUEST HELPER
// ============================================
const torRequest = async (url, options = {}) => {
try {
const agent = new SocksProxyAgent(TOR_PROXY);
const response = await axios({
url,
...options,
httpAgent: agent,
httpsAgent: agent,
timeout: options.timeout || 15000
});
return response;
} catch (error) {
console.error('โŒ Error making Tor request:', error);
throw error;
}
};

module.exports = {
TOR_ENABLED,
TOR_PROXY,
isTorRunning,
getTorIp,
isUsingTor,
getOnionAddress,
resolveOnionAddress,
torMiddleware,
torRequest
};

  1. Tor Middleware (middleware/tor.js)

File: marketplace/middleware/tor.js
javascript

const { isUsingTor, TOR_ENABLED } = require('../services/torService');

// ============================================
// ENFORCE TOR CONNECTION
// ============================================
const enforceTor = (req, res, next) => {
if (!TOR_ENABLED) {
return next();
}

const isTor = isUsingTor(req);
if (!isTor) {
return res.status(403).json({
error: 'This endpoint requires Tor connection',
message: 'Please access via Tor Browser or Orbot'
});
}
next();
};

// ============================================
// TOR LOGGING MIDDLEWARE
// ============================================
const torLogging = (req, res, next) => {
const isTor = isUsingTor(req);
const ip = req.ip || req.connection.remoteAddress;

console.log(๐Ÿ” Request from: ${ip} (Tor: ${isTor ? 'โœ…' : 'โŒ'}));

next();
};

// ============================================
// GET TOR STATUS
// ============================================
const getTorStatus = async () => {
const isRunning = await isTorRunning();
return {
enabled: TOR_ENABLED,
running: isRunning,
proxy: TOR_PROXY
};
};

module.exports = {
enforceTor,
torLogging,
getTorStatus
};

  1. User Model with PGP (models/User.js - updated)

File: marketplace/models/User.js (add these fields)
javascript

// Add to existing User model
pgpPublicKey: {
type: DataTypes.TEXT,
allowNull: true
},
pgpPrivateKey: {
type: DataTypes.TEXT,
allowNull: true
},
pgpFingerprint: {
type: DataTypes.STRING(40),
allowNull: true
},
torPreference: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
lastTorConnection: {
type: DataTypes.DATE,
allowNull: true
}

  1. Tor Configuration (config/tor.js)

File: marketplace/config/tor.js
javascript

const { TOR_ENABLED, TOR_PROXY } = process.env;

module.exports = {
enabled: TOR_ENABLED === 'true',
proxy: TOR_PROXY || 'socks5://localhost:9050',
timeout: 15000,
retries: 3,
// Orbot settings for Android
orbot: {
enabled: process.env.ORBOT_ENABLED === 'true',
port: parseInt(process.env.ORBOT_PORT) || 9050,
host: process.env.ORBOT_HOST || 'localhost'
},
// Hidden service settings
hiddenService: {
enabled: process.env.ONION_SERVICE_ENABLED === 'true',
hostname: process.env.ONION_HOSTNAME || '',
port: parseInt(process.env.ONION_PORT) || 80
}
};

๐Ÿ”ง Environment Variables

File: marketplace/.env (update)
env

Existing variables...

PORT=4000
NODE_ENV=development
DATABASE_URL=sqlite:./database.sqlite
JWT_SECRET=your_jwt_secret

PGP Settings

PGP_ENABLED=true
PGP_KEY_DIR=./keys
PGP_DEFAULT_PASSPHRASE=change_me_123

Tor Settings

TOR_ENABLED=true
TOR_PROXY=socks5://localhost:9050
ORBOT_ENABLED=true
ORBOT_PORT=9050

Onion Service

ONION_SERVICE_ENABLED=false
ONION_HOSTNAME=myzubster.onion
ONION_PORT=80

๐Ÿงช Test Commands
Generate PGP Keys
bash

curl -X POST http://localhost:4000/api/pgp/generate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"passphrase":"my_secure_passphrase"}'

Get User's Public Key
bash

curl http://localhost:4000/api/pgp/public-key/1

Encrypt Message
bash

curl -X POST http://localhost:4000/api/pgp/encrypt \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"recipientUserId":2,"message":"Hello, this is a secret message!"}'

Decrypt Message
bash

curl -X POST http://localhost:4000/api/pgp/decrypt \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"encryptedMessage":"-----BEGIN PGP MESSAGE-----...", "passphrase":"my_secure_passphrase"}'

Sign Message
bash

curl -X POST http://localhost:4000/api/pgp/sign \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"I agree to the terms","passphrase":"my_secure_passphrase"}'

Verify Signature
bash

curl -X POST http://localhost:4000/api/pgp/verify \
-H "Content-Type: application/json" \
-d '{"signedMessage":"-----BEGIN PGP SIGNED MESSAGE-----...","userId":2}'

Check Tor Status
bash

curl http://localhost:4000/api/tor/status

๐Ÿ”’ Security Best Practices
Practice Implementation
PGP Key Generation 4096-bit RSA keys
Private Key Storage Encrypted with passphrase
Message Encryption End-to-end encryption
Signature Verification Digital signatures for authenticity
Tor Connection SOCKS5 proxy with fallback
Orbot Integration Android Tor support
Onion Services Optional hidden services
๐Ÿ› Troubleshooting
"PGP keys not generated"
bash

Check if PGP_ENABLED is true

echo $PGP_ENABLED

"Tor connection failed"
bash

Check if Tor is running

systemctl status tor

Or start Tor

tor &

"Orbot not connecting"
bash

Check Orbot settings

adb shell netstat -an | grep 9050

๐Ÿ“š Resources

OpenPGP.js: openpgpjs.org

Tor Project: torproject.org

Orbot: orbot.app
Enter fullscreen mode Exit fullscreen mode

โœ… Next Steps

Test PGP on mobile app โ€“ React Native encryption

Implement Onion services โ€“ .onion addresses for API

Add key revocation โ€“ Revoke compromised keys

Improve UX โ€“ Passphrase management on mobile

Write tests โ€“ Unit tests for PGP/Tor integration
Enter fullscreen mode Exit fullscreen mode

Built with โค๏ธ for privacy, freedom, and decentralization.

Happy coding! ๐Ÿš€

Top comments (0)