gnutls-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU gnutls branch, master, updated. gnutls_3_1_5-3-g55ff4cd


From: Nikos Mavrogiannopoulos
Subject: [SCM] GNU gnutls branch, master, updated. gnutls_3_1_5-3-g55ff4cd
Date: Sat, 24 Nov 2012 18:54:49 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU gnutls".

http://git.savannah.gnu.org/cgit/gnutls.git/commit/?id=55ff4cdd66d1761d18b2b7e2cb268eac5a355961

The branch, master has been updated
       via  55ff4cdd66d1761d18b2b7e2cb268eac5a355961 (commit)
       via  435cd838a8a1e1a5af6c3e7ea82fe5f1bd0b0552 (commit)
       via  6be35136333b5d6289f23209cf896e741462909a (commit)
      from  bc9bea2b3d5c19c209415000863afc1b7434343b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 55ff4cdd66d1761d18b2b7e2cb268eac5a355961
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Sat Nov 24 19:36:17 2012 +0100

    updated

commit 435cd838a8a1e1a5af6c3e7ea82fe5f1bd0b0552
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Sat Nov 24 19:36:10 2012 +0100

    require libtasn1 3.1 or later

commit 6be35136333b5d6289f23209cf896e741462909a
Author: Nikos Mavrogiannopoulos <address@hidden>
Date:   Sat Nov 24 13:31:02 2012 +0100

    rewritten ASN.1 handling string subsystems to use the new libtasn1 APIs.

-----------------------------------------------------------------------

Summary of changes:
 NEWS                    |    8 +
 cross.mk                |    2 +-
 lib/gnutls_asn1_tab.c   |    2 +-
 lib/pkix_asn1_tab.c     |   62 ++--
 lib/tpm.c               |    4 +-
 lib/x509/common.c       |  829 ++++++++++++++++++++++++++++-------------------
 lib/x509/common.h       |   31 +--
 lib/x509/crl.c          |   12 +-
 lib/x509/crq.c          |   89 +++---
 lib/x509/dn.c           |  232 ++------------
 lib/x509/extensions.c   |    4 +-
 lib/x509/mpi.c          |    2 +-
 lib/x509/ocsp.c         |   10 +-
 lib/x509/pkcs12.c       |   34 +--
 lib/x509/pkcs12_bag.c   |   12 +-
 lib/x509/privkey.c      |    2 +-
 lib/x509/x509.c         |   44 ++-
 lib/x509/x509_int.h     |   10 +-
 lib/x509/x509_write.c   |    6 +-
 m4/hooks.m4             |    2 +-
 tests/crq_apis.c        |   10 +-
 tests/set_pkcs12_cred.c |   11 +
 22 files changed, 719 insertions(+), 699 deletions(-)

diff --git a/NEWS b/NEWS
index abc1d5c..d85daa3 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,14 @@ GnuTLS NEWS -- History of user-visible changes.                
-*- outline -*-
 Copyright (C) 2000-2012 Free Software Foundation, Inc.
 See the end for copying conditions.
 
+* Version 3.1.6 (unreleased)
+
+** libgnutls: Several updates in the ASN.1 string handling subsystem.
+
+** API and ABI modifications:
+No changes since last version.
+
+
 * Version 3.1.5 (released 2012-11-24)
 
 ** libgnutls: Added functions to parse the certificates policies
diff --git a/cross.mk b/cross.mk
index 9b5ba2c..af3dacd 100644
--- a/cross.mk
+++ b/cross.mk
@@ -1,4 +1,4 @@
-GNUTLS_VERSION:=3.1.4
+GNUTLS_VERSION:=3.1.5
 GNUTLS_FILE:=gnutls-$(GNUTLS_VERSION).tar.xz
 GNUTLS_DIR:=gnutls-$(GNUTLS_VERSION)
 
diff --git a/lib/gnutls_asn1_tab.c b/lib/gnutls_asn1_tab.c
index 1079523..5ba1cc9 100644
--- a/lib/gnutls_asn1_tab.c
+++ b/lib/gnutls_asn1_tab.c
@@ -4,7 +4,7 @@
 
 #include <libtasn1.h>
 
-const ASN1_ARRAY_TYPE gnutls_asn1_tab[] = {
+const asn1_static_node gnutls_asn1_tab[] = {
   { "GNUTLS", 536872976, NULL },
   { NULL, 1073741836, NULL },
   { "RSAPublicKey", 1610612741, NULL },
diff --git a/lib/pkix_asn1_tab.c b/lib/pkix_asn1_tab.c
index 9c56d1d..afa6a77 100644
--- a/lib/pkix_asn1_tab.c
+++ b/lib/pkix_asn1_tab.c
@@ -4,7 +4,7 @@
 
 #include <libtasn1.h>
 
-const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
+const asn1_static_node pkix_asn1_tab[] = {
   { "PKIX1", 536875024, NULL },
   { NULL, 1073741836, NULL },
   { "id-pkix", 1879048204, NULL },
@@ -16,9 +16,9 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "mechanisms", 1073741825, "5"},
   { "pkix", 1, "7"},
   { "PrivateKeyUsagePeriod", 1610612741, NULL },
-  { "notBefore", 1619025937, NULL },
+  { "notBefore", 1610637349, NULL },
   { NULL, 4104, "0"},
-  { "notAfter", 545284113, NULL },
+  { "notAfter", 536895525, NULL },
   { NULL, 4104, "1"},
   { "AuthorityKeyIdentifier", 1610612741, NULL },
   { "keyIdentifier", 1610637314, "KeyIdentifier"},
@@ -31,17 +31,17 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "SubjectKeyIdentifier", 1073741826, "KeyIdentifier"},
   { "KeyUsage", 1073741830, NULL },
   { "DirectoryString", 1610612754, NULL },
-  { "teletexString", 1612709890, "TeletexString"},
+  { "teletexString", 1612709918, NULL },
   { "MAX", 524298, "1"},
-  { "printableString", 1612709890, "PrintableString"},
+  { "printableString", 1612709919, NULL },
   { "MAX", 524298, "1"},
-  { "universalString", 1612709890, "UniversalString"},
+  { "universalString", 1612709920, NULL },
   { "MAX", 524298, "1"},
-  { "utf8String", 1612709890, "UTF8String"},
+  { "utf8String", 1612709922, NULL },
   { "MAX", 524298, "1"},
-  { "bmpString", 1612709890, "BMPString"},
+  { "bmpString", 1612709921, NULL },
   { "MAX", 524298, "1"},
-  { "ia5String", 538968066, "IA5String"},
+  { "ia5String", 538968093, NULL },
   { "MAX", 524298, "1"},
   { "SubjectAltName", 1073741826, "GeneralNames"},
   { "GeneralNames", 1612709899, NULL },
@@ -50,9 +50,9 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "GeneralName", 1610612754, NULL },
   { "otherName", 1610620930, "AnotherName"},
   { NULL, 4104, "0"},
-  { "rfc822Name", 1610620930, "IA5String"},
+  { "rfc822Name", 1610620957, NULL },
   { NULL, 4104, "1"},
-  { "dNSName", 1610620930, "IA5String"},
+  { "dNSName", 1610620957, NULL },
   { NULL, 4104, "2"},
   { "x400Address", 1610620941, NULL },
   { NULL, 4104, "3"},
@@ -60,7 +60,7 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { NULL, 2056, "4"},
   { "ediPartyName", 1610620941, NULL },
   { NULL, 4104, "5"},
-  { "uniformResourceIdentifier", 1610620930, "IA5String"},
+  { "uniformResourceIdentifier", 1610620957, NULL },
   { NULL, 4104, "6"},
   { "iPAddress", 1610620935, NULL },
   { NULL, 4104, "7"},
@@ -174,8 +174,8 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "notBefore", 1073741826, "Time"},
   { "notAfter", 2, "Time"},
   { "Time", 1610612754, NULL },
-  { "utcTime", 1090519057, NULL },
-  { "generalTime", 8388625, NULL },
+  { "utcTime", 1073741860, NULL },
+  { "generalTime", 37, NULL },
   { "UniqueIdentifier", 1073741830, NULL },
   { "SubjectPublicKeyInfo", 1610612741, NULL },
   { "algorithm", 1073741826, "AlgorithmIdentifier"},
@@ -227,20 +227,20 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "g", 3, NULL },
   { "CountryName", 1610620946, NULL },
   { NULL, 1073746952, "1"},
-  { "x121-dcc-code", 1612709890, "NumericString"},
+  { "x121-dcc-code", 1612709916, NULL },
   { NULL, 1048586, "ub-country-name-numeric-length"},
-  { "iso-3166-alpha2-code", 538968066, "PrintableString"},
+  { "iso-3166-alpha2-code", 538968095, NULL },
   { NULL, 1048586, "ub-country-name-alpha-length"},
-  { "OrganizationName", 1612709890, "PrintableString"},
+  { "OrganizationName", 1612709919, NULL },
   { "ub-organization-name-length", 524298, "1"},
-  { "NumericUserIdentifier", 1612709890, "NumericString"},
+  { "NumericUserIdentifier", 1612709916, NULL },
   { "ub-numeric-user-id-length", 524298, "1"},
   { "OrganizationalUnitNames", 1612709899, NULL },
   { "ub-organizational-units", 1074266122, "1"},
   { NULL, 2, "OrganizationalUnitName"},
-  { "OrganizationalUnitName", 1612709890, "PrintableString"},
+  { "OrganizationalUnitName", 1612709919, NULL },
   { "ub-organizational-unit-name-length", 524298, "1"},
-  { "CommonName", 1073741826, "PrintableString"},
+  { "CommonName", 1073741855, NULL },
   { "pkcs-7-ContentInfo", 1610612741, NULL },
   { "contentType", 1073741826, "pkcs-7-ContentType"},
   { "content", 541073421, NULL },
@@ -294,8 +294,8 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { NULL, 1073741825, "9"},
   { NULL, 1, "7"},
   { "pkcs-9-challengePassword", 1610612754, NULL },
-  { "printableString", 1073741826, "PrintableString"},
-  { "utf8String", 2, "UTF8String"},
+  { "printableString", 1073741855, NULL },
+  { "utf8String", 34, NULL },
   { "pkcs-9-localKeyId", 1073741831, NULL },
   { "pkcs-8-PrivateKeyInfo", 1610612741, NULL },
   { "version", 1073741827, NULL },
@@ -402,7 +402,7 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "policyQualifierId", 1073741836, NULL },
   { "qualifier", 541065229, NULL },
   { "policyQualifierId", 1, NULL },
-  { "CPSuri", 1073741826, "IA5String"},
+  { "CPSuri", 1073741853, NULL },
   { "UserNotice", 1610612741, NULL },
   { "noticeRef", 1073758210, "NoticeReference"},
   { "explicitText", 16386, "DisplayText"},
@@ -411,13 +411,13 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "noticeNumbers", 536870923, NULL },
   { NULL, 3, NULL },
   { "DisplayText", 1610612754, NULL },
-  { "ia5String", 1612709890, "IA5String"},
+  { "ia5String", 1612709917, NULL },
   { "200", 524298, "1"},
-  { "visibleString", 1612709890, "VisibleString"},
+  { "visibleString", 1612709923, NULL },
   { "200", 524298, "1"},
-  { "bmpString", 1612709890, "BMPString"},
+  { "bmpString", 1612709921, NULL },
   { "200", 524298, "1"},
-  { "utf8String", 538968066, "UTF8String"},
+  { "utf8String", 538968098, NULL },
   { "200", 524298, "1"},
   { "OCSPRequest", 1610612741, NULL },
   { "tbsRequest", 1073741826, "TBSRequest"},
