summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile25
-rw-r--r--README.md72
-rwxr-xr-xaskpass58
-rwxr-xr-xcheck266
-rw-r--r--crypt.c163
-rwxr-xr-xhooks/cryptsetup-nuke-password33
6 files changed, 617 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e1d8c0d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+CFLAGS += -Wall -O2
+LDLIBS += -lcrypt
+
+EXECUTABLES = crypt
+
+all: $(EXECUTABLES)
+
+check:
+ ./check
+
+clean:
+ rm -f *.o
+ rm -f $(EXECUTABLES)
+
+install: $(EXECUTABLES)
+ mkdir -p $(DESTDIR)/lib/cryptsetup
+ cp askpass $(DESTDIR)/lib/cryptsetup/
+
+ mkdir -p $(DESTDIR)/usr/share/initramfs-tools/hooks/
+ cp hooks/* $(DESTDIR)/usr/share/initramfs-tools/hooks/
+
+ mkdir -p $(DESTDIR)/usr/lib/cryptsetup-nuke-password
+ cp crypt $(DESTDIR)/usr/lib/cryptsetup-nuke-password/
+
+.PHONY: check clean install
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b452530
--- /dev/null
+++ b/README.md
@@ -0,0 +1,72 @@
+# cryptsetup-nuke-password
+
+Installing this package lets you configure a special "nuke password" that
+can be used to destroy the encryption keys required to unlock the encrypted
+partitions. This password can be entered in the usual early-boot prompt
+asking the passphrase to unlock the encrypted partition(s).
+
+This provides a relatively stealth way to make your data unreadable in
+case you fear that your computer is going to be seized.
+
+## How can I configure the nuke password
+
+After having installed the package, just run “dpkg-reconfigure
+cryptsetup-nuke-password”. Behind the scene, this creates
+`/etc/cryptsetup-nuke-password/password_hash` with
+the output of `echo your-password |
+/usr/lib/cryptsetup-nuke-password/crypt --generate` and rebuilds
+the initramfs (`update-initramfs -u`).
+
+## How does it work?
+
+The packages diverts /lib/cryptsetup/askpass by our own script that will
+call the original tool to get the password but that will do its own
+treatment before outputting the same password to stdout...
+
+To identify the partition being unlocked, we rely on the environment
+variables exported by
+/usr/share/initramfs-tools/scripts/local-top/cryptroot
+
+To destroy the encryption keys, we call `cryptsetup erase <device>`.
+
+## How to configure the nuke password non-interactively
+
+You need to preseed the password in the debconf database and then
+run dpkg-reconfigure in a way where it is not allowed to ask question:
+```
+$ sudo debconf-set-selections <<END
+cryptsetup-nuke-password cryptsetup-nuke-password/password string Th3Pa$$w0rd
+cryptsetup-nuke-password cryptsetup-nuke-password/password-again string Th3Pa$$w0rd
+END
+$ sudo dpkg-reconfigure -f noninteractive cryptsetup-nuke-password
+```
+
+## Backing up the encryption key
+
+If you expect to make use of this nuke password, then you should consider
+backing up the luks header. That way, if your computer is seized, and then
+later given back to you, you will be able to restore the luks headers and
+get back access to your data.
+
+Here's the command to use (replace `<device>` with the path of the device
+file representing your luks encrypted partition, and `<your-backup-file>`
+with the path to the backup file to create):
+```
+$ sudo cryptsetup luksHeaderBackup <device> --header-backup-file <your-backup-file>
+```
+
+To later restore the header, you will have to do:
+```
+$ sudo cryptsetup luksHeaderRestore <device> --header-backup-file <your-backup-file>
+```
+
+## Limitations
+
+Due to the way this feature is implemented, it can't be used if you
+configure cryptsetup to use something else than an interactive password
+to unlock the encryption keys.
+
+It is also not usable on a live image (unless you configured the nuke
+password with debconf preseeding at the time you generated the live image)
+since it needs to regenerate the initrd to embed itself and the nuke
+password.
diff --git a/askpass b/askpass
new file mode 100755
index 0000000..09df0c8
--- /dev/null
+++ b/askpass
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+DIVERTED_ASKPASS=${DIVERTED_ASKPASS:-/lib/cryptsetup/askpass.cryptsetup}
+NUKE_PASSWORD_HASH_PATH=${NUKE_PASSWORD_HASH_PATH:-/etc/cryptsetup-nuke-password/password_hash}
+CRYPT_HELPER=${CRYPT_HELPER:-/usr/lib/cryptsetup-nuke-password/crypt}
+
+sanity_checks() {
+ local cryptsetup="$(which cryptsetup 2>/dev/null)"
+ if [ -z "$cryptsetup" ]; then
+ echo "$0: WARNING: cryptsetup not found in PATH" >&2
+ return 1
+ fi
+ if [ ! -e "$CRYPTTAB_SOURCE" ]; then
+ echo "$0: WARNING: \$CRYPTTAB_SOURCE (value: $CRYPTTAB_SOURCE) does not exist" >&2
+ return 1
+ fi
+ if [ ! -x "$CRYPT_HELPER" ]; then
+ echo "$0: WARNING: $CRYPT_HELPER is not executable" >&2
+ return 1
+ fi
+ return 0
+}
+
+hash_is_matching() {
+ local pass="$1"
+ local pass_hash
+
+ if [ ! -r $NUKE_PASSWORD_HASH_PATH ]; then
+ # No hash, no match
+ return 1
+ fi
+ pass_hash=$(cat $NUKE_PASSWORD_HASH_PATH)
+ if echo -n "$pass" | $CRYPT_HELPER --check "$pass_hash"; then
+ # User typed the nuke password!
+ return 0
+ else
+ return 1
+ fi
+}
+
+nuke_cryptsetup_partition() {
+ local partition="$1"
+ cryptsetup --batch-mode erase "$partition"
+}
+
+if [ ! -x "$DIVERTED_ASKPASS" ]; then
+ echo "ERROR: $DIVERTED_ASKPASS is not available/executable" >&2
+ exit 1
+fi
+
+PASSWORD=$($DIVERTED_ASKPASS "$1")
+
+if sanity_checks && hash_is_matching "$PASSWORD"; then
+ nuke_cryptsetup_partition "$CRYPTTAB_SOURCE"
+fi
+
+# Forward the password
+echo -n "$PASSWORD"
diff --git a/check b/check
new file mode 100755
index 0000000..6d9123f
--- /dev/null
+++ b/check
@@ -0,0 +1,266 @@
+#!/bin/sh
+
+CRYPT=${CRYPT:-./crypt}
+ASKPASS=${ASKPASS:-./askpass}
+
+oneTimeSetUp() {
+ export OUTDIR="$SHUNIT_TMPDIR/out"
+ export BINDIR="$SHUNIT_TMPDIR/bin"
+ export PATH="$BINDIR:$PATH"
+
+ # Directory for output data (dropped after each test)
+ mkdir -p $OUTDIR
+
+ # Test helper scripts
+ mkdir -p $BINDIR
+ cat >$BINDIR/askpass <<END
+#!/bin/sh
+echo -n "\${FAKE_ASKPASS_ANSWER:-foobar}"
+END
+ cat >$BINDIR/cryptsetup <<END
+#!/bin/sh
+echo "\$@" >>$OUTDIR/cryptsetup
+END
+ chmod 755 $BINDIR/*
+}
+
+setUp() {
+ # Overrides for askpass
+ export DIVERTED_ASKPASS="$BINDIR/askpass"
+ export CRYPT_HELPER="$CRYPT"
+ export NUKE_PASSWORD_HASH_PATH="$SHUNIT_TMPDIR/password_hash"
+ export CRYPTTAB_SOURCE="$SHUNIT_TMPDIR/device"
+ touch $CRYPTTAB_SOURCE
+
+ # Clean up some environment variables that might be set by tests
+ unset FAKE_ASKPASS_ANSWER
+}
+
+tearDown() {
+ if [ -d "$OUTDIR" ]; then
+ rm -f $OUTDIR/*
+ fi
+ rm -f $NUKE_PASSWORD_HASH_PATH
+}
+
+testCryptNoArgsOutput() {
+ output=$($CRYPT 2>&1 </dev/null)
+ if ! echo "$output" | grep -q "Usage:"; then
+ fail 'Does not print Usage output without argument'
+ fi
+}
+testCryptNoArgsExitCode() {
+ $CRYPT >/dev/null 2>&1 </dev/null
+ assertEquals 'Bad exit code when ran without argument' 1 $?
+}
+
+testCryptBadArgOutput() {
+ output=$($CRYPT --doesnotexist 2>&1 </dev/null)
+ if ! echo "$output" | grep -q "Usage:"; then
+ fail 'Does not print Usage output with invalid argument'
+ fi
+}
+testCryptBadArgExitCode() {
+ $CRYPT --doesnotexist >/dev/null 2>&1 </dev/null
+ assertEquals 'Bad exit code when ran with invalid argument' 1 $?
+}
+
+testCryptHelpOutput() {
+ output=$($CRYPT --help 2>&1 </dev/null)
+ if ! echo "$output" | grep -q "Usage:"; then
+ fail 'Does not print Usage output with --help'
+ fi
+}
+testCryptHelpExitCode() {
+ $CRYPT --help >/dev/null 2>&1 </dev/null
+ assertEquals 'Bad exit code when ran with --help' 0 $?
+}
+
+testCryptGenerateEmptyPassword() {
+ output_stdout=$($CRYPT --generate 'h/' 2>/dev/null </dev/null)
+ output_stderr=$($CRYPT --generate 'h/' 2>&1 >/dev/null </dev/null)
+ exit_code=$?
+ assertNull "'crypt --generate </dev/null' unexpectedly generated something" "$output_stdout"
+ if ! echo "$output_stderr" | grep -q "ERROR:"; then
+ fail "'crypt --generate </dev/null' did not print any error message"
+ fi
+ assertEquals "'crypt --generate </dev/null' has a bad exit code" 1 $exit_code
+}
+
+do_testCryptGenerate() {
+ output=$(echo "foobar" | $CRYPT --generate $1)
+ exit_code=$?
+ assertNotNull "'echo foobar | crypt --generate $1' ($2) provided no output" "$output"
+ assertEquals "'echo foobar | crypt --generate $1' ($2) returned bad exit code" 0 $exit_code
+}
+
+testCryptGenerateBasic() {
+ do_testCryptGenerate 'h/' 'DES'
+}
+testCryptGenerateMD5() {
+ do_testCryptGenerate '$1$h/$' 'MD5'
+}
+testCryptGenerateSHA256() {
+ do_testCryptGenerate '$5$h/$' 'SHA-256'
+}
+testCryptGenerateSHA512() {
+ do_testCryptGenerate '$6$h/$' 'SHA-512'
+}
+
+testCryptGenerateBadSalt() {
+ output_stdout=$(echo foobar | $CRYPT --generate '$999$foobar$' 2>/dev/null)
+ output_stderr=$(echo foobar | $CRYPT --generate '$999$foobar$' 2>&1 >/dev/null)
+ exit_code=$?
+ assertNull "'echo foobar | crypt --generate <bad-salt>' unexpectedly generated something" "$output_stdout"
+ if ! echo "$output_stderr" | grep -q "ERROR:"; then
+ fail "'echo foobar | crypt --generate <bad-salt>' did not print any error message"
+ fi
+ assertEquals "'echo foobar | crypt --generate <bad-salt>' has a bad exit code" 1 $exit_code
+}
+
+testCryptGenerateNoSaltSupplied() {
+ output_stdout=$(echo foobar | $CRYPT --generate 2>/dev/null)
+ output_stderr=$(echo foobar | $CRYPT --generate 2>&1 >/dev/null)
+ exit_code=$?
+ assertNull "'echo foobar | crypt --generate' generated noise on stderr" "$output_stderr"
+ if ! echo "$output_stdout" | grep -q '^\$6\$'; then
+ fail "'echo foobar | crypt --generate' did not generate a SHA-512 based hash ($output_stdout)"
+ fi
+ assertEquals "'echo foobar | crypt --generate' has a bad exit code" 0 $exit_code
+}
+
+testCryptGenerateEmptySaltSupplied() {
+ output_stdout=$(echo foobar | $CRYPT --generate '' 2>/dev/null)
+ output_stderr=$(echo foobar | $CRYPT --generate '' 2>&1 >/dev/null)
+ exit_code=$?
+ assertNull "'echo foobar | crypt --generate ''' generated noise on stderr" "$output_stderr"
+ if ! echo "$output_stdout" | grep -q '^\$6\$'; then
+ fail "'echo foobar | crypt --generate ''' did not generate a SHA-512 based hash ($output_stdout)"
+ fi
+ assertEquals "'echo foobar | crypt --generate ''' has a bad exit code" 0 $exit_code
+}
+
+testCryptGenerateNoSaltRandomness() {
+ output1=$(echo foobar | $CRYPT --generate 2>/dev/null)
+ output2=$(echo foobar | $CRYPT --generate 2>/dev/null)
+ salt1="$(echo $output1 | cut -d$ -f1-3)"'$'
+ salt2="$(echo $output2 | cut -d$ -f1-3)"'$'
+ assertNotEquals "Two consecutive runs of 'echo foobar | $CRYPT --generate' generated the same salt" "$output1" "$output2"
+ output3=$(echo foobar | $CRYPT --generate "$salt1")
+ assertEquals "'echo foobar | $CRYPT --generate <salt>' did not recreate the original hash" "$output1" "$output3"
+}
+
+testCryptCheckEmptyPassword() {
+ output_stdout=$($CRYPT --check 'h/GdiFWQsXxA.' 2>/dev/null </dev/null)
+ output_stderr=$($CRYPT --check 'h/GdiFWQsXxA.' 2>&1 >/dev/null </dev/null)
+ exit_code=$?
+ assertNull "'crypt --check <hash> </dev/null' unexpectedly generated something" "$output_stdout"
+ if ! echo "$output_stderr" | grep -q "ERROR:"; then
+ fail "'crypt --check <hash> </dev/null' did not print any error message"
+ fi
+ assertEquals "'crypt --check <hash> </dev/null' has a bad exit code" 1 $exit_code
+}
+
+testCryptCheckNoHashSupplied() {
+ output_stdout=$(echo "foobar" | $CRYPT --check 2>/dev/null)
+ output_stderr=$(echo "foobar" | $CRYPT --check 2>&1 >/dev/null)
+ if ! echo "$output_stderr" | grep -q "ERROR:"; then
+ fail "'echo foobar | crypt --check' did not print any error message"
+ fi
+ assertNull "'echo foobar | crypt --check' unexpectly returned something on stdout" "$output_stdout"
+}
+
+testCryptCheckGoodPassword() {
+ output=$(echo "foobar" | $CRYPT --check '$6$dkcZzIkv$Ju7XCIc4igWvht3bOu266vvRam6IdnIFxoyonDt.6JZl8NfCaukACeIRYVW7WQtrUtqN2TrWSgEFnXumuTiN41' 2>&1)
+ exit_code=$?
+ assertNull "'echo foobar | $CRYPT --check <good-hash>' printed unexpected output" "$output"
+ assertEquals "'echo foobar | $CRYPT --check <good-hash>' did not exit with" 0 $exit_code
+}
+
+testCryptCheckBadPassword() {
+ output=$(echo "foobar-bad" | $CRYPT --check '$6$dkcZzIkv$Ju7XCIc4igWvht3bOu266vvRam6IdnIFxoyonDt.6JZl8NfCaukACeIRYVW7WQtrUtqN2TrWSgEFnXumuTiN41' 2>&1)
+ exit_code=$?
+ assertNull "'echo foobar | $CRYPT --check <bad-hash>' printed unexpected output" "$output"
+ assertEquals "'echo foobar | $CRYPT --check <bad-hash>' did not exit with" 1 $exit_code
+}
+
+testCryptGenerateCheckRoundtrip() {
+ for salt in '' 'h/' '$1$abcd$' '$5$12345678$' '$6$deadbeef$'
+ do
+ password="haX0rd3ad"
+
+ password_hash=$(echo $password | $CRYPT --generate "$salt")
+ exit_code=$?
+ assertEquals "'echo $password | $CRYPT --generate $salt' did not exit with" 0 $exit_code
+
+ echo $password | $CRYPT --check "$password_hash"
+ exit_code=$?
+ assertEquals "'echo $password | $CRYPT --check $password_hash' did not exit with" 0 $exit_code
+ done
+}
+
+testAskPassWarnsAboutMissingCrypttabSource() {
+ export CRYPTTAB_SOURCE=/does/not/exist
+
+ $ASKPASS >$OUTDIR/log 2>&1
+
+ if ! grep -q 'WARNING: $CRYPTTAB_SOURCE' $OUTDIR/log; then
+ fail "askpass should complain of missing CRYPTTAB_SOURCE"
+ fi
+}
+
+testAskPassWarnsAboutMissingCryptHelper() {
+ export CRYPT_HELPER=/does/not/exist
+
+ $ASKPASS >$OUTDIR/log 2>&1
+
+ if ! grep -q "WARNING: $CRYPT_HELPER" $OUTDIR/log; then
+ fail "askpass should complain of missing \$CRYPT_HELPER"
+ fi
+}
+
+testAskPassCallsCryptsetupErase() {
+ # Setup the password and its matching hash
+ export FAKE_ASKPASS_ANSWER="foobar"
+ echo '$6$dkcZzIkv$Ju7XCIc4igWvht3bOu266vvRam6IdnIFxoyonDt.6JZl8NfCaukACeIRYVW7WQtrUtqN2TrWSgEFnXumuTiN41' >$NUKE_PASSWORD_HASH_PATH
+
+ $ASKPASS >$OUTDIR/log 2>&1
+
+ touch $OUTDIR/cryptsetup
+ if ! grep -q "erase $CRYPTTAB_SOURCE" $OUTDIR/cryptsetup; then
+ echo "Output of askpass:"
+ cat $OUTDIR/log
+ echo ""
+ echo "Cryptsetup log:"
+ cat $OUTDIR/cryptsetup
+ fail "cryptsetup erase has not been called by askpass"
+ fi
+}
+
+testAskPassWithoutPasswordHash() {
+ # No password_hash is created
+
+ $ASKPASS >$OUTDIR/log 2>&1
+
+ assertFalse 'cryptsetup was unexpectly run' "[ -e $OUTDIR/cryptsetup ]"
+}
+
+testAskPassWithNonMatchingPasswordHash() {
+ # Setup the password and a non-matching hash
+ export FAKE_ASKPASS_ANSWER="this-is-not-the-good-password"
+ echo '$6$dkcZzIkv$Ju7XCIc4igWvht3bOu266vvRam6IdnIFxoyonDt.6JZl8NfCaukACeIRYVW7WQtrUtqN2TrWSgEFnXumuTiN41' >$NUKE_PASSWORD_HASH_PATH
+
+ $ASKPASS >$OUTDIR/log 2>&1
+
+ assertFalse 'cryptsetup was unexpectly run' "[ -e $OUTDIR/cryptsetup ]"
+}
+
+testAskPassReturnsPassword() {
+ export FAKE_ASKPASS_ANSWER="my-password"
+
+ OUT=$($ASKPASS 2>/dev/null)
+
+ assertEquals "askpass did not print the password" "$FAKE_ASKPASS_ANSWER" "$OUT"
+}
+
+. shunit2
diff --git a/crypt.c b/crypt.c
new file mode 100644
index 0000000..65b55d0
--- /dev/null
+++ b/crypt.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2019 Offensive Security
+ * Copyright 2019 Raphaël Hertzog <raphael@offensive-security.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/random.h>
+#include <crypt.h>
+
+void
+usage(const char* name, int exitcode)
+{
+ fprintf(stderr, "Usage: %s <command>\n", name);
+ fprintf(stderr, "Valid commands:\n");
+ fprintf(stderr, " --help: display this output\n");
+ fprintf(stderr, " --generate <salt>: read password from stdin and print hashed version\n");
+ fprintf(stderr, " --check <hash>: read password from stdin and check it against supplied hash\n");
+ fprintf(stderr, " Exits with error 0 when password matched, 1 otherwise.\n");
+ exit(exitcode);
+}
+
+void
+read_password(char *buffer, int bufsize)
+{
+ int size = -1, total = 0, i = 0;
+
+ while ((size != 0) && (total < bufsize - 1)) {
+ size = read(0, buffer+total, bufsize - total);
+ if (size != -1) {
+ total += size;
+ }
+ }
+ buffer[total+1] = '\0';
+ for(i = 0; i < total; i++) {
+ if ((buffer[i] == '\n') || (buffer[i] == '\r')) {
+ buffer[i] = '\0';
+ break;
+ }
+ }
+ if (strlen(buffer) == 0) {
+ fprintf(stderr, "ERROR: no password supplied");
+ exit(1);
+ }
+}
+
+char*
+gen_salt()
+{
+ static const char alphanum[] = "123456789abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ static char salt[16];
+ int i, index;
+ unsigned int seed;
+ ssize_t ret;
+
+ ret = getrandom(&seed, sizeof(seed), 0);
+ if (ret != sizeof(seed)) {
+ fprintf(stderr, "ERROR: failed to get random data\n");
+ exit(2);
+ }
+ srandom(seed);
+
+ salt[0] = '$';
+ salt[1] = '6';
+ salt[2] = '$';
+ for (i = 3; i < 11; i++) {
+ index = random() % (sizeof(alphanum) - 1);
+ salt[i] = alphanum[index];
+ }
+ salt[11] = '$';
+ salt[12] = '\0';
+ return salt;
+}
+
+char*
+extract_salt(char* hash)
+{
+ static char salt[24];
+
+ if (hash[0] != '$') {
+ /* Traditional crypt hash with 2 characters */
+ salt[0] = hash[0];
+ salt[1] = hash[1];
+ salt[2] = '\0';
+ } else {
+ /* Hash delimited by $ characters */
+ char *p, *end;
+ int i;
+
+ p = hash;
+ end = strrchr(hash, '$');
+ for(i = 0; p <= end && i < sizeof(salt) - 1; i++) {
+ salt[i] = *p;
+ p++;
+ }
+ salt[i] = '\0';
+ }
+ return salt;
+}
+
+int
+main (int argc, char* argv[])
+{
+ static char buffer[256];
+ char *crypted, *salt;
+
+ if (argc == 1) {
+ usage(argv[0], 1);
+ }
+
+ if (strcmp(argv[1], "--help") == 0) {
+ usage(argv[0], 0);
+ } else if (strcmp(argv[1], "--generate") == 0) {
+ read_password(buffer, sizeof(buffer));
+ salt = (argc > 2 && strlen(argv[2])) ? argv[2] : gen_salt();
+ crypted = crypt(buffer, salt);
+ if (crypted == NULL || (
+ strlen(crypted) < 13 &&
+ strncmp(crypted, "*", 1) == 0 &&
+ strcmp(crypted, salt) != 0
+ )) {
+ fprintf(stderr, "ERROR: crypt() failed to return anything with salt '%s'", salt);
+ exit(1);
+ }
+ printf("%s\n", crypted);
+ } else if (strcmp(argv[1], "--check") == 0) {
+ read_password(buffer, sizeof(buffer));
+ if (argc < 3) {
+ fprintf(stderr, "ERROR: missing <hash> argument after --check");
+ exit(1);
+ }
+ salt = extract_salt(argv[2]);
+ crypted = crypt(buffer, salt);
+ if (crypted == NULL)
+ exit(1);
+ if (strcmp(crypted, argv[2]) == 0)
+ exit(0);
+ else
+ exit(1);
+ } else {
+ usage(argv[0], 1);
+ }
+
+ return 0;
+}
+
diff --git a/hooks/cryptsetup-nuke-password b/hooks/cryptsetup-nuke-password
new file mode 100755
index 0000000..d350966
--- /dev/null
+++ b/hooks/cryptsetup-nuke-password
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -e
+
+PREREQ="cryptroot"
+
+prereqs()
+{
+ echo "$PREREQ"
+}
+
+case "$1" in
+ prereqs)
+ prereqs
+ exit 0
+ ;;
+esac
+
+. /usr/share/initramfs-tools/hook-functions
+
+# Copy the file that we diverted away and that does the real work
+if [ -e /lib/cryptsetup/askpass.cryptsetup ]; then
+ copy_exec /lib/cryptsetup/askpass.cryptsetup
+fi
+
+# Copy the password hash to recognize the nuke password
+if [ -e /etc/cryptsetup-nuke-password/password_hash ]; then
+ copy_file conffile /etc/cryptsetup-nuke-password/password_hash
+fi
+
+copy_exec /usr/lib/cryptsetup-nuke-password/crypt
+
+exit 0