diff --git a/.gitattributes b/.gitattributes
index f52fd70ecd691d75de2b3725a4c8ea448846f8e5..4f158de9a4c0e0a156ce174e6100cb83ee00b608 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -55,6 +55,7 @@ testfont binary
 /lib/modules/Sql.pmod/postgres.pike foreign_ident
 /lib/modules/Sql.pmod/sql.pike foreign_ident
 /lib/modules/Sql.pmod/sql_result.pike foreign_ident
+/lib/modules/Standards.pmod/ASN1.pmod/decode.pike foreign_ident
 /src/backend.c foreign_ident
 /src/builtin_functions.c foreign_ident
 /src/configure.in foreign_ident
diff --git a/lib/modules/Standards.pmod/ASN1.pmod/Encode.pmod b/lib/modules/Standards.pmod/ASN1.pmod/Encode.pmod
new file mode 100644
index 0000000000000000000000000000000000000000..9b38843ebe483287d6a0845496c1a8b4db8d12c6
--- /dev/null
+++ b/lib/modules/Standards.pmod/ASN1.pmod/Encode.pmod
@@ -0,0 +1,255 @@
+/* encode.pmod
+ *
+ * Encodes various asn.1 objects according to the Distinguished
+ * Encoding Rules (DER) */
+
+#if 0
+#define WERROR werror
+#else
+#define WERROR(x)
+#endif
+
+class asn1_object
+{
+  constant cls = 0;
+  constant tag = 0;
+  constant constructed = 0;
+  
+  int get_cls() { return cls; }
+  int get_tag() { return tag; }
+
+  string to_base_128(int n)
+    {
+      /* Convert tag number to base 128 */
+      array(int) digits = ({ });
+
+      /* Array is built in reverse order, least significant digit first */
+      while(n)
+      {
+	digits += ({ (n & 0x7f) | 0x80 });
+	n >>= 7;
+      }
+      digits[0] &= 0x7f;
+
+      return sprintf("%@c", reverse(digits));
+    }
+  
+  string encode_tag()
+    {
+      int tag = get_tag();
+      int cls = get_cls();
+      if (tag < 31)
+	return sprintf("%c", (cls << 6) | (constructed << 5) | tag);
+
+      return sprintf("%c%s", (cls << 6) | (constructed << 5) | 0x1f,
+		     to_base_128(tag) );
+    }
+
+  string encode_length(int|object len)
+    {
+      if (len < 0x80)
+	return sprintf("%c", len);
+      string s = Gmp.mpz(len)->digits(256);
+      if (strlen(s) >= 0x80)
+	throw( ({ "asn1.encode.asn1_object->encode_length: Max length exceeded.\n",
+		  backtrace() }) );
+      return sprintf("%c%s", strlen(s) | 0x80, s);
+    }
+  
+  string build_der(string contents)
+    {
+      string data = encode_tag() + encode_length(strlen(contents)) + contents;
+      // WERROR(sprintf("build_der: '%s'\n", Crypto.string_to_hex(data)));
+      WERROR(sprintf("build_der: '%s'\n", data));
+      return data;
+    }
+}
+
+class asn1_compound
+{
+  inherit asn1_object;
+
+  constant constructed = 1;
+  array(object) elements;
+
+  void create(object ...args)
+    {
+      elements = args;
+      WERROR(sprintf("asn1_compound: %O\n", elements));
+    }
+}
+
+class asn1_integer
+{
+  inherit asn1_object;
+  constant tag = 2;
+  
+  object value;
+
+  void create(int|object n) { value = Gmp.mpz(n); }
+
+  string der()
+    {
+      string s;
+      
+      if (value < 0)
+      {
+	object n = value + Gmp.pow(256, (- value)->size(256));
+	s = n->digits(256);
+	if (!(s[0] & 0x80))
+	  s = "\377" + s;
+      } else {
+	s = value->digits(256);
+	if (s[0] & 0x80)
+	  s = "\0" + s;
+      }
+      return build_der(s);
+    }
+}
+
+class asn1_bitstring
+{
+  inherit asn1_object;
+  constant tag = 3;
+
+  string value;
+  int unused = 0;
+  
+  void create(string s) { value = s; }
+
+  string der()
+    {
+      return build_der(sprintf("%c%s", unused, value));
+    }
+
+  int set_length(int len)
+    {
+      if (len)
+      {
+	value = value[..(len + 7)/8];
+	unused = (- len) % 8;
+	value = sprintf("%s%c", value[..strlen(value)-2], value[-1]
+		    & ({ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 })[unused]);
+      } else {
+	unused = 0;
+	value = "";
+      }
+    }  
+}
+
+class asn1_octet_string
+{
+  inherit asn1_object;
+  constant tag = 4;
+  string value;
+
+  void create(string s) { value = s; }
+
+  string der()
+    {
+      return build_der(value);
+    }
+}
+
+class asn1_null
+{
+  inherit asn1_object;
+  constant tag = 5;
+
+  string der() { return build_der(""); }
+}
+
+class asn1_identifier
+{
+  inherit asn1_object;
+  constant tag = 6;
+
+  array(int) id;
+
+  void create(int ...args)
+    {
+      if ( (sizeof(args) < 2)
+	   || (args[0] > 2)
+	   || (args[1] >= ( (args[0] < 2) ? 40 : 176) ))
+	throw( ({ "asn1.encode.asn1_identifier->create: Invalid object identifier.\n",
+		  backtrace() }) );
+      id = args;
+    }
+
+  object append(int ...args)
+    {
+      return object_program(this_object())(@id, @args);
+    }
+  
+  string der()
+    {
+      return build_der(sprintf("%c%@s", 40 * id[0] + id[1],
+			       Array.map(id[2..], to_base_128)));
+    }
+}
+
+class asn1_sequence
+{
+  inherit asn1_compound;
+  constant tag = 16;
+
+  string der()
+    {
+      return build_der(`+("", @Array.map(elements, "der")));
+    }
+}
+
+class asn1_set
+{
+  inherit asn1_compound;
+  constant tag = 17;
+
+  int compare_octet_strings(string r, string s)
+    {
+      for(int i = 0;; i++)
+      {
+	if (i == strlen(r))
+	  return (i = strlen(s)) ? 0 : 1;
+	if (i == strlen(s))
+	  return -1;
+	if (r[i] < s[i])
+	  return 1;
+	else if (r[i] > s[i])
+	  return -1;
+      }
+    }
+
+  string der()
+    {
+      WERROR(sprintf("asn1_set->der: elements = '%O\n",
+		     elements));
+      WERROR(sprintf("asn1_set->der: der(elements) = '%O\n",
+		     Array.map(elements, "der")));
+      return build_der(`+("", @Array.sort_array(Array.map(elements, "der"),
+						compare_octet_strings)));
+    }
+}
+
+class asn1_printable_string
+{
+  inherit asn1_octet_string;
+  constant tag = 19;
+}
+
+class asn1_T61_string
+{
+  inherit asn1_octet_string;
+  constant tag = 20;
+}
+
+class asn1_IA5_string
+{
+  inherit asn1_octet_string;
+  constant tag = 22;
+}
+
+class asn1_utc
+{
+  inherit asn1_octet_string;
+  constant tag = 23;
+}
diff --git a/lib/modules/Standards.pmod/ASN1.pmod/decode.pike b/lib/modules/Standards.pmod/ASN1.pmod/decode.pike
new file mode 100644
index 0000000000000000000000000000000000000000..ded17b5530e1b135b9959bd0b8461f1870e25b35
--- /dev/null
+++ b/lib/modules/Standards.pmod/ASN1.pmod/decode.pike
@@ -0,0 +1,117 @@
+/* decode.pike
+ *
+ * Rudimentary support for decoding ASN.1 encoded data.
+ *
+ * $Id: decode.pike,v 1.1 1997/11/30 11:36:37 nisse Exp $
+ */
+
+/* BER/DER decoder
+ *
+ * Values are represented as arrays ({ tag, value }).
+ * Tag is either an integer tag number, or a string, in case
+ * the tag recognized.
+ *
+ * Values are strings, integers, or arrays */
+
+inherit ADT.struct;
+
+array get_asn1()
+{
+  int|string tag = get_int(1);
+  int len;
+  string contents;
+  mixed value;
+    
+#ifdef SSL3_DEBUG
+  werror(sprintf("decoding tag %x\n", tag));
+#endif
+  if ( (tag & 0x1f) == 0x1f)
+    throw( ({ "high tag numbers is not supported\n", backtrace() }) );
+  int len = get_int(1);
+  if (len & 0x80)
+    len = get_int(len & 0x7f);
+    
+#ifdef SSL3_DEBUG
+  werror(sprintf("len : %d\n", len));
+#endif
+  contents = get_fix_string(len);
+#ifdef SSL3_DEBUG
+  werror(sprintf("contents: %O\n", contents));
+#endif
+  value = contents; /* Default is no conversion */
+  if (tag & 0x20)
+  {
+    object seq = object_program(this_object())(contents);
+    value = ({ });
+    while(! seq->is_empty())
+    {
+      array elem = seq->get_asn1();
+#ifdef SSL3_DEBUG
+      // werror(sprintf("elem: %O\n", elem));
+#endif
+      value += ({ elem });
+    }
+  }
+  switch(tag & 0xdf)
+  {
+  case 1: /* Boolean */
+    if (strlen(contents) != 1)
+      throw( ({ "SSL.asn1: Invalid boolean value.\n", backtrace() }) );
+    tag = "BOOLEAN";
+    value = !!contents[0];
+    break;
+  case 2: /* Integer */
+    tag = "INTEGER";
+    value = Gmp.mpz(contents, 256);
+    if (contents[0] & 0x80)  /* Negative */
+      value -= Gmp.pow(256, strlen(contents));
+    break;
+  case 3: /* Bit string */
+    tag = "BIT STRING";
+    break;
+  case 4: /* Octet string */
+    tag = "OCTET STRING";
+    break;
+  case 5: /* Null */
+    if (strlen(contents))
+      throw( ({ "SSL.asn1: Invalid NULL value.\n", backtrace() }) );
+    tag = "NULL";
+    value = 0;
+    break;
+  case 6: /* Object id */
+  {
+    tag = "Identifier";
+    if (contents[0] < 120)
+      value = ({ contents[0] / 40, contents[0] % 40 });
+    else
+      value = ({ 2, contents[0] - 80 });
+    int index = 1;
+    while(index < strlen(contents))
+    {
+      int id = 0;
+      do
+      {
+	id = id << 7 | (contents[index] & 0x7f);
+      } while(contents[index++] & 0x80);
+      value += ({ id });
+    }
+    break;
+  }
+  case 9: /* Real */
+    tag = "REAL";
+    break;
+  case 10: /* Enumerated */
+    tag = "ENUMERATED";
+    break;
+  case 16: /* Sequence */
+    tag = "SEQUENCE";
+    break;
+  case 17: /* Set */
+    tag = "SET";
+    break;
+  default: /* Keep numeric tag */
+    break;
+  }
+      
+  return ({ tag, value });
+}