Skip to content

Instantly share code, notes, and snippets.

Last active May 19, 2022
What would you like to do?
TP-Link Router Config Decrypt

TP-Link Router Config

Update 2021-04-29: This may still work for you if you've got an old TP-Link router, but this is not maintained and doesn't work with newer models. If you've got a newer router, other projects like tpconf_bin_xml will likely work better for you.

TP-Link allows you to backup and restore your router's config file. For some reason, they decided to encrypt these backups so you cannot modify them. I think this is dumb. This script lets you decrypt and re-encrypt your config files so you can modify them as you see fit.

I use this to modify my reserved addresses list because editing them through the web interface is terribly slow and cumbersome.

  1. Go to the router and download the config file from the "Backup & Restore" section of "System Tools".
  2. Run ruby tp.rb config.bin
  3. Edit the created config file config.cfg as you please.
  4. Run ruby tp.rb config.cfg
  5. Return to the "Backup & Restore" section and upload your modified config.bin file.

This work is based on the hack by Matteo Croce here. Thank you for doing the hard part.

#!/usr/bin/env ruby
require 'openssl'
infile = ARGV[0]
content =
mode = infile.end_with?('.bin') ? :decrypt : :encrypt
# Configure the cipher with what we know about the TP-Link encryption
cipher =
cipher.key = ["478DA50BF9E3D2CF"].pack("H*")
cipher.padding = 0
case mode
when :decrypt
plaintext = cipher.update(content) +
# First 16 bytes are checksum of content
reported_checksum = plaintext[0...16]
plaintext = plaintext[16..-1]
# Trim empty bytes used to pad out the plaintext to be multiple of 8
plaintext = plaintext[0...-1] while plaintext.end_with?("\x0")
checksum = OpenSSL::Digest::MD5.digest(plaintext)
if reported_checksum != checksum
$stderr.puts "Checksums do not match"
$stderr.puts "reported: " << { |b| b.to_s(16) }.join
$stderr.puts "actual: " << { |b| b.to_s(16) }.join
File.write(infile.sub('.bin', '.cfg'), plaintext)
when :encrypt
# The body is prefixed with the 16 byte md5 checksum of the contents
checksum = OpenSSL::Digest::MD5.digest(content)
body = checksum + content
# The body needs to be padded out with zero bytes until the byte
# length is a multiple of 8.
remaining = 8 - (body.length % 8)
pad = remaining == 8 ? '' : ("\x0" * remaining)
# Set the cipher to encryption mode and perform the action
ciphertext = cipher.update(body + pad) +
File.write(infile.sub('.cfg', '.bin'), ciphertext)
Copy link

thatoo-apple commented Dec 1, 2020

Hi. First, thank you. It's really great what you have done. I'm trying to decrypt my WR840N, but when I will edit the config.cfg file I got "incompatible character encodings: ASCII-8BIT and UTF-8".

Copy link

Nikhil1O1 commented Apr 14, 2021

$ ruby tp.rb config.bin
tp.rb:18:in update': key not set (OpenSSL::Cipher::CipherError) from tp.rb:18:in


I'm getting this error. Any idea?

Copy link

sengiv commented Apr 29, 2021

This script appears out of date, and no longer functional for newer TL-LINK devices.
tpconf_bin, a similar project is a working alternative to this

Copy link

shreve commented Apr 29, 2021

@sengiv thanks. I'll add that and a deprecation notice to this.

Copy link

ret5et commented May 18, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment