Cifrado end-to-end
SMSGate 001 soporta cifrado opcional entre tu celular Android y tu código cliente. Nuestros servidores solo ven ciphertext, marcas de tiempo y números de teléfono — nunca el contenido del mensaje.
Modelo de amenazas
- Nuestros servidores quedan fuera del alcance. No podemos leer ni entregar el contenido.
- Los números de teléfono (remitente/destinatario) siguen siendo visibles para nosotros.
- El carrier (Telcel / AT&T / Movistar / WhatsApp) ve el texto plano cuando viaja por la red celular — esto protege contra nosotros, no contra el carrier.
- Si pierdes la passphrase, no podemos recuperar mensajes pasados.
Formato del envelope
Base64 de: "smsgw1" | salt[16] | iv[12] | AES-256-GCM(ciphertext + tag 16 bytes).
Derivación: PBKDF2-HMAC-SHA256(passphrase, salt, 100_000 iteraciones, 32 bytes).
Cifrado: AES-256-GCM, IV de 12 bytes, tag de 16 bytes.
Paso 1 — Configurar la passphrase en el celular
En la app Android, toca 🔒 Set E2E encryption passphrase e ingresa una passphrase fuerte (recomendamos 4+ palabras al azar o una cadena aleatoria de 20+ caracteres). Se guarda solo en la memoria privada del celular — nunca sale del dispositivo.
Paso 2 — Usar la misma passphrase en tu código
Node.js
import crypto from "node:crypto";
const ITER = 100_000;
const MAGIC = Buffer.from("smsgw1");
export function encrypt(passphrase, plaintext) {
const salt = crypto.randomBytes(16);
const iv = crypto.randomBytes(12);
const key = crypto.pbkdf2Sync(passphrase, salt, ITER, 32, "sha256");
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();
return Buffer.concat([MAGIC, salt, iv, ct, tag]).toString("base64");
}
export function decrypt(passphrase, envelopeB64) {
const buf = Buffer.from(envelopeB64, "base64");
if (!buf.slice(0, 6).equals(MAGIC)) throw new Error("envelope inválido");
const salt = buf.slice(6, 22);
const iv = buf.slice(22, 34);
const tag = buf.slice(buf.length - 16);
const ct = buf.slice(34, buf.length - 16);
const key = crypto.pbkdf2Sync(passphrase, salt, ITER, 32, "sha256");
const d = crypto.createDecipheriv("aes-256-gcm", key, iv);
d.setAuthTag(tag);
return Buffer.concat([d.update(ct), d.final()]).toString("utf8");
}
Python
import os, base64, hashlib
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
ITER = 100_000
MAGIC = b"smsgw1"
def encrypt(passphrase: str, plaintext: str) -> str:
salt = os.urandom(16)
iv = os.urandom(12)
key = hashlib.pbkdf2_hmac("sha256", passphrase.encode(), salt, ITER, 32)
ct = AESGCM(key).encrypt(iv, plaintext.encode(), None)
return base64.b64encode(MAGIC + salt + iv + ct).decode()
def decrypt(passphrase: str, envelope_b64: str) -> str:
buf = base64.b64decode(envelope_b64)
assert buf[:6] == MAGIC
salt, iv, ct = buf[6:22], buf[22:34], buf[34:]
key = hashlib.pbkdf2_hmac("sha256", passphrase.encode(), salt, ITER, 32)
return AESGCM(key).decrypt(iv, ct, None).decode()
Paso 3 — Enviar un mensaje cifrado
Cifra en tu lado, manda el envelope como message, marca isEncrypted: true:
CIPHERTEXT=$(node -e "import('./e2e.js').then(m=>console.log(m.encrypt('mi passphrase','hola mundo')))")
curl -X POST https://sms.001.com.mx/api/v1/send \
-H "Authorization: Bearer sk_..." \
-H "content-type: application/json" \
-d "{
\"channel\": \"sms\",
\"phoneNumbers\": [\"+5215555555555\"],
\"message\": \"$CIPHERTEXT\",
\"isEncrypted\": true
}"
El dispositivo Android descifra con la passphrase local y manda el texto plano al carrier. Nuestros servidores solo ven el envelope cifrado.
Paso 4 — Descifrar mensajes entrantes
Cuando E2E está activo en el celular, los SMS entrantes se cifran con la passphrase antes de enviarnos nada. Tu webhook receiver debe descifrar:
import { decrypt } from "./e2e.js";
app.post("/webhook", express.json(), (req, res) => {
const { event, data } = req.body;
if (event === "sms:received" && data.isEncrypted) {
const plaintext = decrypt(process.env.E2E_PASSPHRASE, data.body);
console.log("plaintext:", plaintext);
}
res.sendStatus(200);
});
Consideraciones
- WhatsApp ya trae su propio E2E;
isEncryptedaplica solo a SMS/MMS. - SMS multipart: el envelope agrega ~60 bytes de overhead.
- Rotación de llaves: cambiar la passphrase invalida los mensajes pasados. Mantén un log de llaves.
- Higiene: guarda la passphrase en un secrets manager (1Password, AWS Secrets Manager, GCP Secret Manager). Nunca la subas al repo.