diff --git a/.gitattributes b/.gitattributes
index e1a8d41a888820dabc89e67c404f39e229157bd4..14229eba498fc8297e44d82a1144da9e645c10d5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19,3 +19,4 @@ testfont binary
 /src/modules/_Crypto/md2.c foreign_ident
 /src/modules/_Crypto/md5.c foreign_ident
 /src/modules/_Crypto/md5.pike foreign_ident
+/src/modules/_Crypto/pipe.c foreign_ident
diff --git a/src/modules/_Crypto/pipe.c b/src/modules/_Crypto/pipe.c
new file mode 100644
index 0000000000000000000000000000000000000000..870dd9df6dc8b5b5936f4b4cb8a0994c95f440c8
--- /dev/null
+++ b/src/modules/_Crypto/pipe.c
@@ -0,0 +1,303 @@
+/*
+ * $Id: pipe.c,v 1.1 1996/11/11 19:24:04 grubba Exp $
+ *
+ * PIPE crypto module for Pike.
+ *
+ * /precompiled/crypto/pipe
+ *
+ * Henrik Grubbström 1996-11-11
+ */
+
+/*
+ * Includes
+ */
+
+/* From the Pike distribution */
+#include "global.h"
+#include "stralloc.h"
+#include "interpret.h"
+#include "svalue.h"
+#include "constants.h"
+#include "macros.h"
+#include "threads.h"
+#include "object.h"
+#include "stralloc.h"
+#include "builtin_functions.h"
+#include "operators.h"
+
+/* Module specific includes */
+#include "precompiled_crypto.h"
+
+/*
+ * Globals
+ */
+
+struct program *pike_pipe_program;
+
+/*
+ * Functions
+ */
+
+void init_pike_pipe(struct object *o)
+{
+  MEMSET(PIKE_PIPE, 0, sizeof(struct pike_pipe));
+}
+
+void exit_pike_pipe(struct object *o)
+{
+  int i;
+
+  if (PIKE_PIPE->objects) {
+    for (i=0; i<PIKE_PIPE->num_objs; i++) {
+      free_object(PIKE_PIPE->objects[i]);
+    }
+    MEMSET(PIKE_PIPE->objects, 0,
+	   PIKE_PIPE->num_objs * sizeof(struct object *));
+  }
+  MEMSET(PIKE_PIPE, 0, sizeof(struct pike_pipe));
+}
+
+/*
+ * efuns and the like
+ */
+
+/* void create(program|object|array(program|mixed)) */
+static void f_create(INT32 args)
+{
+  int i;
+  int block_size=1;
+
+  if (!args) {
+    error("Too few arguments to pipe->create()\n");
+  }
+  PIKE_PIPE->objects = (struct object **)xalloc(args * sizeof(struct object *));
+  for (i=-args; i; i++) {
+    if (sp[i].type == T_OBJECT) {
+      PIKE_PIPE->objects[args + i] = sp[i].u.object;
+      PIKE_PIPE->objects[i]->refs++;
+    } else if (sp[i].type == T_PROGRAM) {
+      PIKE_PIPE->objects[i] = clone(sp[i].u.program, 0);
+    } else if (sp[i].type == T_ARRAY) {
+      struct program *prog;
+      INT32 n_args;
+
+      if (!sp[i].u.array->size) {
+	error("pipe->create(): Argument %d: Empty array\n", 1 + args + i);
+      }
+      if (sp[i].u.array->item[0].type != T_PROGRAM) {
+	error("pipe->create(): Argument %d: First element of array must be a program\n",
+	      1 + args + i);
+      }
+      prog = sp[i].u.array->item[0].u.program;
+      n_args = sp[i].u.array->size - 1;
+
+      push_array_items(sp[i].u.array);	/* Pushes one arg too many */
+      PIKE_PIPE->objects[i] = clone(prog, n_args);
+
+      pop_stack();	/* Pop the program */
+
+      assert_is_crypto_module(PIKE_PIPE->objects[i]);
+    } else {
+      error("Bad argument %d to pipe->create()\n", i + args);
+    }
+  }
+  PIKE_PIPE->num_objs = args;
+
+  for (i=0; i<PIKE_PIPE->num_objs; i++) {
+    int j;
+    int sub_size;
+    int factor = 1;
+
+    safe_apply(PIKE_PIPE->objects[i], "block_size", 0);
+    if (sp[-1].type != T_INT) {
+      error("pipe->create(): block_size() returned other than int\n");
+    }
+    sub_size = sp[-1].u.integer;
+    pop_stack();
+
+    for (j=2; j <= sub_size;) {
+      if (!(block_size % j)) {
+	factor *= j;
+	block_size /= j;
+	sub_size /= j;
+      } else {
+	j++;
+      }
+    }
+    block_size *= factor * sub_size;
+  }
+
+  PIKE_PIPE->block_size = block_size;
+
+  pop_n_elems(args);
+}
+
+/* string name(void) */
+static void f_name(INT32 args)
+{
+  int i;
+
+  if (args) {
+    error("Too many arguments to pipe->name()\n");
+  }
+  push_string(make_shared_string("PIPE("));
+  
+  for (i=0; i<PIKE_PIPE->num_objs; i++) {
+    if (i) {
+      push_string(make_shared_binary_string(", ", 2));
+    }
+    safe_apply(PIKE_PIPE->objects[i], "name", 0);
+  }
+  push_string(make_shared_binary_string(")", 1));
+
+  f_add(2*PIKE_PIPE->num_objs + 1);
+}
+
+/* int query_block_size(void) */
+static void f_query_block_size(INT32 args)
+{
+  if (args) {
+    error("Too many arguments to pipe->query_block_size()\n");
+  }
+  push_int(8);
+}
+
+/* array(int|array) query_key_length(void) */
+static void f_query_key_length(INT32 args)
+{
+  int i;
+
+  if (args) {
+    error("Too many arguments to pipe->query_key_length()\n");
+  }
+
+  for (i=0; i<PIKE_PIPE->num_objs; i++) {
+    safe_apply(PIKE_PIPE->objects[i], "query_key_length", 0);
+  }
+  f_aggregate(PIKE_PIPE->num_objs);
+}
+
+/* void set_encrypt_key(array|string ..) */
+static void f_set_encrypt_key(INT32 args)
+{
+  int i;
+
+  if (args != PIKE_PIPE->num_objs) {
+    error("Wrong number of arguments to pipe->set_encrypt_key()\n");
+  }
+  for (i=-args; i; i++) {
+    int n_args;
+
+    if (sp[i].type == T_STRING) {
+      push_string(sp[i].u.string);
+      n_args = 1;
+    } else if (sp[i].type == T_ARRAY) {
+      n_args = sp[i].u.array->size;
+      push_array_items(sp[i].u.array);
+    } else {
+      error("Bad argument %d to pipe->set_encrypt_key()\n", 1 + args + i);
+    }
+    safe_apply(PIKE_PIPE->objects[args + i], "set_encrypt_key", n_args);
+    pop_stack(); /* Get rid of the void value */
+  }
+  pop_n_elems(args);
+}
+
+/* void set_decrypt_key(array|string ..) */
+static void f_set_decrypt_key(INT32 args)
+{
+  int i;
+
+  if (args != PIKE_PIPE->num_objs) {
+    error("Wrong number of arguments to pipe->set_decrypt_key()\n");
+  }
+  for (i=-args; i; i++) {
+    int n_args;
+
+    if (sp[i].type == T_STRING) {
+      push_string(sp[i].u.string);
+      n_args = 1;
+    } else if (sp[i].type == T_ARRAY) {
+      n_args = sp[i].u.array->size;
+      push_array_items(sp[i].u.array);
+    } else {
+      error("Bad argument %d to pipe->set_decrypt_key()\n", 1 + args + i);
+    }
+    safe_apply(PIKE_PIPE->objects[args + i], "set_decrypt_key", n_args);
+    pop_stack(); /* Get rid of the void value */
+  }
+  pop_n_elems(args);
+}
+
+/* string crypt_block(string) */
+static void f_crypt_block(INT32 args)
+{
+  int i;
+
+  if (args != 1) {
+    error("Wrong number of arguments to pipe->crypt_block()\n");
+  }
+  if (sp[-1].type != T_STRING) {
+    error("Bad argument 1 to pipe->crypt_block()\n");
+  }
+  if (sp[-1].u.string->len % PIKE_PIPE->block_size) {
+    error("Bad length of argument 1 to pipe->crypt_block()\n");
+  }
+  for (i=0; i<PIKE_PIPE->num_objs; i++) {
+    safe_apply(PIKE_PIPE->objects[i], "crypt_block", 1);
+  }
+}
+
+/*
+ * Module linkage
+ */
+
+void init_pipe_efuns(void)
+{
+  /* add_efun()s */
+}
+
+void init_pipe_programs(void)
+{
+  /*
+   * start_new_program();
+   *
+   * add_storage();
+   *
+   * add_function();
+   * add_function();
+   * ...
+   *
+   * set_init_callback();
+   * set_exit_callback();
+   *
+   * program = end_c_program();
+   * program->refs++;
+   *
+   */
+
+  /* /precompiled/crypto/pipe */
+  start_new_program();
+  add_storage(sizeof(struct pike_pipe));
+
+  add_function("create", f_create, "function(program|object|array(program|mixed) ...:void)", OPT_SIDE_EFFECT);
+
+  add_function("name", f_name, "function(void:string)", OPT_TRY_OPTIMIZE);
+  add_function("query_block_size", f_query_block_size, "function(void:int)", OPT_TRY_OPTIMIZE);
+  add_function("query_key_length", f_query_key_length, "function(void:int)", OPT_TRY_OPTIMIZE);
+  add_function("set_encrypt_key", f_set_encrypt_key, "function(string:void)", OPT_SIDE_EFFECT);
+  add_function("set_decrypt_key", f_set_decrypt_key, "function(string:void)", OPT_SIDE_EFFECT);
+  add_function("crypt_block", f_crypt_block, "function(string:string)", OPT_EXTERNAL_DEPEND);
+
+  set_init_callback(init_pike_pipe);
+  set_exit_callback(exit_pike_pipe);
+
+  pike_pipe_program = end_c_program("/precompiled/crypto/pipe");
+  pike_pipe_program->refs++;
+}
+
+void exit_pipe(void)
+{
+  /* free_program()s */
+  free_program(pike_pipe_program);
+}