Local SDK quickstart
For your engineers: encrypt a test message, create a decoy control file, then open both outputs. No API key is needed for these local operations. The examples use demo passwords; use your own secure credential handling in a real application.
TypeScript / Node.js
In the code, controlData is the control file: the separate file that chooses which message opens. generateDeniableControl makes a second control file that opens your chosen decoy from the same encrypted data, with the same two passwords.
npm install deny-shimport { encrypt, decrypt, generateControlData,
generateDeniableControl } from 'deny-sh';
// Demo passwords only. Run as an ES module (.mjs).
const password1 = 'demo-pass-one';
const password2 = 'demo-pass-two';
const message = new TextEncoder().encode('Test secret: blue door');
const controlData = generateControlData(message.length + 4);
const { ciphertext } = await encrypt(message, {
password1, password2, controlData
});
const { controlData: decoyControl } = await generateDeniableControl(
ciphertext, password1, password2,
new TextEncoder().encode('Test note: buy milk')
);
for (const control of [controlData, decoyControl]) {
const { plaintext } = await decrypt(ciphertext, {
password1, password2, controlData: control
});
console.log(new TextDecoder().decode(plaintext));
}
// Test secret: blue door
// Test note: buy milkPython
pip install deny-shShow the Python real-and-decoy example
from deny_sh import encrypt, decrypt, generate_deniable_control
p1, p2 = 'demo-pass-one', 'demo-pass-two'
ciphertext, control = encrypt(b'Test secret: blue door', p1, p2)
decoy = generate_deniable_control(
ciphertext, p1, p2, b'Test note: buy milk'
)
for selected in (control, decoy):
print(decrypt(ciphertext, p1, p2, selected).decode())
# Test secret: blue door
# Test note: buy milkGo
go get github.com/deny-sh-crypto/deny-go/v2Show the Go real-and-decoy example
package main
import (
"fmt"
deny "github.com/deny-sh-crypto/deny-go/v2"
)
func main() {
p1, p2 := "demo-pass-one", "demo-pass-two"
ct, control, err := deny.Encrypt([]byte("Test secret: blue door"), p1, p2, nil)
if err != nil { panic(err) }
decoy, err := deny.GenerateDeniableControl(ct, p1, p2, []byte("Test note: buy milk"))
if err != nil { panic(err) }
for _, selected := range [][]byte{control, decoy} {
message, err := deny.Decrypt(ct, p1, p2, selected)
if err != nil { panic(err) }
fmt.Println(string(message))
}
}
// Test secret: blue door
// Test note: buy milkRust
cargo add deny-shShow the Rust real-and-decoy example
use deny_sh::{encrypt, decrypt, generate_deniable_control};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (p1, p2) = ("demo-pass-one", "demo-pass-two");
let (ct, control) = encrypt(b"Test secret: blue door", p1, p2, None)?;
let decoy = generate_deniable_control(&ct, p1, p2, b"Test note: buy milk")?;
for selected in [&control, &decoy] {
let message = decrypt(&ct, p1, p2, selected)?;
println!("{}", String::from_utf8(message)?);
}
Ok(())
}
// Test secret: blue door
// Test note: buy milkExpected result: each example prints the real message, then the fake, from the same ciphertext and password pair. Store control files separately from the encrypted data.
For engineers: cross-language compatibility
The SDKs share an encrypted-data format. Known-answer tests (KATs) compare fixed reference inputs and outputs across implementations; they are implementation checks, not an independent security audit. See verification and the SDK guide.