@@ -474,7 +474,7 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { NULL, 1073741833, "0"},
   { NULL, 2056, "0"},
   { "responderID", 1073741826, "ResponderID"},
-  { "producedAt", 1082130449, NULL },
+  { "producedAt", 1073741861, NULL },
   { "responses", 1610612747, NULL },
   { NULL, 2, "SingleResponse"},
   { "responseExtensions", 536895490, "Extensions"},
@@ -487,8 +487,8 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "SingleResponse", 1610612741, NULL },
   { "certID", 1073741826, "CertID"},
   { "certStatus", 1073741826, "CertStatus"},
-  { "thisUpdate", 1082130449, NULL },
-  { "nextUpdate", 1619025937, NULL },
+  { "thisUpdate", 1073741861, NULL },
+  { "nextUpdate", 1610637349, NULL },
   { NULL, 2056, "0"},
   { "singleExtensions", 536895490, "Extensions"},
   { NULL, 2056, "1"},
@@ -500,7 +500,7 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[] = {
   { "unknown", 536879106, "UnknownInfo"},
   { NULL, 4104, "2"},
   { "RevokedInfo", 1610612741, NULL },
-  { "revocationTime", 1082130449, NULL },
+  { "revocationTime", 1073741861, NULL },
   { "revocationReason", 536895490, "CRLReason"},
   { NULL, 2056, "0"},
   { "UnknownInfo", 1073741844, NULL },
diff --git a/lib/tpm.c b/lib/tpm.c
index d1d74ce..c8f6769 100644
--- a/lib/tpm.c
+++ b/lib/tpm.c
@@ -381,7 +381,7 @@ gnutls_datum_t asn1 = { NULL, 0 };
           return ret;
         }
 
-      ret = _gnutls_x509_decode_string(NULL, asn1.data, asn1.size, &td);
+      ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, asn1.data, 
asn1.size, &td);
       if (ret < 0)
         {
           gnutls_assert();
@@ -1361,7 +1361,7 @@ uint8_t buf[32];
       
       if (format == GNUTLS_TPMKEY_FMT_CTK_PEM)
         {
-          ret = _gnutls_x509_encode_string(NULL, tdata, tint, &tmpkey);
+          ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, tdata, 
tint, &tmpkey);
           if (ret < 0)
             {
               gnutls_assert();
diff --git a/lib/x509/common.c b/lib/x509/common.c
index 8de90f6..b89c9a7 100644
--- a/lib/x509/common.c
+++ b/lib/x509/common.c
@@ -32,94 +32,91 @@
 #include "x509_int.h"
 #include <common.h>
 
-struct oid2string
+struct oid_to_string
 {
   const char *oid;
   const char *ldap_desc;
-  int choice;                   /* of type DirectoryString */
-  int printable;
-  const char *asn_desc;         /* description in the pkix file */
+  const char *asn_desc;         /* description in the pkix file if complex 
type */
+  unsigned int etype;           /* the libtasn1 ASN1_ETYPE or INVALID
+                                 * if cannot be simply parsed */
 };
 
 /* This list contains all the OIDs that may be
  * contained in a rdnSequence and are printable.
  */
-static const struct oid2string _oid2str[] = {
+static const struct oid_to_string _oid2str[] = {
   /* PKIX
    */
-  {"1.3.6.1.5.5.7.9.1", "dateOfBirth", 0, 1, "PKIX1.GeneralizedTime"},
-  {"1.3.6.1.5.5.7.9.2", "placeOfBirth", 1, 1, "PKIX1.DirectoryString"},
-  {"1.3.6.1.5.5.7.9.3", "gender", 0, 1, "PKIX1.PrintableString"},
-  {"1.3.6.1.5.5.7.9.4", "countryOfCitizenship", 0, 1,
-   "PKIX1.PrintableString"},
-  {"1.3.6.1.5.5.7.9.5", "countryOfResidence", 0, 1, "PKIX1.PrintableString"},
-
-  {"2.5.4.6", "C", 0, 1, "PKIX1.PrintableString"},
-  {"2.5.4.9", "STREET", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.12", "T", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.10", "O", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.11", "OU", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.3", "CN", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.7", "L", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.8", "ST", 1, 1, "PKIX1.DirectoryString"},
-
-  {"2.5.4.5", "serialNumber", 0, 1, "PKIX1.PrintableString"},
-  {"2.5.4.20", "telephoneNumber", 0, 1, "PKIX1.PrintableString"},
-  {"2.5.4.4", "surName", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.43", "initials", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.44", "generationQualifier", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.42", "givenName", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.65", "pseudonym", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.46", "dnQualifier", 0, 1, "PKIX1.PrintableString"},
-  {"2.5.4.17", "postalCode", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.41", "Name", 1, 1, "PKIX1.DirectoryString"},
-  {"2.5.4.15", "businessCategory", 1, 1, "PKIX1.DirectoryString"},
-
-  {"0.9.2342.19200300.100.1.25", "DC", 0, 1, "PKIX1.IA5String"},
-  {"0.9.2342.19200300.100.1.1", "UID", 1, 1, "PKIX1.DirectoryString"},
+  {"1.3.6.1.5.5.7.9.1", "dateOfBirth", NULL, ASN1_ETYPE_GENERALIZED_TIME},
+  {"1.3.6.1.5.5.7.9.2", "placeOfBirth", "PKIX1.DirectoryString", 
ASN1_ETYPE_INVALID},
+  {"1.3.6.1.5.5.7.9.3", "gender", NULL, ASN1_ETYPE_PRINTABLE_STRING},
+  {"1.3.6.1.5.5.7.9.4", "countryOfCitizenship", NULL, 
ASN1_ETYPE_PRINTABLE_STRING},
+  {"1.3.6.1.5.5.7.9.5", "countryOfResidence", NULL, 
ASN1_ETYPE_PRINTABLE_STRING},
+
+  {"2.5.4.6", "C", NULL, ASN1_ETYPE_PRINTABLE_STRING},
+  {"2.5.4.9", "STREET", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.12", "T", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.10", "O", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.11", "OU", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.3", "CN", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.7", "L", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.8", "ST", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+
+  {"2.5.4.5", "serialNumber", NULL, ASN1_ETYPE_PRINTABLE_STRING},
+  {"2.5.4.20", "telephoneNumber", NULL, ASN1_ETYPE_PRINTABLE_STRING},
+  {"2.5.4.4", "surName", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.43", "initials", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.44", "generationQualifier", "PKIX1.DirectoryString", 
ASN1_ETYPE_INVALID},
+  {"2.5.4.42", "givenName", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.65", "pseudonym", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.46", "dnQualifier", NULL, ASN1_ETYPE_PRINTABLE_STRING},
+  {"2.5.4.17", "postalCode", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.41", "Name", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"2.5.4.15", "businessCategory", "PKIX1.DirectoryString", 
ASN1_ETYPE_INVALID},
+
+  {"0.9.2342.19200300.100.1.25", "DC", NULL, ASN1_ETYPE_IA5_STRING},
+  {"0.9.2342.19200300.100.1.1", "UID", "PKIX1.DirectoryString", 
ASN1_ETYPE_INVALID},
 
   /* Extended validation
    */
-  {"1.3.6.1.4.1.311.60.2.1.1", "jurisdictionOfIncorporationLocalityName", 1,
-   1, "PKIX1.DirectoryString"},
+  {"1.3.6.1.4.1.311.60.2.1.1", "jurisdictionOfIncorporationLocalityName",
+   "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
   {"1.3.6.1.4.1.311.60.2.1.2",
-   "jurisdictionOfIncorporationStateOrProvinceName", 1, 1,
-   "PKIX1.DirectoryString"},
-  {"1.3.6.1.4.1.311.60.2.1.3", "jurisdictionOfIncorporationCountryName", 0, 1,
-   "PKIX1.PrintableString"},
+   "jurisdictionOfIncorporationStateOrProvinceName",
+   "PKIX1.DirectoryString", ASN1_ETYPE_INVALID},
+  {"1.3.6.1.4.1.311.60.2.1.3", "jurisdictionOfIncorporationCountryName",
+   NULL, ASN1_ETYPE_PRINTABLE_STRING},
 
   /* PKCS #9
    */
-  {"1.2.840.113549.1.9.1", "EMAIL", 0, 1, "PKIX1.IA5String"},
-  {"1.2.840.113549.1.9.7", NULL, 1, 1, "PKIX1.pkcs-9-challengePassword"},
+  {"1.2.840.113549.1.9.1", "EMAIL", NULL, ASN1_ETYPE_IA5_STRING},
+  {"1.2.840.113549.1.9.7", NULL, "PKIX1.pkcs-9-challengePassword", 
ASN1_ETYPE_INVALID},
 
   /* friendly name */
-  {"1.2.840.113549.1.9.20", NULL, 0, 1, "PKIX1.BMPString"},
+  {"1.2.840.113549.1.9.20", NULL, NULL, ASN1_ETYPE_BMP_STRING},
   /* local key id */
-  {"1.2.840.113549.1.9.21", NULL, 0, 1, "PKIX1.pkcs-9-localKeyId"},
+  {"1.2.840.113549.1.9.21", NULL, NULL, ASN1_ETYPE_OCTET_STRING},
 
   /* rfc3920 section 5.1.1 */
-  {"1.3.6.1.5.5.7.8.5", "XmppAddr", 0, 1, "PKIX1.UTF8String"},
+  {"1.3.6.1.5.5.7.8.5", "XmppAddr", NULL, ASN1_ETYPE_UTF8_STRING},
 
-  {NULL, NULL, 0, 0, ""}
+  {NULL, NULL, NULL, 0}
 };
 
-/* Returns 1 if the data defined by the OID are printable.
- */
-int
-_gnutls_x509_oid_data_printable (const char *oid)
+static const struct oid_to_string* get_oid_entry (const char* oid)
 {
   unsigned int i = 0;
 
   do
     {
       if (strcmp (_oid2str[i].oid, oid) == 0)
-        return _oid2str[i].printable;
+        return &_oid2str[i];
       i++;
     }
   while (_oid2str[i].oid != NULL);
 
-  return 0;
+  return NULL;
+
 }
 
 /**
@@ -152,25 +149,6 @@ gnutls_x509_dn_oid_known (const char *oid)
   return 0;
 }
 
-/* Returns 1 if the data defined by the OID are of a choice
- * type.
- */
-int
-_gnutls_x509_oid_data_choice (const char *oid)
-{
-  unsigned int i = 0;
-
-  do
-    {
-      if (strcmp (_oid2str[i].oid, oid) == 0)
-        return _oid2str[i].choice;
-      i++;
-    }
-  while (_oid2str[i].oid != NULL);
-
-  return 0;
-}
-
 /**
  * gnutls_x509_dn_oid_name:
  * @oid: holds an Object Identifier in a null terminated string
@@ -202,62 +180,95 @@ gnutls_x509_dn_oid_name (const char *oid, unsigned int 
flags)
   else return NULL;
 }
 
-const char *
-_gnutls_x509_oid2asn_string (const char *oid)
+static int
+make_printable_string(unsigned etype, const gnutls_datum_t *input, 
gnutls_datum_t *out)
 {
-  unsigned int i = 0;
+int printable = 0;
+int ret;
+unsigned int i;
+size_t size;
 
-  do
+  if (etype == ASN1_ETYPE_BMP_STRING)
     {
-      if (strcmp (_oid2str[i].oid, oid) == 0)
-        return _oid2str[i].asn_desc;
-      i++;
+      ret = _gnutls_ucs2_to_utf8(input->data, input->size, out);
+      if (ret < 0)
+        {
+          /* could not convert. Handle it as non-printable */
+          printable = 0;
+        }
+      else
+        printable = 1;
     }
-  while (_oid2str[i].oid != NULL);
+  else if (etype == ASN1_ETYPE_TELETEX_STRING)
+    {
+      int ascii = 0;
+      /* HACK: if the teletex string contains only ascii
+       * characters then treat it as printable.
+       */
+      for (i = 0; i < input->size; i++)
+        if (!isascii (input->data[i]))
+          ascii = 1;
 
-  return NULL;
-}
+      if (ascii == 0)
+        {
+          out->data = gnutls_malloc(input->size+1);
+          if (out->data == NULL)
+            return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+          
+          memcpy(out->data, input->data, input->size);
+          out->size = input->size;
+          
+          out->data[out->size] = 0;
+          
+          printable = 1;
+        }
+    }
+  else
+    return GNUTLS_E_INVALID_REQUEST;
 
+  if (printable == 0)
+    { /* need to allocate out */
+      out->size = input->size*2+2;
+      out->data = gnutls_malloc(out->size);
+      if (out->data == NULL)
+        return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
 
-/* This function will convert an attribute value, specified by the OID,
- * to a string. The result will be a null terminated string.
- *
- * res may be null. This will just return the res_size, needed to
- * hold the string.
- */
-int
-_gnutls_x509_oid_data2string (const char *oid, void *value,
-                              int value_size, char *res, size_t * res_size)
+      size = out->size;
+      ret = _gnutls_x509_data2hex (input->data, input->size, out->data, &size);
+      if (ret < 0)
+        {
+          gnutls_assert();
+          goto cleanup;
+        }
+      out->size = size;
+    }
+
+  return 0;
+
+cleanup:
+  _gnutls_free_datum(out);
+  return ret;
+}
+
+static int
+decode_complex_string (const struct oid_to_string* oentry, void *value,
+                       int value_size, gnutls_datum_t* out)
 {
   char str[MAX_STRING_LEN], tmpname[128];
-  const char *aname = NULL;
-  int choice = -1, len = -1, result;
+  int len = -1, result;
   ASN1_TYPE tmpasn = ASN1_TYPE_EMPTY;
   char asn1_err[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = "";
+  int etype = ASN1_ETYPE_INVALID;
+  gnutls_datum_t td;
 
-  if (value == NULL || value_size <= 0 || res_size == NULL)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INVALID_REQUEST;
-    }
-
-  if (_gnutls_x509_oid_data_printable (oid) == 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-  aname = _gnutls_x509_oid2asn_string (oid);
-  choice = _gnutls_x509_oid_data_choice (oid);
-
-  if (aname == NULL)
+  if (oentry->asn_desc == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INTERNAL_ERROR;
     }
 
   if ((result =
-       asn1_create_element (_gnutls_get_pkix (), aname,
+       asn1_create_element (_gnutls_get_pkix (), oentry->asn_desc,
                             &tmpasn)) != ASN1_SUCCESS)
     {
       gnutls_assert ();
@@ -269,123 +280,119 @@ _gnutls_x509_oid_data2string (const char *oid, void 
*value,
                           asn1_err)) != ASN1_SUCCESS)
     {
       gnutls_assert ();
-      _gnutls_debug_log ("asn1_der_decoding: %s:%s\n", str, asn1_err);
+      _gnutls_debug_log ("asn1_der_decoding: %s\n", asn1_err);
       asn1_delete_structure (&tmpasn);
       return _gnutls_asn2err (result);
     }
 
-  /* If this is a choice then we read the choice. Otherwise it
-   * is the value;
+  /* Read the type of choice.
    */
   len = sizeof (str) - 1;
   if ((result = asn1_read_value (tmpasn, "", str, &len)) != ASN1_SUCCESS)
-    {                           /* CHOICE */
+    { /* CHOICE */
       gnutls_assert ();
       asn1_delete_structure (&tmpasn);
       return _gnutls_asn2err (result);
     }
 
-  if (choice == 0)
-    {
-      str[len] = 0;
+  str[len] = 0;
 
-      /* Refuse to deal with strings containing NULs. */
-      if (strlen (str) != (size_t)len)
-        return GNUTLS_E_ASN1_DER_ERROR;
+  /* Note that we do not support strings other than
+   * UTF-8 (thus ASCII as well).
+   */
+  if (strcmp (str, "teletexString") == 0)
+    etype = ASN1_ETYPE_TELETEX_STRING;
 
-      if (res)
-        _gnutls_str_cpy (res, *res_size, str);
-      *res_size = (size_t)len;
+  if (strcmp (str, "bmpString") == 0)
+    etype = ASN1_ETYPE_BMP_STRING;
 
-      asn1_delete_structure (&tmpasn);
+  _gnutls_str_cpy (tmpname, sizeof (tmpname), str);
+
+  result = _gnutls_x509_read_value(tmpasn, tmpname, &td);
+  asn1_delete_structure (&tmpasn);
+  if (result < 0)
+    return gnutls_assert_val(result);
+
+  if (etype != ASN1_ETYPE_INVALID)
+    {
+      result = make_printable_string(etype, &td, out);
+      
+      _gnutls_free_datum(&td);
+      
+      if (result < 0)
+        return gnutls_assert_val(result);
     }
   else
-    {                           /* choice */
-      int non_printable = 0, teletex = 0;
-      int ucs2 = 0;
-      str[len] = 0;
-
-      /* Note that we do not support strings other than
-       * UTF-8 (thus ASCII as well).
-       */
-      if (strcmp (str, "printableString") != 0 && strcmp (str, "bmpString") != 
0 && 
-          strcmp (str, "ia5String") != 0 && strcmp (str, "utf8String") != 0)
-        {
-          non_printable = 1;
-        }
-      if (strcmp (str, "teletexString") == 0)
-        teletex = 1;
+    {
+      out->data = td.data;
+      out->size = td.size;
+      out->data[out->size] = 0;
+    }
 
-      if (strcmp (str, "bmpString") == 0)
-        ucs2 = 1;
+   /* Refuse to deal with strings containing NULs. */
+  if (strlen ((void*)out->data) != (size_t)out->size)
+    {
+      _gnutls_free_datum(out);
+      return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+    }
 
-      _gnutls_str_cpy (tmpname, sizeof (tmpname), str);
+  return 0;
+}
 
-      len = sizeof (str) - 1;
-      if ((result =
-           asn1_read_value (tmpasn, tmpname, str, &len)) != ASN1_SUCCESS)
-        {
-          asn1_delete_structure (&tmpasn);
-          return _gnutls_asn2err (result);
-        }
 
-      asn1_delete_structure (&tmpasn);
-      
-      if (ucs2 != 0)
-        {
-          gnutls_datum_t td;
-
-          result = _gnutls_ucs2_to_utf8(str, len, &td);
-          if (result < 0)
-            {
-              /* could not convert. Handle it as non-printable */
-              non_printable = 1;
-              ucs2 = 0;
-            }
-          else
-            {
-              if (td.size >= sizeof(str))
-                {
-                  gnutls_free(td.data);
-                  return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
-                }
-              memcpy(str, td.data, td.size);
-              len = td.size;
-          
-              gnutls_free(td.data);
-            }
-        }
-      else if (teletex != 0)
-        {
-          int ascii = 0, i;
-          /* HACK: if the teletex string contains only ascii
-           * characters then treat it as printable.
-           */
-          for (i = 0; i < len; i++)
-            if (!isascii (str[i]))
-              ascii = 1;
-
-          if (ascii == 0)
-            non_printable = 0;
-        }
+/* This function will convert an attribute value, specified by the OID,
+ * to a string. The result will be a null terminated string.
+ *
+ * res may be null. This will just return the res_size, needed to
+ * hold the string.
+ */
+int
+_gnutls_x509_dn_to_string (const char *oid, void *value,
+                           int value_size, gnutls_datum_t *str)
+{
+  const struct oid_to_string* oentry;
+  int ret;
+  size_t size;
 
-      if (non_printable == 0)
-        {
-          str[len] = 0;
+  if (value == NULL || value_size <= 0)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
 
-          /* Refuse to deal with strings containing NULs. */
-          if (strlen (str) != (size_t)len)
-            return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+  oentry = get_oid_entry(oid);
+  if (oentry == NULL)
+    { /* unknown OID -> hex */
+      str->size = value_size*2+2;
+      str->data = gnutls_malloc(str->size);
+      if (str->data == NULL)
+        return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
 
-          if (res)
-            _gnutls_str_cpy (res, *res_size, str);
-          *res_size = (size_t)len;
+      size = str->size;
+      ret = _gnutls_x509_data2hex (value, value_size, str->data, &size);
+      if (ret < 0)
+        {
+          gnutls_assert();
+          gnutls_free(str->data);
+          return ret;
         }
-      else
+      str->size = size;
+      return 0;
+    }
+  
+  if (oentry->asn_desc != NULL)
+    { /* complex */
+      ret = decode_complex_string(oentry, value, value_size, str);
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+    }
+  else
+    {
+      ret = _gnutls_x509_decode_string(oentry->etype, value, value_size,
+                                       str);
+      if (ret < 0)
         {
-          result = _gnutls_x509_data2hex (str, (size_t)len, res, res_size);
-          if (result < 0)
-            return gnutls_assert_val(result);
+          return gnutls_assert_val(ret);
         }
     }
 
@@ -872,60 +879,59 @@ _gnutls_x509_export_int_named2 (ASN1_TYPE asn1_data, 
const char *name,
   return 0;
 }
 
-/* Decodes an octet string. Leave string_type null for a normal
- * octet string. Otherwise put something like BMPString, PrintableString
- * etc.
+/* Decodes an octet string. The etype specifies the string type.
+ * The returned string is always null terminated (but null is not
+ * included in size).
  */
 int
-_gnutls_x509_decode_string (const char *string_type,
+_gnutls_x509_decode_string (unsigned int etype,
                             const uint8_t * der, size_t der_size,
                             gnutls_datum_t * output)
 {
-  ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
-  int result;
-  char strname[64];
-
-  if (string_type == NULL) /* assume octet string */
-    _gnutls_str_cpy (strname, sizeof (strname), "PKIX1.pkcs-7-Data");
-  else
+  int ret;
+  const uint8_t *str;
+  unsigned int str_size;
+  gnutls_datum_t td;
+  
+  ret = asn1_decode_simple_der (etype, der, der_size, &str, &str_size);
+  if (ret != ASN1_SUCCESS)
     {
-      _gnutls_str_cpy (strname, sizeof (strname), "PKIX1.");
-      _gnutls_str_cat (strname, sizeof (strname), string_type);
+      gnutls_assert ();
+      ret = _gnutls_asn2err (ret);
+      return ret;
     }
+    
+  td.size = str_size;
+  td.data = gnutls_malloc(str_size+1);
+  if (td.data == NULL)
+    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+  
+  memcpy(td.data, str, str_size);
+  td.data[str_size] = 0;
 
-  if ((result = asn1_create_element
-       (_gnutls_get_pkix (), strname, &c2)) != ASN1_SUCCESS)
+  ret = make_printable_string(etype, &td, output);
+  if (ret == GNUTLS_E_INVALID_REQUEST) /* unsupported etype */
     {
-      gnutls_assert ();
-      result = _gnutls_asn2err (result);
-      goto cleanup;
+      output->data = td.data;
+      output->size = td.size;
+      ret = 0;
     }
-
-  result = asn1_der_decoding (&c2, der, der_size, NULL);
-  if (result != ASN1_SUCCESS)
+  else if (ret <= 0)
     {
-      gnutls_assert ();
-      result = _gnutls_asn2err (result);
-      goto cleanup;
+      _gnutls_free_datum(&td);
     }
 
-  result = _gnutls_x509_read_value(c2, "", output);
-  if (result < 0)
+  /* Refuse to deal with strings containing NULs. */
+  if (etype != ASN1_ETYPE_OCTET_STRING)
     {
-      gnutls_assert ();
-      goto cleanup;
+      if (strlen ((void*)output->data) != (size_t)output->size)
+        {
+          _gnutls_free_datum(output);
+          ret = gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+        }
     }
-  
-  /* This is allowed since _gnutls_x509_read_value allocates one more */
-  output->data[output->size] = 0;
 
-  result = 0;
-
-cleanup:
-  if (c2)
-    asn1_delete_structure (&c2);
-
-  return result;
+  return ret;
 }
 
 
@@ -941,14 +947,18 @@ _gnutls_x509_read_value (ASN1_TYPE c, const char *root,
 {
   int len = 0, result;
   uint8_t *tmp = NULL;
+  unsigned int etype;
 
-  result = asn1_read_value (c, root, NULL, &len);
+  result = asn1_read_value_type (c, root, NULL, &len, &etype);
   if (result != ASN1_MEM_ERROR)
     {
       gnutls_assert ();
       result = _gnutls_asn2err (result);
       return result;
     }
+  
+  if (etype == ASN1_ETYPE_BIT_STRING)
+    len /= 8;
 
   tmp = gnutls_malloc ((size_t)len+1);
   if (tmp == NULL)
@@ -966,6 +976,9 @@ _gnutls_x509_read_value (ASN1_TYPE c, const char *root,
       goto cleanup;
     }
 
+  if (etype == ASN1_ETYPE_BIT_STRING)
+    len /= 8;
+
   ret->data = tmp;
   ret->size = (unsigned)len;
 
@@ -976,21 +989,22 @@ cleanup:
   return result;
 }
 
-/* Reads a value from an ASN1 tree, and puts the output
- * in an allocated variable in the given datum.
+/* Reads a value from an ASN1 tree, then interprets it as the provided
+ * type of string and returns the output in an allocated variable.
  *
  * Note that this function always places a null character
  * at the end of a readable string value (which is not accounted into size)
  */
 int
 _gnutls_x509_read_string (ASN1_TYPE c, const char *root,
-                          gnutls_datum_t * ret, x509_string_type type)
+                          gnutls_datum_t * ret, unsigned int etype)
 {
   int len = 0, result;
   size_t slen;
   uint8_t *tmp = NULL;
+  unsigned rtype;
 
-  result = asn1_read_value (c, root, NULL, &len);
+  result = asn1_read_value_type (c, root, NULL, &len, &rtype);
   if (result != ASN1_MEM_ERROR)
     {
       gnutls_assert ();
@@ -998,7 +1012,7 @@ _gnutls_x509_read_string (ASN1_TYPE c, const char *root,
       return result;
     }
 
-  if (type == RV_BIT_STRING)
+  if (rtype == ASN1_ETYPE_BIT_STRING)
     len /= 8;
 
   tmp = gnutls_malloc ((size_t)len+1);
@@ -1017,36 +1031,20 @@ _gnutls_x509_read_string (ASN1_TYPE c, const char *root,
       goto cleanup;
     }
 
-  if (type == RV_BIT_STRING)
+  if (rtype == ASN1_ETYPE_BIT_STRING)
     len /= 8;
 
   /* Extract the STRING.
    */
-  if (type == RV_IA5STRING || type == RV_UTF8STRING || type == RV_OCTET_STRING)
-    {
-      const char* sname;
-      slen = (size_t)len;
+  slen = (size_t)len;
 
-      if (type == RV_UTF8STRING)
-        sname = "UTF8String";
-      else if (type == RV_IA5STRING)
-        sname = "IA5String";
-      else
-        sname = NULL;
-
-      result = _gnutls_x509_decode_string (sname, tmp, slen, ret);
-      if (result < 0)
-        {
-          gnutls_assert ();
-          goto cleanup;
-        }
-      gnutls_free(tmp);
-    }
-  else
+  result = _gnutls_x509_decode_string (etype, tmp, slen, ret);
+  if (result < 0)
     {
-      ret->data = tmp;
-      ret->size = (unsigned)len;
+      gnutls_assert ();
+      goto cleanup;
     }
+  gnutls_free(tmp);
 
   return 0;
 
@@ -1057,48 +1055,33 @@ cleanup:
 
 /* The string type should be IA5String, UTF8String etc. Leave
  * null for octet string */
-int _gnutls_x509_encode_string(const char* string_type, 
+int _gnutls_x509_encode_string(unsigned int etype, 
                                const void* input_data, size_t input_size,
                                gnutls_datum_t* output)
 {
+  uint8_t tl[ASN1_MAX_TL_SIZE];
+  unsigned int tl_size;
   int ret;
-  ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
-  char strname[64];
   
-  _gnutls_str_cpy (strname, sizeof (strname), "PKIX1.");
-  if (string_type == NULL)
-    _gnutls_str_cat (strname, sizeof (strname), "pkcs-7-Data");
-  else
-    _gnutls_str_cat (strname, sizeof (strname), string_type);
-  
-  if ((ret = asn1_create_element
-       (_gnutls_get_pkix (), strname, &c2)) != ASN1_SUCCESS)
-    {
-      gnutls_assert ();
-      ret = _gnutls_asn2err (ret);
-      goto cleanup;
-    }
-
-  ret = asn1_write_value (c2, "", input_data, input_size);
+  tl_size = sizeof(tl);
+  ret = asn1_encode_simple_der (etype, input_data, input_size, tl, &tl_size);
   if (ret != ASN1_SUCCESS)
     {
       gnutls_assert ();
       ret = _gnutls_asn2err (ret);
-      goto cleanup;
-    }
-
-  ret = _gnutls_x509_der_encode(c2, "", output, 0);
-  if (ret < 0)
-    {
-      gnutls_assert ();
-      goto cleanup;
+      return ret;
     }
-
-  ret = 0;
-
-cleanup:
-  asn1_delete_structure (&c2);
-  return ret;
+  
+  output->data = gnutls_malloc(tl_size + input_size);
+  if (output->data == NULL)
+    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+  
+  memcpy(output->data, tl, tl_size);
+  memcpy(output->data+tl_size, input_data, input_size);
+  
+  output->size = tl_size + input_size;
+  
+  return 0;
 }
 
 /* DER Encodes the src ASN1_TYPE and stores it to
@@ -1224,39 +1207,39 @@ _gnutls_x509_der_encode_and_copy (ASN1_TYPE src, const 
char *src_name,
   return 0;
 }
 
-/* Writes the value of the datum in the given ASN1_TYPE. If str is non
- * (0) it encodes it as OCTET STRING.
+/* Writes the value of the datum in the given ASN1_TYPE. 
  */
 int
 _gnutls_x509_write_value (ASN1_TYPE c, const char *root,
-                          const gnutls_datum_t * data, x509_string_type type)
+                          const gnutls_datum_t * data)
 {
   int ret;
-  ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
-  gnutls_datum_t val = { NULL, 0 };
-  const char* sname = NULL;
-
-  if (type == RV_OCTET_STRING || type == RV_IA5STRING || type == RV_UTF8STRING)
-    {
-    
-      if (type == RV_IA5STRING)
-        sname = "IA5String";
-      else if (type == RV_UTF8STRING)
-        sname = "UTF8String";
 
-      ret = _gnutls_x509_encode_string(sname, data->data, data->size, &val);
-      if (ret < 0)
-        {
-          gnutls_assert ();
-          goto cleanup;
-        }
-    }
-  else
+  /* Write the data.
+   */
+  ret = asn1_write_value (c, root, data->data, data->size);
+  if (ret != ASN1_SUCCESS)
     {
-      val.data = data->data;
-      val.size = data->size;
+      gnutls_assert ();
+      return _gnutls_asn2err (ret);
     }
 
+  return 0;
+}
+
+/* Writes the value of the datum in the given ASN1_TYPE as a string. 
+ */
+int
+_gnutls_x509_write_string (ASN1_TYPE c, const char *root,
+                           const gnutls_datum_t * data, unsigned int etype)
+{
+  int ret;
+  gnutls_datum_t val = { NULL, 0 };
+
+  ret = _gnutls_x509_encode_string(etype, data->data, data->size, &val);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
   /* Write the data.
    */
   ret = asn1_write_value (c, root, val.data, val.size);
@@ -1270,9 +1253,7 @@ _gnutls_x509_write_value (ASN1_TYPE c, const char *root,
   ret = 0;
 
 cleanup:
-  asn1_delete_structure (&c2);
-  if (val.data != data->data)
-    _gnutls_free_datum (&val);
+  _gnutls_free_datum (&val);
   return ret;
 }
 
@@ -1598,3 +1579,197 @@ _gnutls_x509_get_signature (ASN1_TYPE src, const char 
*src_name,
 cleanup:
   return result;
 }
+
+/* ASN.1 PrintableString rules */
+static int is_printable(char p)
+{
+  if ((p >= 'a' && p <= 'z') || (p >= 'A' && p <= 'Z') ||
+      (p >= '0' && p <= '9') || p == ' ' || p == '(' || p == ')' ||
+      p == '+' || p == ',' || p == '-' || p == '.' || p == '/' || 
+      p == ':' || p == '=' || p == '?')
+    return 1;
+    
+  return 0;
+}
+
+static int write_complex_string(ASN1_TYPE asn_struct, const char* where,
+                                const struct oid_to_string* oentry, const 
uint8_t *data, 
+                                size_t data_size)
+{
+  char tmp[128];
+  ASN1_TYPE c2;
+  int result;
+  const char *string_type;
+  unsigned int i;
+
+  result = asn1_create_element (_gnutls_get_pkix (), oentry->asn_desc, &c2);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      return _gnutls_asn2err (result);
+    }
+
+  tmp[0] = 0;
+
+  string_type = "printableString";
+
+  /* Check if the data is ASN.1 printable, and use
+   * the UTF8 string type if not.
+   */
+  for (i = 0; i < data_size; i++)
+    {
+      if (!is_printable (data[i]))
+        {
+          string_type = "utf8String";
+          break;
+        }
+    }
+
+  /* if the type is a CHOICE then write the
+   * type we'll use.
+   */
+  result = asn1_write_value (c2, "", string_type, 1);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      result = _gnutls_asn2err (result);
+      goto error;
+    }
+
+  _gnutls_str_cpy (tmp, sizeof (tmp), string_type);
+
+  result = asn1_write_value (c2, tmp, data, data_size);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      result = _gnutls_asn2err (result);
+      goto error;
+    }
+
+  result = _gnutls_x509_der_encode_and_copy (c2, "", asn_struct, where, 0);
+  if (result < 0)
+    {
+      gnutls_assert ();
+      goto error;
+    }
+
+  result = 0;
+
+error:
+  asn1_delete_structure (&c2);
+  return result;
+}
+
+
+/* This will encode and write the AttributeTypeAndValue field.
+ * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute.
+ * In all cases only one value is written.
+ */
+int
+_gnutls_x509_encode_and_write_attribute (const char *given_oid,
+                                         ASN1_TYPE asn1_struct,
+                                         const char *where,
+                                         const void *_data,
+                                         int data_size, int multi)
+{
+  const uint8_t *data = _data;
+  char tmp[128];
+  int result;
+  const struct oid_to_string* oentry;
+
+  oentry = get_oid_entry(given_oid);
+  if (oentry == NULL)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Cannot find OID: %s\n", given_oid);
+      return GNUTLS_E_X509_UNSUPPORTED_OID;
+    }
+
+  /* write the data (value)
+   */
+
+  _gnutls_str_cpy (tmp, sizeof (tmp), where);
+  _gnutls_str_cat (tmp, sizeof (tmp), ".value");
+
+  if (multi != 0)
+    { /* if not writing an AttributeTypeAndValue, but an Attribute */
+      _gnutls_str_cat (tmp, sizeof (tmp), "s"); /* values */
+
+      result = asn1_write_value (asn1_struct, tmp, "NEW", 1);
+      if (result != ASN1_SUCCESS)
+        {
+          gnutls_assert ();
+          result = _gnutls_asn2err (result);
+          goto error;
+        }
+
+      _gnutls_str_cat (tmp, sizeof (tmp), ".?LAST");
+    }
+
+  if (oentry->asn_desc != NULL) /* write a complex string API */
+    {
+      result = write_complex_string(asn1_struct, tmp, oentry, data, data_size);
+      if (result < 0)
+        return gnutls_assert_val(result);
+    }
+  else /* write a simple string */
+    {
+      gnutls_datum_t td;
+
+      td.data = (void*)data;
+      td.size = data_size;
+      result = _gnutls_x509_write_string (asn1_struct, tmp, &td, 
oentry->etype);
+      if (result < 0)
+        {
+          gnutls_assert ();
+          goto error;
+        }
+    }
+
+  /* write the type
+   */
+  _gnutls_str_cpy (tmp, sizeof (tmp), where);
+  _gnutls_str_cat (tmp, sizeof (tmp), ".type");
+
+  result = asn1_write_value (asn1_struct, tmp, given_oid, 1);
+  if (result != ASN1_SUCCESS)
+    {
+      gnutls_assert ();
+      result = _gnutls_asn2err (result);
+      goto error;
+    }
+
+  result = 0;
+
+error:
+  return result;
+}
+
+/* copies a datum to a buffer. If it doesn't fit it returns
+ * GNUTLS_E_SHORT_MEMORY_BUFFER. It always deinitializes the datum
+ * after the copy.
+ *
+ * The buffer will always be null terminated.
+ */
+int _gnutls_strdatum_to_buf (gnutls_datum_t * d, void* buf, size_t * 
sizeof_buf)
+{
+int ret;
+uint8_t *_buf = buf;
+
+  if (*sizeof_buf < d->size+1)
+    {
+      *sizeof_buf = d->size+1;
+      ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+      goto cleanup;
+    }
+  memcpy(buf, d->data, d->size);
+  _buf[d->size] = 0;
+
+  *sizeof_buf = d->size;
+  ret = 0;
+
+cleanup:
+  _gnutls_free_datum(d);
+  
+  return ret;                          
+}
diff --git a/lib/x509/common.h b/lib/x509/common.h
index 4b38884..faf29a1 100644
--- a/lib/x509/common.h
+++ b/lib/x509/common.h
@@ -66,35 +66,21 @@
 #define ASN1_NULL "\x05\x00"
 #define ASN1_NULL_SIZE 2
 
-typedef enum x509_string_type {
-  RV_RAW,
-  RV_OCTET_STRING,
-  RV_BIT_STRING,
-  RV_IA5STRING,
-  RV_UTF8STRING
-} x509_string_type;
-
 int _gnutls_x509_set_time (ASN1_TYPE c2, const char *where, time_t tim, int 
general);
 
-int _gnutls_x509_decode_string (const char *string_type,
+int _gnutls_x509_decode_string (unsigned int etype,
                                 const uint8_t * der, size_t der_size,
                                 gnutls_datum_t *output);
 
-int _gnutls_x509_encode_string(const char* string_type,
+int _gnutls_x509_encode_string(unsigned int etype,
                                const void* input_data, size_t input_size,
                                gnutls_datum_t* output);
 
-int _gnutls_x509_oid_data2string (const char *OID, void *value,
-                                  int value_size, char *res,
-                                  size_t * res_size);
+int _gnutls_x509_dn_to_string (const char *OID, void *value,
+                                  int value_size, gnutls_datum_t* out);
 int _gnutls_x509_data2hex (const void * data, size_t data_size,
                            void * out, size_t * sizeof_out);
 
-const char *_gnutls_x509_oid2asn_string (const char *oid);
-
-int _gnutls_x509_oid_data_choice (const char *OID);
-int _gnutls_x509_oid_data_printable (const char *OID);
-
 time_t _gnutls_x509_get_time (ASN1_TYPE c2, const char *when, int general);
 
 gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type (char *str_type);
@@ -124,9 +110,12 @@ int _gnutls_x509_export_int_named2 (ASN1_TYPE asn1_data, 
const char *name,
 int _gnutls_x509_read_value (ASN1_TYPE c, const char *root,
                              gnutls_datum_t * ret);
 int _gnutls_x509_read_string (ASN1_TYPE c, const char *root,
-                             gnutls_datum_t * ret, x509_string_type type);
+                             gnutls_datum_t * ret, unsigned int etype);
 int _gnutls_x509_write_value (ASN1_TYPE c, const char *root,
-                              const gnutls_datum_t * data, x509_string_type 
type);
+                              const gnutls_datum_t * data);
+
+int _gnutls_x509_write_string (ASN1_TYPE c, const char *root,
+                              const gnutls_datum_t * data, unsigned int etype);
 
 int _gnutls_x509_encode_and_write_attribute (const char *given_oid,
                                              ASN1_TYPE asn1_struct,
@@ -183,4 +172,6 @@ int set_extension (ASN1_TYPE asn, const char *root,
                   const char *ext_id,
                   const gnutls_datum_t * ext_data, unsigned int critical);
 
+int _gnutls_strdatum_to_buf (gnutls_datum_t * d, void* buf, size_t * 
sizeof_buf);
+
 #endif
diff --git a/lib/x509/crl.c b/lib/x509/crl.c
index b69a1c8..bdde7e9 100644
--- a/lib/x509/crl.c
+++ b/lib/x509/crl.c
@@ -215,17 +215,25 @@ gnutls_x509_crl_get_issuer_dn_by_oid (gnutls_x509_crl_t 
crl,
                                       unsigned int raw_flag, void *buf,
                                       size_t * sizeof_buf)
 {
+gnutls_datum_t td;
+int ret;
+
   if (crl == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  return _gnutls_x509_parse_dn_oid (crl->crl,
+  ret = _gnutls_x509_parse_dn_oid (crl->crl,
                                     "tbsCertList.issuer.rdnSequence",
-                                    oid, indx, raw_flag, buf, sizeof_buf);
+                                    oid, indx, raw_flag, &td);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  
+  return _gnutls_strdatum_to_buf (&td, buf, sizeof_buf);
 }
 
+
 /**
  * gnutls_x509_crl_get_dn_oid:
  * @crl: should contain a gnutls_x509_crl_t structure
diff --git a/lib/x509/crq.c b/lib/x509/crq.c
index 853d145..672447f 100644
--- a/lib/x509/crq.c
+++ b/lib/x509/crq.c
@@ -285,16 +285,23 @@ gnutls_x509_crq_get_dn_by_oid (gnutls_x509_crq_t crq, 
const char *oid,
                                int indx, unsigned int raw_flag,
                                void *buf, size_t * sizeof_buf)
 {
+gnutls_datum_t td;
+int ret;
+
   if (crq == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  return _gnutls_x509_parse_dn_oid
+  ret = _gnutls_x509_parse_dn_oid
     (crq->crq,
      "certificationRequestInfo.subject.rdnSequence",
-     oid, indx, raw_flag, buf, sizeof_buf);
+     oid, indx, raw_flag, &td);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  
+  return _gnutls_strdatum_to_buf (&td, buf, sizeof_buf);
 }
 
 /**
@@ -339,14 +346,15 @@ gnutls_x509_crq_get_dn_oid (gnutls_x509_crq_t crq,
 static int
 parse_attribute (ASN1_TYPE asn1_struct,
                  const char *attr_name, const char *given_oid, int indx,
-                 int raw, char *buf, size_t * sizeof_buf)
+                 int raw, gnutls_datum_t * out)
 {
   int k1, result;
   char tmpbuffer1[ASN1_MAX_NAME_SIZE];
   char tmpbuffer3[ASN1_MAX_NAME_SIZE];
   char value[200];
+  gnutls_datum_t td;
   char oid[MAX_OID_SIZE];
-  int len, printable;
+  int len;
 
   k1 = 0;
   do
@@ -404,7 +412,7 @@ parse_attribute (ASN1_TYPE asn1_struct,
                     tmpbuffer1, indx + 1);
 
           len = sizeof (value) - 1;
-          result = asn1_read_value (asn1_struct, tmpbuffer3, value, &len);
+          result = _gnutls_x509_read_value (asn1_struct, tmpbuffer3, &td);
 
           if (result != ASN1_SUCCESS)
             {
@@ -415,38 +423,25 @@ parse_attribute (ASN1_TYPE asn1_struct,
 
           if (raw == 0)
             {
-              printable = _gnutls_x509_oid_data_printable (oid);
-              if (printable == 1)
-                {
-                  if ((result =
-                       _gnutls_x509_oid_data2string
-                       (oid, value, len, buf, sizeof_buf)) < 0)
-                    {
-                      gnutls_assert ();
-                      goto cleanup;
-                    }
-                  return 0;
-                }
-              else
+              result =
+                   _gnutls_x509_dn_to_string
+                   (oid, td.data, td.size, out);
+              
+              _gnutls_free_datum(&td);
+
+              if (result < 0)
                 {
                   gnutls_assert ();
-                  return GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE;
+                  goto cleanup;
                 }
+              return 0;
             }
           else
             {                   /* raw!=0 */
-              if (*sizeof_buf >= (size_t) len && buf != NULL)
-                {
-                  *sizeof_buf = len;
-                  memcpy (buf, value, len);
-
-                  return 0;
-                }
-              else
-                {
-                  *sizeof_buf = len;
-                  return GNUTLS_E_SHORT_MEMORY_BUFFER;
-                }
+              out->data = td.data;
+              out->size = td.size;
+              
+              return 0;
             }
         }
 
@@ -464,8 +459,8 @@ cleanup:
 /**
  * gnutls_x509_crq_get_challenge_password:
  * @crq: should contain a #gnutls_x509_crq_t structure
- * @pass: will hold a (0)-terminated password string
- * @sizeof_pass: Initially holds the size of @pass.
+ * @buf: will hold a (0)-terminated password string
+ * @sizeof_buf: Initially holds the size of @pass.
  *
  * This function will return the challenge password in the request.
  * The challenge password is intended to be used for requesting a
@@ -476,16 +471,23 @@ cleanup:
  **/
 int
 gnutls_x509_crq_get_challenge_password (gnutls_x509_crq_t crq,
-                                        char *pass, size_t * sizeof_pass)
+                                        char *buf, size_t * sizeof_buf)
 {
+gnutls_datum_t td;
+int ret;
+
   if (crq == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  return parse_attribute (crq->crq, "certificationRequestInfo.attributes",
-                          "1.2.840.113549.1.9.7", 0, 0, pass, sizeof_pass);
+  ret = parse_attribute (crq->crq, "certificationRequestInfo.attributes",
+                          "1.2.840.113549.1.9.7", 0, 0, &td);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  
+  return _gnutls_strdatum_to_buf (&td, buf, sizeof_buf);
 }
 
 /* This function will attempt to set the requested attribute in
@@ -531,7 +533,7 @@ add_attribute (ASN1_TYPE asn, const char *root, const char 
*attribute_id,
 
   snprintf (name, sizeof (name), "%s.?LAST.values.?LAST", root);
 
-  result = _gnutls_x509_write_value (asn, name, ext_data, 0);
+  result = _gnutls_x509_write_value (asn, name, ext_data);
   if (result < 0)
     {
       gnutls_assert ();
@@ -556,7 +558,7 @@ overwrite_attribute (ASN1_TYPE asn, const char *root, 
unsigned int indx,
   _gnutls_str_cpy (name2, sizeof (name2), name);
   _gnutls_str_cat (name2, sizeof (name2), ".values.?LAST");
 
-  result = _gnutls_x509_write_value (asn, name2, ext_data, 0);
+  result = _gnutls_x509_write_value (asn, name2, ext_data);
   if (result < 0)
     {
       gnutls_assert ();
@@ -700,14 +702,21 @@ gnutls_x509_crq_get_attribute_by_oid (gnutls_x509_crq_t 
crq,
                                       const char *oid, int indx, void *buf,
                                       size_t * sizeof_buf)
 {
+int ret;
+gnutls_datum_t td;
+
   if (crq == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  return parse_attribute (crq->crq, "certificationRequestInfo.attributes",
-                          oid, indx, 1, buf, sizeof_buf);
+  ret = parse_attribute (crq->crq, "certificationRequestInfo.attributes",
+                          oid, indx, 1, &td);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  
+  return _gnutls_strdatum_to_buf (&td, buf, sizeof_buf);
 }
 
 /**
diff --git a/lib/x509/dn.c b/lib/x509/dn.c
index 89a07e6..5d2cce4 100644
--- a/lib/x509/dn.c
+++ b/lib/x509/dn.c
@@ -78,12 +78,12 @@ _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct,
   char tmpbuffer2[ASN1_MAX_NAME_SIZE];
   char tmpbuffer3[ASN1_MAX_NAME_SIZE];
   uint8_t value[MAX_STRING_LEN], *value2 = NULL;
+  gnutls_datum_t td;
   char *escaped = NULL;
   const char *ldap_desc;
   char oid[MAX_OID_SIZE];
-  int len, printable;
-  char *string = NULL;
-  size_t sizeof_string, sizeof_escaped;
+  int len;
+  size_t sizeof_escaped;
 
   if (sizeof_buf == NULL)
     {
@@ -226,7 +226,6 @@ _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct,
             }
 
           ldap_desc = gnutls_x509_dn_oid_name (oid, 
GNUTLS_X509_DN_OID_RETURN_OID);
-          printable = _gnutls_x509_oid_data_printable (oid);
 
           /* leading #, hex encoded value and terminating NULL */
           sizeof_escaped = 2 * len + 2;
@@ -239,43 +238,22 @@ _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct,
               goto cleanup;
             }
 
-          sizeof_string = 2 * len + 2;  /* in case it is not printable */
-
-          string = gnutls_malloc (sizeof_string);
-          if (string == NULL)
-            {
-              gnutls_assert ();
-              result = GNUTLS_E_MEMORY_ERROR;
-              goto cleanup;
-            }
-
           STR_APPEND (ldap_desc);
           STR_APPEND ("=");
-          result = 0;
-
-          if (printable)
-            result =
-              _gnutls_x509_oid_data2string (oid,
-                                            value2, len,
-                                            string, &sizeof_string);
-
-          if (!printable || result < 0)
-            result =
-              _gnutls_x509_data2hex (value2, len, string, &sizeof_string);
 
+          result =
+              _gnutls_x509_dn_to_string (oid, value2, len, &td);
           if (result < 0)
             {
               gnutls_assert ();
               _gnutls_debug_log
-                ("Found OID: '%s' with value '%s'\n",
+                ("Cannot parse OID: '%s' with value '%s'\n",
                  oid, _gnutls_bin2hex (value2, len, escaped, sizeof_escaped,
                                        NULL));
               goto cleanup;
             }
-          STR_APPEND (str_escape (string, escaped, sizeof_escaped));
-          gnutls_free (string);
-          string = NULL;
-
+          STR_APPEND (str_escape ((char*)td.data, escaped, sizeof_escaped));
+          _gnutls_free_datum (&td);
           gnutls_free (escaped);
           escaped = NULL;
           gnutls_free (value2);
@@ -307,7 +285,6 @@ _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct,
 
 cleanup:
   gnutls_free (value2);
-  gnutls_free (string);
   gnutls_free (escaped);
   _gnutls_buffer_clear (&out_str);
   return result;
@@ -330,22 +307,17 @@ _gnutls_x509_parse_dn_oid (ASN1_TYPE asn1_struct,
                            const char *asn1_rdn_name,
                            const char *given_oid, int indx,
                            unsigned int raw_flag,
-                           void *buf, size_t * sizeof_buf)
+                           gnutls_datum_t* out)
 {
   int k2, k1, result;
   char tmpbuffer1[ASN1_MAX_NAME_SIZE];
   char tmpbuffer2[ASN1_MAX_NAME_SIZE];
   char tmpbuffer3[ASN1_MAX_NAME_SIZE];
+  gnutls_datum_t td;
   uint8_t value[256];
   char oid[MAX_OID_SIZE];
-  int len, printable;
+  int len;
   int i = 0;
-  char *cbuf = buf;
-
-  if (cbuf == NULL)
-    *sizeof_buf = 0;
-  else
-    cbuf[0] = 0;
 
   k1 = 0;
   do
@@ -431,43 +403,26 @@ _gnutls_x509_parse_dn_oid (ASN1_TYPE asn1_struct,
               _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer2);
               _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".value");
 
-              len = *sizeof_buf;
-              result = asn1_read_value (asn1_struct, tmpbuffer3, buf, &len);
-
-              if (result != ASN1_SUCCESS)
+              result = _gnutls_x509_read_value(asn1_struct, tmpbuffer3, &td);
+              if (result < 0)
                 {
                   gnutls_assert ();
-                  if (result == ASN1_MEM_ERROR)
-                    *sizeof_buf = len;
-                  result = _gnutls_asn2err (result);
                   goto cleanup;
                 }
 
               if (raw_flag != 0)
                 {
-                  if ((unsigned) len > *sizeof_buf)
-                    {
-                      *sizeof_buf = len;
-                      result = GNUTLS_E_SHORT_MEMORY_BUFFER;
-                      goto cleanup;
-                    }
-                  *sizeof_buf = len;
-
+                  out->data = td.data;
+                  out->size = td.size;
                   return 0;
 
                 }
               else
                 {               /* parse data. raw_flag == 0 */
-                  printable = _gnutls_x509_oid_data_printable (oid);
-
-                  if (printable == 1)
-                    result =
-                      _gnutls_x509_oid_data2string (oid, buf, len,
-                                                    cbuf, sizeof_buf);
-                  else
-                    result =
-                      _gnutls_x509_data2hex (buf, len, cbuf, sizeof_buf);
-
+                  result =
+                    _gnutls_x509_dn_to_string (oid, td.data, td.size, out);
+                    
+                  _gnutls_free_datum(&td);
                   if (result < 0)
                     {
                       gnutls_assert ();
@@ -623,146 +578,6 @@ cleanup:
   return result;
 }
 
-static int is_printable(char p)
-{
-  if ((p >= 'a' && p <= 'z') || (p >= 'A' && p <= 'Z') ||
-      (p >= '0' && p <= '9') || p == ' ' || p == '(' || p == ')' ||
-      p == '+' || p == ',' || p == '-' || p == '.' || p == '/' || 
-      p == ':' || p == '=' || p == '?')
-    return 1;
-    
-  return 0;
-}
-
-/* This will encode and write the AttributeTypeAndValue field.
- * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute.
- * In all cases only one value is written.
- */
-int
-_gnutls_x509_encode_and_write_attribute (const char *given_oid,
-                                         ASN1_TYPE asn1_struct,
-                                         const char *where,
-                                         const void *_data,
-                                         int sizeof_data, int multi)
-{
-  const char *val_name;
-  const uint8_t *data = _data;
-  char tmp[128];
-  ASN1_TYPE c2;
-  int result;
-
-
-  /* Find how to encode the data.
-   */
-  val_name = _gnutls_x509_oid2asn_string (given_oid);
-  if (val_name == NULL)
-    {
-      gnutls_assert ();
-      _gnutls_debug_log ("Cannot find OID: %s\n", given_oid);
-      return GNUTLS_E_X509_UNSUPPORTED_OID;
-    }
-
-  result = asn1_create_element (_gnutls_get_pkix (), val_name, &c2);
-  if (result != ASN1_SUCCESS)
-    {
-      gnutls_assert ();
-      return _gnutls_asn2err (result);
-    }
-
-  tmp[0] = 0;
-
-  if ((result = _gnutls_x509_oid_data_choice (given_oid)) > 0)
-    {
-      const char *string_type;
-      int i;
-
-      string_type = "printableString";
-
-      /* Check if the data is ASN.1 printable, and use
-       * the UTF8 string type if not.
-       */
-      for (i = 0; i < sizeof_data; i++)
-        {
-          if (!is_printable (data[i]))
-            {
-              string_type = "utf8String";
-              break;
-            }
-        }
-
-      /* if the type is a CHOICE then write the
-       * type we'll use.
-       */
-      result = asn1_write_value (c2, "", string_type, 1);
-      if (result != ASN1_SUCCESS)
-        {
-          gnutls_assert ();
-          result = _gnutls_asn2err (result);
-          goto error;
-        }
-
-      _gnutls_str_cpy (tmp, sizeof (tmp), string_type);
-    }
-
-  result = asn1_write_value (c2, tmp, data, sizeof_data);
-  if (result != ASN1_SUCCESS)
-    {
-      gnutls_assert ();
-      result = _gnutls_asn2err (result);
-      goto error;
-    }
-
-
-  /* write the data (value)
-   */
-
-  _gnutls_str_cpy (tmp, sizeof (tmp), where);
-  _gnutls_str_cat (tmp, sizeof (tmp), ".value");
-
-  if (multi != 0)
-    {                           /* if not writing an AttributeTypeAndValue, 
but an Attribute */
-      _gnutls_str_cat (tmp, sizeof (tmp), "s"); /* values */
-
-      result = asn1_write_value (asn1_struct, tmp, "NEW", 1);
-      if (result != ASN1_SUCCESS)
-        {
-          gnutls_assert ();
-          result = _gnutls_asn2err (result);
-          goto error;
-        }
-
-      _gnutls_str_cat (tmp, sizeof (tmp), ".?LAST");
-
-    }
-
-  result = _gnutls_x509_der_encode_and_copy (c2, "", asn1_struct, tmp, 0);
-  if (result < 0)
-    {
-      gnutls_assert ();
-      result = _gnutls_asn2err (result);
-      goto error;
-    }
-
-  /* write the type
-   */
-  _gnutls_str_cpy (tmp, sizeof (tmp), where);
-  _gnutls_str_cat (tmp, sizeof (tmp), ".type");
-
-  result = asn1_write_value (asn1_struct, tmp, given_oid, 1);
-  if (result != ASN1_SUCCESS)
-    {
-      gnutls_assert ();
-      result = _gnutls_asn2err (result);
-      goto error;
-    }
-
-  result = 0;
-
-error:
-  asn1_delete_structure (&c2);
-  return result;
-}
-
 /* This will write the AttributeTypeAndValue field. The data must be already 
DER encoded.
  * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute.
  * In all cases only one value is written.
@@ -846,7 +661,7 @@ _gnutls_x509_decode_and_read_attribute (ASN1_TYPE 
asn1_struct,
     _gnutls_str_cat (tmpbuffer, sizeof (tmpbuffer), "s.?1");    /* .values.?1 
*/
 
   if (octet_string)
-    result = _gnutls_x509_read_string (asn1_struct, tmpbuffer, value, 
RV_OCTET_STRING);
+    result = _gnutls_x509_read_string (asn1_struct, tmpbuffer, value, 
ASN1_ETYPE_OCTET_STRING);
   else
     result = _gnutls_x509_read_value (asn1_struct, tmpbuffer, value);
   if (result < 0)
@@ -1106,6 +921,7 @@ gnutls_x509_rdn_get_by_oid (const gnutls_datum_t * idn, 
const char *oid,
 {
   int result;
   ASN1_TYPE dn = ASN1_TYPE_EMPTY;
+  gnutls_datum_t td;
 
   if (sizeof_buf == 0)
     {
@@ -1131,11 +947,13 @@ gnutls_x509_rdn_get_by_oid (const gnutls_datum_t * idn, 
const char *oid,
 
   result =
     _gnutls_x509_parse_dn_oid (dn, "rdnSequence", oid, indx,
-                               raw_flag, buf, sizeof_buf);
+                               raw_flag, &td);
 
   asn1_delete_structure (&dn);
-  return result;
+  if (result < 0)
+    return gnutls_assert_val(result);
 
+  return _gnutls_strdatum_to_buf (&td, buf, sizeof_buf);
 }
 
 /**
diff --git a/lib/x509/extensions.c b/lib/x509/extensions.c
index c251b0d..620fc1b 100644
--- a/lib/x509/extensions.c
+++ b/lib/x509/extensions.c
@@ -353,7 +353,7 @@ add_extension (ASN1_TYPE asn, const char *root, const char 
*extension_id,
   else
     snprintf (name, sizeof (name), "?LAST.extnValue");
 
-  result = _gnutls_x509_write_value (asn, name, ext_data, 0);
+  result = _gnutls_x509_write_value (asn, name, ext_data);
   if (result < 0)
     {
       gnutls_assert ();
@@ -397,7 +397,7 @@ overwrite_extension (ASN1_TYPE asn, const char *root, 
unsigned int indx,
   _gnutls_str_cpy (name2, sizeof (name2), name);
   _gnutls_str_cat (name2, sizeof (name2), ".extnValue");
 
-  result = _gnutls_x509_write_value (asn, name2, ext_data, 0);
+  result = _gnutls_x509_write_value (asn, name2, ext_data);
   if (result < 0)
     {
       gnutls_assert ();
diff --git a/lib/x509/mpi.c b/lib/x509/mpi.c
index 7792c58..af943f5 100644
--- a/lib/x509/mpi.c
+++ b/lib/x509/mpi.c
@@ -97,7 +97,7 @@ _gnutls_get_asn_mpis (ASN1_TYPE asn, const char *root,
   /* Read the algorithm's parameters
    */
   _asnstr_append_name (name, sizeof (name), root, ".subjectPublicKey");
-  result = _gnutls_x509_read_string (asn, name, &tmp, RV_BIT_STRING);
+  result = _gnutls_x509_read_value (asn, name, &tmp);
 
   if (result < 0)
     {
diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c
index 87a2503..8d6c5e4 100644
--- a/lib/x509/ocsp.c
+++ b/lib/x509/ocsp.c
@@ -718,9 +718,9 @@ gnutls_ocsp_req_add_cert (gnutls_ocsp_req_t req,
   inh.size = inhlen;
   inh.data = inh_buf;
 
-  ret = _gnutls_x509_read_string
+  ret = _gnutls_x509_read_value
     (issuer->cert, "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey",
-     &tmp, RV_BIT_STRING);
+     &tmp);
   if (ret != GNUTLS_E_SUCCESS)
     {
       gnutls_assert ();
@@ -913,7 +913,7 @@ gnutls_ocsp_req_get_nonce (gnutls_ocsp_req_t req,
       return ret;
     }
 
-  ret = _gnutls_x509_decode_string (NULL, tmp.data, (size_t) tmp.size,
+  ret = _gnutls_x509_decode_string (ASN1_ETYPE_OCTET_STRING, tmp.data, 
(size_t) tmp.size,
                                    nonce);
   if (ret < 0)
     {
@@ -1695,7 +1695,7 @@ gnutls_ocsp_resp_get_nonce (gnutls_ocsp_resp_t resp,
       return ret;
     }
 
-  ret = _gnutls_x509_decode_string (NULL, tmp.data, (size_t) tmp.size,
+  ret = _gnutls_x509_decode_string (ASN1_ETYPE_OCTET_STRING, tmp.data, 
(size_t) tmp.size,
                                    nonce);
   if (ret < 0)
     {
@@ -1763,7 +1763,7 @@ gnutls_ocsp_resp_get_signature (gnutls_ocsp_resp_t resp,
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  ret = _gnutls_x509_read_string (resp->basicresp, "signature", sig, 
RV_BIT_STRING);
+  ret = _gnutls_x509_read_value (resp->basicresp, "signature", sig);
   if (ret != GNUTLS_E_SUCCESS)
     {
       gnutls_assert ();
diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c
index edd0682..1344589 100644
--- a/lib/x509/pkcs12.c
+++ b/lib/x509/pkcs12.c
@@ -68,7 +68,7 @@ _decode_pkcs12_auth_safe (ASN1_TYPE pkcs12, ASN1_TYPE * 
authen_safe,
    */
 
   result =
-    _gnutls_x509_read_string (pkcs12, "authSafe.content", &auth_safe, 
RV_OCTET_STRING);
+    _gnutls_x509_read_string (pkcs12, "authSafe.content", &auth_safe, 
ASN1_ETYPE_OCTET_STRING);
   if (result < 0)
     {
       gnutls_assert ();
@@ -343,24 +343,6 @@ bag_to_oid (int bag)
   return NULL;
 }
 
-static inline char *
-ucs2_to_ascii (char *data, int size)
-{
-  int i, j;
-
-  for (i = 0; i < size / 2; i++)
-    {
-      j = 2 * i + 1;
-      if (isascii (data[j]))
-        data[i] = data[i * 2 + 1];
-      else
-        data[i] = '?';
-    }
-  data[i] = 0;
-
-  return data;
-}
-
 /* Decodes the SafeContents, and puts the output in
  * the given bag. 
  */
@@ -496,7 +478,8 @@ _pkcs12_decode_safe_contents (const gnutls_datum_t * 
content,
             if (strcmp (oid, KEY_ID_OID) == 0)
               {
                 result =
-                  _gnutls_x509_decode_string (NULL, attr_val.data, 
attr_val.size, &t);
+                  _gnutls_x509_decode_string (ASN1_ETYPE_OCTET_STRING, 
attr_val.data, 
+                                              attr_val.size, &t);
                 _gnutls_free_datum (&attr_val);
                 if (result < 0)
                   {
@@ -514,7 +497,7 @@ _pkcs12_decode_safe_contents (const gnutls_datum_t * 
content,
             else if (strcmp (oid, FRIENDLY_NAME_OID) == 0)
               {
                 result =
-                  _gnutls_x509_decode_string ("BMPString",
+                  _gnutls_x509_decode_string (ASN1_ETYPE_BMP_STRING,
                                               attr_val.data, attr_val.size, 
&t);
                 _gnutls_free_datum (&attr_val);
                 if (result < 0)
@@ -528,8 +511,7 @@ _pkcs12_decode_safe_contents (const gnutls_datum_t * 
content,
                 attr_val.data = t.data;
                 attr_val.size = t.size;
 
-                bag->element[i].friendly_name =
-                  ucs2_to_ascii ((char*)attr_val.data, attr_val.size);
+                bag->element[i].friendly_name = (char*)t.data;
               }
             else
               {
@@ -567,7 +549,7 @@ _parse_safe_contents (ASN1_TYPE sc, const char *sc_name,
   /* Step 1. Extract the content.
    */
 
-  result = _gnutls_x509_read_string (sc, sc_name, &content, RV_OCTET_STRING);
+  result = _gnutls_x509_read_string (sc, sc_name, &content, 
ASN1_ETYPE_OCTET_STRING);
   if (result < 0)
     {
       gnutls_assert ();
@@ -1320,7 +1302,7 @@ _pkcs12_encode_safe_contents (gnutls_pkcs12_bag_t bag, 
ASN1_TYPE * contents,
               goto cleanup;
             }
 
-          result = _gnutls_x509_write_value (c2, "?LAST.bagValue", &tmp, 0);
+          result = _gnutls_x509_write_value (c2, "?LAST.bagValue", &tmp);
 
           _gnutls_free_datum (&tmp);
 
@@ -1329,7 +1311,7 @@ _pkcs12_encode_safe_contents (gnutls_pkcs12_bag_t bag, 
ASN1_TYPE * contents,
         {
 
           result = _gnutls_x509_write_value (c2, "?LAST.bagValue",
-                                             &bag->element[i].data, 0);
+                                             &bag->element[i].data);
         }
 
       if (result < 0)
diff --git a/lib/x509/pkcs12_bag.c b/lib/x509/pkcs12_bag.c
index 64db1bf..c7cd409 100644
--- a/lib/x509/pkcs12_bag.c
+++ b/lib/x509/pkcs12_bag.c
@@ -195,7 +195,7 @@ _pkcs12_decode_crt_bag (gnutls_pkcs12_bag_type_t type,
           goto cleanup;
         }
 
-      ret = _gnutls_x509_read_string (c2, "certValue", out, RV_OCTET_STRING);
+      ret = _gnutls_x509_read_string (c2, "certValue", out, 
ASN1_ETYPE_OCTET_STRING);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -221,7 +221,7 @@ _pkcs12_decode_crt_bag (gnutls_pkcs12_bag_type_t type,
           goto cleanup;
         }
 
-      ret = _gnutls_x509_read_string (c2, "crlValue", out, RV_OCTET_STRING);
+      ret = _gnutls_x509_read_string (c2, "crlValue", out, 
ASN1_ETYPE_OCTET_STRING);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -247,7 +247,7 @@ _pkcs12_decode_crt_bag (gnutls_pkcs12_bag_type_t type,
           goto cleanup;
         }
 
-      ret = _gnutls_x509_read_string (c2, "secretValue", out, RV_OCTET_STRING);
+      ret = _gnutls_x509_read_string (c2, "secretValue", out, 
ASN1_ETYPE_OCTET_STRING);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -300,7 +300,7 @@ _pkcs12_encode_crt_bag (gnutls_pkcs12_bag_type_t type,
           goto cleanup;
         }
 
-      ret = _gnutls_x509_write_value (c2, "certValue", raw, 1);
+      ret = _gnutls_x509_write_string (c2, "certValue", raw, 
ASN1_ETYPE_OCTET_STRING);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -326,7 +326,7 @@ _pkcs12_encode_crt_bag (gnutls_pkcs12_bag_type_t type,
           goto cleanup;
         }
 
-      ret = _gnutls_x509_write_value (c2, "crlValue", raw, 1);
+      ret = _gnutls_x509_write_string (c2, "crlValue", raw, 
ASN1_ETYPE_OCTET_STRING);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -352,7 +352,7 @@ _pkcs12_encode_crt_bag (gnutls_pkcs12_bag_type_t type,
           goto cleanup;
         }
 
-      ret = _gnutls_x509_write_value (c2, "secretValue", raw, 1);
+      ret = _gnutls_x509_write_string (c2, "secretValue", raw, 
ASN1_ETYPE_OCTET_STRING);
       if (ret < 0)
         {
           gnutls_assert ();
diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c
index 5e95bb4..820a69f 100644
--- a/lib/x509/privkey.c
+++ b/lib/x509/privkey.c
@@ -300,7 +300,7 @@ _gnutls_privkey_decode_ecc_key (const gnutls_datum_t * 
raw_key,
     }
 
   /* read the public key */
-  ret = _gnutls_x509_read_string(pkey_asn, "publicKey", &out, RV_BIT_STRING);
+  ret = _gnutls_x509_read_value (pkey_asn, "publicKey", &out);
   if (ret < 0)
     {
       gnutls_assert();
diff --git a/lib/x509/x509.c b/lib/x509/x509.c
index 45a24df..627ffa5 100644
--- a/lib/x509/x509.c
+++ b/lib/x509/x509.c
@@ -308,15 +308,22 @@ gnutls_x509_crt_get_issuer_dn_by_oid (gnutls_x509_crt_t 
cert,
                                       unsigned int raw_flag, void *buf,
                                       size_t * buf_size)
 {
+gnutls_datum_t td;
+int ret;
+
   if (cert == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  return _gnutls_x509_parse_dn_oid (cert->cert,
+  ret = _gnutls_x509_parse_dn_oid (cert->cert,
                                     "tbsCertificate.issuer.rdnSequence",
-                                    oid, indx, raw_flag, buf, buf_size);
+                                    oid, indx, raw_flag, &td);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  
+  return _gnutls_strdatum_to_buf (&td, buf, buf_size);
 }
 
 /**
@@ -419,15 +426,22 @@ gnutls_x509_crt_get_dn_by_oid (gnutls_x509_crt_t cert, 
const char *oid,
                                int indx, unsigned int raw_flag,
                                void *buf, size_t * buf_size)
 {
+gnutls_datum_t td;
+int ret;
+
   if (cert == NULL)
     {
       gnutls_assert ();
       return GNUTLS_E_INVALID_REQUEST;
     }
 
-  return _gnutls_x509_parse_dn_oid (cert->cert,
+  ret = _gnutls_x509_parse_dn_oid (cert->cert,
                                     "tbsCertificate.subject.rdnSequence",
-                                    oid, indx, raw_flag, buf, buf_size);
+                                    oid, indx, raw_flag, &td);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+  
+  return _gnutls_strdatum_to_buf (&td, buf, buf_size);
 }
 
 /**
@@ -1967,7 +1981,7 @@ gnutls_x509_crt_get_policy (gnutls_x509_crt_t crt, int 
indx,
         {
           snprintf (tmpstr, sizeof (tmpstr), 
"?%u.policyQualifiers.?%u.qualifier", indx, i+1);
 
-          ret = _gnutls_x509_read_string(c2, tmpstr, &td, RV_IA5STRING);
+          ret = _gnutls_x509_read_string(c2, tmpstr, &td, 
ASN1_ETYPE_IA5_STRING);
           if (ret < 0)
             {
               gnutls_assert();
@@ -1985,7 +1999,7 @@ gnutls_x509_crt_get_policy (gnutls_x509_crt_t crt, int 
indx,
 
           snprintf (tmpstr, sizeof (tmpstr), 
"?%u.policyQualifiers.?%u.qualifier", indx, i+1);
 
-          ret = _gnutls_x509_read_string(c2, tmpstr, &td, RV_RAW);
+          ret = _gnutls_x509_read_value(c2, tmpstr, &td);
           if (ret < 0)
             {
               gnutls_assert();
@@ -2424,10 +2438,12 @@ gnutls_x509_crt_get_issuer (gnutls_x509_crt_t cert, 
gnutls_x509_dn_t * dn)
  * The X.509 distinguished name is a sequence of sequences of strings
  * and this is what the @irdn and @iava indexes model.
  *
- * Note that @ava will contain pointers into the @dn structure, so you
- * should not modify any data or deallocate it.  Note also that the DN
- * in turn points into the original certificate structure, and thus
- * you may not deallocate the certificate and continue to access @dn.
+ * Note that @ava will contain pointers into the @dn structure which
+ * in turns points to the original certificate. Thus you should not
+ * modify any data or deallocate any of those.
+ *
+ * This is a low-level function that depends on the internals of
+ * libtasn1.
  *
  * Returns: Returns 0 on success, or an error code.
  **/
@@ -3724,8 +3740,8 @@ gnutls_x509_crt_get_subject_unique_id (gnutls_x509_crt_t 
crt, char *buf,
   gnutls_datum_t datum = { NULL, 0 };
 
   result =
-    _gnutls_x509_read_string (crt->cert, "tbsCertificate.subjectUniqueID",
-                             &datum, RV_BIT_STRING);
+    _gnutls_x509_read_value (crt->cert, "tbsCertificate.subjectUniqueID",
+                             &datum);
 
   if (datum.size > *buf_size)
     {                           /* then we're not going to fit */
@@ -3770,8 +3786,8 @@ gnutls_x509_crt_get_issuer_unique_id (gnutls_x509_crt_t 
crt, char *buf,
   gnutls_datum_t datum = { NULL, 0 };
 
   result =
-    _gnutls_x509_read_string (crt->cert, "tbsCertificate.issuerUniqueID",
-                             &datum, RV_BIT_STRING);
+    _gnutls_x509_read_value (crt->cert, "tbsCertificate.issuerUniqueID",
+                              &datum);
 
   if (datum.size > *buf_size)
     {                           /* then we're not going to fit */
diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h
index 59475e7..6c5b33c 100644
--- a/lib/x509/x509_int.h
+++ b/lib/x509/x509_int.h
@@ -111,10 +111,12 @@ int _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct,
                            const char *asn1_rdn_name, char *buf,
                            size_t * sizeof_buf);
 
-int _gnutls_x509_parse_dn_oid (ASN1_TYPE asn1_struct,
-                               const char *asn1_rdn_name, const char *oid,
-                               int indx, unsigned int raw_flag, void *buf,
-                               size_t * sizeof_buf);
+int
+_gnutls_x509_parse_dn_oid (ASN1_TYPE asn1_struct,
+                           const char *asn1_rdn_name,
+                           const char *given_oid, int indx,
+                           unsigned int raw_flag,
+                           gnutls_datum_t* out);
 
 int _gnutls_x509_set_dn_oid (ASN1_TYPE asn1_struct,
                              const char *asn1_rdn_name, const char *oid,
diff --git a/lib/x509/x509_write.c b/lib/x509/x509_write.c
index a8bf474..77a26a8 100644
--- a/lib/x509/x509_write.c
+++ b/lib/x509/x509_write.c
@@ -1720,8 +1720,8 @@ gnutls_x509_crt_set_policy (gnutls_x509_crt_t crt, struct 
gnutls_x509_policy_st*
           tmpd.data = (void*)policy->qualifier[i].data;
           tmpd.size = policy->qualifier[i].size;
           
-          result = _gnutls_x509_write_value(c2, 
"?LAST.policyQualifiers.?LAST.qualifier", 
-                                            &tmpd, RV_IA5STRING);
+          result = _gnutls_x509_write_string(c2, 
"?LAST.policyQualifiers.?LAST.qualifier", 
+                                             &tmpd, ASN1_ETYPE_IA5_STRING);
           if (result < 0)
             {
               gnutls_assert();
@@ -1748,7 +1748,7 @@ gnutls_x509_crt_set_policy (gnutls_x509_crt_t crt, struct 
gnutls_x509_policy_st*
             }
 
           result = _gnutls_x509_write_value(c2, 
"?LAST.policyQualifiers.?LAST.qualifier", 
-                                            &der_data, RV_RAW);
+                                            &der_data);
           _gnutls_free_datum(&der_data);
           if (result < 0)
             {
diff --git a/m4/hooks.m4 b/m4/hooks.m4
index c42b474..5812205 100644
--- a/m4/hooks.m4
+++ b/m4/hooks.m4
@@ -103,7 +103,7 @@ fi
       included_libtasn1=$withval,
       included_libtasn1=no)
   if test "$included_libtasn1" = "no"; then
-    PKG_CHECK_MODULES(LIBTASN1, [libtasn1 >= 2.14], [], 
[included_libtasn1=yes])
+    PKG_CHECK_MODULES(LIBTASN1, [libtasn1 >= 3.1], [], [included_libtasn1=yes])
     if test "$included_libtasn1" = yes; then
       AC_MSG_WARN([[
   *** 
diff --git a/tests/crq_apis.c b/tests/crq_apis.c
index c34babf..83ed6d9 100644
--- a/tests/crq_apis.c
+++ b/tests/crq_apis.c
@@ -118,7 +118,7 @@ doit (void)
 
   ret = gnutls_x509_crq_get_challenge_password (crq, NULL, &s);
   if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
-    fail ("gnutls_x509_crq_get_challenge_password %d\n", ret);
+    fail ("%d: gnutls_x509_crq_get_challenge_password %d: %s\n", __LINE__, 
ret, gnutls_strerror(ret));
 
   ret = gnutls_x509_crq_set_challenge_password (crq, "foo");
   if (ret != 0)
@@ -126,14 +126,14 @@ doit (void)
 
   s = 0;
   ret = gnutls_x509_crq_get_challenge_password (crq, NULL, &s);
-  if (ret != 0 || s != 3)
-    fail ("gnutls_x509_crq_get_challenge_password2 %d/%d\n", ret, (int) s);
+  if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER || s != 4)
+    fail ("%d: gnutls_x509_crq_get_challenge_password %d: %s (passlen: %d)\n", 
__LINE__, ret, gnutls_strerror(ret), (int)s);
 
   s = 10;
   ret = gnutls_x509_crq_get_challenge_password (crq, smallbuf, &s);
   if (ret != 0 || s != 3 || strcmp (smallbuf, "foo") != 0)
-    fail ("gnutls_x509_crq_get_challenge_password3 %d/%d/%s\n",
-          ret, (int) s, smallbuf);
+    fail ("%d: gnutls_x509_crq_get_challenge_password3 %d/%d/%s\n",
+          __LINE__, ret, (int) s, smallbuf);
 
   s = 0;
   ret = gnutls_x509_crq_get_extension_info (crq, 0, NULL, &s, NULL);
diff --git a/tests/set_pkcs12_cred.c b/tests/set_pkcs12_cred.c
index 8770bea..71a4ce7 100644
--- a/tests/set_pkcs12_cred.c
+++ b/tests/set_pkcs12_cred.c
@@ -25,9 +25,16 @@
 #endif
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #include "utils.h"
 
+static void
+tls_log_func (int level, const char *str)
+{
+  fprintf (stderr, "<%d>| %s", level, str);
+}
+
 void
 doit (void)
 {
@@ -39,6 +46,10 @@ doit (void)
   if (ret < 0)
     fail ("gnutls_global_init failed %d\n", ret);
 
+  gnutls_global_set_log_function (tls_log_func);
+  if (debug)
+    gnutls_global_set_log_level (4711);
+
   ret = gnutls_certificate_allocate_credentials (&x509cred);
   if (ret < 0)
     fail ("gnutls_certificate_allocate_credentials failed %d\n", ret);


hooks/post-receive
-- 
GNU gnutls



reply via email to

[Prev in Thread] Current Thread [Next in Thread]