aboutsummaryrefslogtreecommitdiffstats
path: root/src/gnutls.c
diff options
context:
space:
mode:
authorTed Zlatanov2017-07-14 11:04:19 -0400
committerTed Zlatanov2017-07-14 11:06:16 -0400
commit583995c62dd424775dda33d5134ce04bee2ae685 (patch)
tree732251c7c468b20a70d20578b778946cf49f77fe /src/gnutls.c
parent0f3cc0b8245dfd7a9f6fcc95ec148be03fde8931 (diff)
downloademacs-583995c62dd424775dda33d5134ce04bee2ae685.tar.gz
emacs-583995c62dd424775dda33d5134ce04bee2ae685.zip
GnuTLS HMAC and symmetric cipher support
* etc/NEWS: Add news for new feature. * doc/lispref/text.texi (GnuTLS Cryptography): Add documentation. * configure.ac: Add macros HAVE_GNUTLS3_DIGEST, HAVE_GNUTLS3_CIPHER, HAVE_GNUTLS3_AEAD, HAVE_GNUTLS3_HMAC. * src/fns.c (Fsecure_hash_algorithms): Add function to list supported `secure-hash' algorithms. (extract_data_from_object): Add data extraction function that can operate on buffers and strings. (secure_hash): Use it. (Fsecure_hash): Mention `secure-hash-algorithms'. * src/gnutls.h: Include gnutls/crypto.h. * src/gnutls.c (Fgnutls_ciphers, gnutls_symmetric_aead) (gnutls_symmetric, Fgnutls_symmetric_encrypt, Fgnutls_symmetric_decrypt) (Fgnutls_macs, Fgnutls_digests, Fgnutls_hash_mac, Fgnutls_hash_digest) (Fgnutls_available_p): Implement GnuTLS cryptographic integration. * test/lisp/net/gnutls-tests.el: Add tests.
Diffstat (limited to 'src/gnutls.c')
-rw-r--r--src/gnutls.c674
1 files changed, 670 insertions, 4 deletions
diff --git a/src/gnutls.c b/src/gnutls.c
index 2078ad88f28..7a4e92f0d3f 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -24,6 +24,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
24#include "process.h" 24#include "process.h"
25#include "gnutls.h" 25#include "gnutls.h"
26#include "coding.h" 26#include "coding.h"
27#include "buffer.h"
27 28
28#ifdef HAVE_GNUTLS 29#ifdef HAVE_GNUTLS
29 30
@@ -1697,24 +1698,660 @@ This function may also return `gnutls-e-again', or
1697 1698
1698#endif /* HAVE_GNUTLS */ 1699#endif /* HAVE_GNUTLS */
1699 1700
1701#ifdef HAVE_GNUTLS3
1702
1703DEFUN ("gnutls-ciphers", Fgnutls_ciphers, Sgnutls_ciphers, 0, 0, 0,
1704 doc: /* Return alist of GnuTLS symmetric cipher descriptions as plists.
1705The alist key is the cipher name. */)
1706 (void)
1707{
1708 Lisp_Object ciphers = Qnil;
1709
1710 const gnutls_cipher_algorithm_t* gciphers = gnutls_cipher_list ();
1711 for (size_t pos = 0; gciphers[pos] != GNUTLS_CIPHER_NULL; pos++)
1712 {
1713 const gnutls_cipher_algorithm_t gca = gciphers[pos];
1714
1715 Lisp_Object cp = listn (CONSTYPE_HEAP, 15,
1716 /* A symbol representing the cipher */
1717 intern (gnutls_cipher_get_name (gca)),
1718 /* The internally meaningful cipher ID */
1719 QCcipher_id,
1720 make_number (gca),
1721 /* The type (vs. other GnuTLS objects). */
1722 QCtype,
1723 Qgnutls_type_cipher,
1724 /* The tag size (nonzero means AEAD). */
1725 QCcipher_aead_capable,
1726 (gnutls_cipher_get_tag_size (gca) == 0) ? Qnil : Qt,
1727 /* The tag size (nonzero means AEAD). */
1728 QCcipher_tagsize,
1729 make_number (gnutls_cipher_get_tag_size (gca)),
1730 /* The block size */
1731 QCcipher_blocksize,
1732 make_number (gnutls_cipher_get_block_size (gca)),
1733 /* The key size */
1734 QCcipher_keysize,
1735 make_number (gnutls_cipher_get_key_size (gca)),
1736 /* IV size */
1737 QCcipher_ivsize,
1738 make_number (gnutls_cipher_get_iv_size (gca)));
1739
1740 ciphers = Fcons (cp, ciphers);
1741 }
1742
1743 return ciphers;
1744}
1745
1746static Lisp_Object
1747gnutls_symmetric_aead (bool encrypting, gnutls_cipher_algorithm_t gca,
1748 Lisp_Object cipher,
1749 const char* kdata, size_t ksize,
1750 const char* vdata, size_t vsize,
1751 const char* idata, size_t isize,
1752 Lisp_Object aead_auth)
1753{
1754#ifdef HAVE_GNUTLS3_AEAD
1755
1756 const char* desc = (encrypting ? "encrypt" : "decrypt");
1757 int ret = GNUTLS_E_SUCCESS;
1758 Lisp_Object actual_iv = make_unibyte_string (vdata, vsize);
1759
1760 gnutls_aead_cipher_hd_t acipher;
1761 gnutls_datum_t key_datum = { (unsigned char*) kdata, ksize };
1762 ret = gnutls_aead_cipher_init (&acipher, gca, &key_datum);
1763
1764 if (ret < GNUTLS_E_SUCCESS)
1765 {
1766 const char* str = gnutls_strerror (ret);
1767 if (!str)
1768 str = "unknown";
1769 error ("GnuTLS AEAD cipher %s/%s initialization failed: %s",
1770 gnutls_cipher_get_name (gca), desc, str);
1771 }
1772
1773 size_t storage_length = isize + gnutls_cipher_get_tag_size (gca);
1774 USE_SAFE_ALLOCA;
1775 unsigned char *storage = SAFE_ALLOCA (storage_length);
1776
1777 const char* aead_auth_data = NULL;
1778 size_t aead_auth_size = 0;
1779
1780 if (!NILP (aead_auth))
1781 {
1782 if (BUFFERP (aead_auth) || STRINGP (aead_auth))
1783 aead_auth = list1 (aead_auth);
1784
1785 CHECK_CONS (aead_auth);
1786
1787 ptrdiff_t astart_byte, aend_byte;
1788 const char* adata = extract_data_from_object (aead_auth, &astart_byte, &aend_byte);
1789
1790 if (adata == NULL)
1791 error ("GnuTLS AEAD cipher auth extraction failed");
1792
1793 aead_auth_data = adata;
1794 aead_auth_size = aend_byte - astart_byte;
1795 }
1796
1797 size_t expected_remainder = 0;
1798
1799 if (!encrypting)
1800 expected_remainder = gnutls_cipher_get_tag_size (gca);
1801
1802 if ((isize - expected_remainder) % gnutls_cipher_get_block_size (gca) != 0)
1803 error ("GnuTLS AEAD cipher %s/%s input block length %ld was not a "
1804 "multiple of the required %ld plus the expected tag remainder %ld",
1805 gnutls_cipher_get_name (gca), desc,
1806 (long) isize, (long) gnutls_cipher_get_block_size (gca),
1807 (long) expected_remainder);
1808
1809 if (encrypting)
1810 ret = gnutls_aead_cipher_encrypt (acipher,
1811 vdata, vsize,
1812 aead_auth_data, aead_auth_size,
1813 gnutls_cipher_get_tag_size (gca),
1814 idata, isize,
1815 storage, &storage_length);
1816 else
1817 ret = gnutls_aead_cipher_decrypt (acipher,
1818 vdata, vsize,
1819 aead_auth_data, aead_auth_size,
1820 gnutls_cipher_get_tag_size (gca),
1821 idata, isize,
1822 storage, &storage_length);
1823
1824 if (ret < GNUTLS_E_SUCCESS)
1825 {
1826 memset (storage, 0, storage_length);
1827 SAFE_FREE ();
1828 gnutls_aead_cipher_deinit (acipher);
1829 const char* str = gnutls_strerror (ret);
1830 if (!str)
1831 str = "unknown";
1832 error ("GnuTLS AEAD cipher %s %sion failed: %s",
1833 gnutls_cipher_get_name (gca), desc, str);
1834 }
1835
1836 gnutls_aead_cipher_deinit (acipher);
1837
1838 Lisp_Object output = make_unibyte_string ((const char *)storage, storage_length);
1839 memset (storage, 0, storage_length);
1840 SAFE_FREE ();
1841 return list2 (output, actual_iv);
1842#else
1843 error ("GnuTLS AEAD cipher %ld was invalid or not found", (long) gca);
1844#endif
1845}
1846
1847static Lisp_Object
1848gnutls_symmetric (bool encrypting, Lisp_Object cipher,
1849 Lisp_Object key, Lisp_Object iv,
1850 Lisp_Object input, Lisp_Object aead_auth)
1851{
1852 if (BUFFERP (key) || STRINGP (key))
1853 key = list1 (key);
1854
1855 CHECK_CONS (key);
1856
1857 if (BUFFERP (input) || STRINGP (input))
1858 input = list1 (input);
1859
1860 CHECK_CONS (input);
1861
1862 if (BUFFERP (iv) || STRINGP (iv))
1863 iv = list1 (iv);
1864
1865 CHECK_CONS (iv);
1866
1867
1868 const char* desc = (encrypting ? "encrypt" : "decrypt");
1869
1870 int ret = GNUTLS_E_SUCCESS;
1871
1872 gnutls_cipher_algorithm_t gca = GNUTLS_CIPHER_UNKNOWN;
1873
1874 Lisp_Object info = Qnil;
1875 if (STRINGP (cipher))
1876 cipher = intern (SSDATA (cipher));
1877
1878 if (SYMBOLP (cipher))
1879 info = XCDR (Fassq (cipher, Fgnutls_ciphers ()));
1880 else if (INTEGERP (cipher))
1881 gca = XINT (cipher);
1882 else
1883 info = cipher;
1884
1885 if (!NILP (info) && CONSP (info))
1886 {
1887 Lisp_Object v = Fplist_get (info, QCcipher_id);
1888 if (INTEGERP (v))
1889 gca = XINT (v);
1890 }
1891
1892 if (gca == GNUTLS_CIPHER_UNKNOWN)
1893 error ("GnuTLS cipher was invalid or not found");
1894
1895 ptrdiff_t kstart_byte, kend_byte;
1896 const char* kdata = extract_data_from_object (key, &kstart_byte, &kend_byte);
1897
1898 if (kdata == NULL)
1899 error ("GnuTLS cipher key extraction failed");
1900
1901 if ((kend_byte - kstart_byte) != gnutls_cipher_get_key_size (gca))
1902 error ("GnuTLS cipher %s/%s key length %ld was not equal to "
1903 "the required %ld",
1904 gnutls_cipher_get_name (gca), desc,
1905 kend_byte - kstart_byte, (long) gnutls_cipher_get_key_size (gca));
1906
1907 ptrdiff_t vstart_byte, vend_byte;
1908 const char* vdata = extract_data_from_object (iv, &vstart_byte, &vend_byte);
1909
1910 if (vdata == NULL)
1911 error ("GnuTLS cipher IV extraction failed");
1912
1913 if ((vend_byte - vstart_byte) != gnutls_cipher_get_iv_size (gca))
1914 error ("GnuTLS cipher %s/%s IV length %ld was not equal to "
1915 "the required %ld",
1916 gnutls_cipher_get_name (gca), desc,
1917 vend_byte - vstart_byte, (long) gnutls_cipher_get_iv_size (gca));
1918
1919 Lisp_Object actual_iv = make_unibyte_string (vdata, vend_byte - vstart_byte);
1920
1921 ptrdiff_t istart_byte, iend_byte;
1922 const char* idata = extract_data_from_object (input, &istart_byte, &iend_byte);
1923
1924 if (idata == NULL)
1925 error ("GnuTLS cipher input extraction failed");
1926
1927 /* Is this an AEAD cipher? */
1928 if (gnutls_cipher_get_tag_size (gca) > 0)
1929 {
1930 Lisp_Object aead_output =
1931 gnutls_symmetric_aead (encrypting, gca, cipher,
1932 kdata, kend_byte - kstart_byte,
1933 vdata, vend_byte - vstart_byte,
1934 idata, iend_byte - istart_byte,
1935 aead_auth);
1936 if (STRINGP (XCAR (key)))
1937 Fclear_string (XCAR (key));
1938 return aead_output;
1939 }
1940
1941 if ((iend_byte - istart_byte) % gnutls_cipher_get_block_size (gca) != 0)
1942 error ("GnuTLS cipher %s/%s input block length %ld was not a multiple "
1943 "of the required %ld",
1944 gnutls_cipher_get_name (gca), desc,
1945 iend_byte - istart_byte, (long) gnutls_cipher_get_block_size (gca));
1946
1947 gnutls_cipher_hd_t hcipher;
1948 gnutls_datum_t key_datum = { (unsigned char*) kdata, kend_byte - kstart_byte };
1949
1950 ret = gnutls_cipher_init (&hcipher, gca, &key_datum, NULL);
1951
1952 if (ret < GNUTLS_E_SUCCESS)
1953 {
1954 const char* str = gnutls_strerror (ret);
1955 if (!str)
1956 str = "unknown";
1957 error ("GnuTLS cipher %s/%s initialization failed: %s",
1958 gnutls_cipher_get_name (gca), desc, str);
1959 }
1960
1961 /* Note that this will not support streaming block mode. */
1962 gnutls_cipher_set_iv (hcipher, (void*) vdata, vend_byte - vstart_byte);
1963
1964 /*
1965 * GnuTLS docs: "For the supported ciphers the encrypted data length
1966 * will equal the plaintext size."
1967 */
1968 size_t storage_length = iend_byte - istart_byte;
1969 Lisp_Object storage = make_uninit_string (storage_length);
1970
1971 if (encrypting)
1972 ret = gnutls_cipher_encrypt2 (hcipher,
1973 idata, iend_byte - istart_byte,
1974 SSDATA (storage), storage_length);
1975 else
1976 ret = gnutls_cipher_decrypt2 (hcipher,
1977 idata, iend_byte - istart_byte,
1978 SSDATA (storage), storage_length);
1979
1980 if (STRINGP (XCAR (key)))
1981 Fclear_string (XCAR (key));
1982
1983 if (ret < GNUTLS_E_SUCCESS)
1984 {
1985 gnutls_cipher_deinit (hcipher);
1986 const char* str = gnutls_strerror (ret);
1987 if (!str)
1988 str = "unknown";
1989 error ("GnuTLS cipher %s %sion failed: %s",
1990 gnutls_cipher_get_name (gca), desc, str);
1991 }
1992
1993 gnutls_cipher_deinit (hcipher);
1994
1995 return list2 (storage, actual_iv);
1996}
1997
1998DEFUN ("gnutls-symmetric-encrypt", Fgnutls_symmetric_encrypt, Sgnutls_symmetric_encrypt, 4, 5, 0,
1999 doc: /* Encrypt INPUT with symmetric CIPHER, KEY+AEAD_AUTH, and IV to a unibyte string.
2000
2001Returns nil on error.
2002
2003The KEY can be specified as a buffer or string or in other ways
2004(see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). The KEY will be
2005wiped after use if it's a string.
2006
2007The IV and INPUT and the optional AEAD_AUTH can be
2008specified as a buffer or string or in other ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs').
2009
2010The alist of symmetric ciphers can be obtained with `gnutls-ciphers`.
2011The CIPHER may be a string or symbol matching a key in that alist, or
2012a plist with the `:cipher-id' numeric property, or the number itself.
2013
2014AEAD ciphers: these ciphers will have a `gnutls-ciphers' entry with
2015:cipher-aead-capable set to t. AEAD_AUTH can be supplied for
2016these AEAD ciphers, but it may still be omitted (nil) as well. */)
2017 (Lisp_Object cipher, Lisp_Object key, Lisp_Object iv, Lisp_Object input, Lisp_Object aead_auth)
2018{
2019 return gnutls_symmetric (true, cipher, key, iv, input, aead_auth);
2020}
2021
2022DEFUN ("gnutls-symmetric-decrypt", Fgnutls_symmetric_decrypt, Sgnutls_symmetric_decrypt, 4, 5, 0,
2023 doc: /* Decrypt INPUT with symmetric CIPHER, KEY+AEAD_AUTH, and IV to a unibyte string.
2024
2025Returns nil on error.
2026
2027The KEY can be specified as a buffer or string or in other ways
2028(see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). The KEY will be
2029wiped after use if it's a string.
2030
2031The IV and INPUT and the optional AEAD_AUTH can be
2032specified as a buffer or string or in other ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs').
2033
2034The alist of symmetric ciphers can be obtained with `gnutls-ciphers`.
2035The CIPHER may be a string or symbol matching a key in that alist, or
2036a plist with the `:cipher-id' numeric property, or the number itself.
2037
2038AEAD ciphers: these ciphers will have a `gnutls-ciphers' entry with
2039:cipher-aead-capable set to t. AEAD_AUTH can be supplied for
2040these AEAD ciphers, but it may still be omitted (nil) as well. */)
2041 (Lisp_Object cipher, Lisp_Object key, Lisp_Object iv, Lisp_Object input, Lisp_Object aead_auth)
2042{
2043 return gnutls_symmetric (false, cipher, key, iv, input, aead_auth);
2044}
2045
2046DEFUN ("gnutls-macs", Fgnutls_macs, Sgnutls_macs, 0, 0, 0,
2047 doc: /* Return alist of GnuTLS mac-algorithm method descriptions as plists.
2048
2049Use the value of the alist (extract it with `alist-get' for instance)
2050with `gnutls-hash-mac'. The alist key is the mac-algorithm method
2051name. */)
2052 (void)
2053{
2054 Lisp_Object mac_algorithms = Qnil;
2055 const gnutls_mac_algorithm_t* macs = gnutls_mac_list ();
2056 for (size_t pos = 0; macs[pos] != 0; pos++)
2057 {
2058 const gnutls_mac_algorithm_t gma = macs[pos];
2059
2060 const char* name = gnutls_mac_get_name (gma);
2061
2062 Lisp_Object mp = listn (CONSTYPE_HEAP, 11,
2063 /* A symbol representing the mac-algorithm. */
2064 intern (name),
2065 /* The internally meaningful mac-algorithm ID. */
2066 QCmac_algorithm_id,
2067 make_number (gma),
2068 /* The type (vs. other GnuTLS objects). */
2069 QCtype,
2070 Qgnutls_type_mac_algorithm,
2071 /* The output length. */
2072 QCmac_algorithm_length,
2073 make_number (gnutls_hmac_get_len (gma)),
2074 /* The key size. */
2075 QCmac_algorithm_keysize,
2076 make_number (gnutls_mac_get_key_size (gma)),
2077 /* The nonce size. */
2078 QCmac_algorithm_noncesize,
2079 make_number (gnutls_mac_get_nonce_size (gma)));
2080 mac_algorithms = Fcons (mp, mac_algorithms);
2081 }
2082
2083 return mac_algorithms;
2084}
2085
2086DEFUN ("gnutls-digests", Fgnutls_digests, Sgnutls_digests, 0, 0, 0,
2087 doc: /* Return alist of GnuTLS digest-algorithm method descriptions as plists.
2088
2089Use the value of the alist (extract it with `alist-get' for instance)
2090with `gnutls-hash-digest'. The alist key is the digest-algorithm
2091method name. */)
2092 (void)
2093{
2094 Lisp_Object digest_algorithms = Qnil;
2095 const gnutls_digest_algorithm_t* digests = gnutls_digest_list ();
2096 for (size_t pos = 0; digests[pos] != 0; pos++)
2097 {
2098 const gnutls_digest_algorithm_t gda = digests[pos];
2099
2100 const char* name = gnutls_digest_get_name (gda);
2101
2102 Lisp_Object mp = listn (CONSTYPE_HEAP, 7,
2103 /* A symbol representing the digest-algorithm. */
2104 intern (name),
2105 /* The internally meaningful digest-algorithm ID. */
2106 QCdigest_algorithm_id,
2107 make_number (gda),
2108 QCtype,
2109 Qgnutls_type_digest_algorithm,
2110 /* The digest length. */
2111 QCdigest_algorithm_length,
2112 make_number (gnutls_hash_get_len (gda)));
2113
2114 digest_algorithms = Fcons (mp, digest_algorithms);
2115 }
2116
2117 return digest_algorithms;
2118}
2119
2120DEFUN ("gnutls-hash-mac", Fgnutls_hash_mac, Sgnutls_hash_mac, 3, 3, 0,
2121 doc: /* Hash INPUT with HASH-METHOD and KEY into a unibyte string.
2122
2123Returns nil on error.
2124
2125The KEY can be specified as a buffer or string or in other ways
2126(see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). The KEY will be
2127wiped after use if it's a string.
2128
2129The INPUT can be specified as a buffer or string or in other
2130ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs').
2131
2132The alist of MAC algorithms can be obtained with `gnutls-macs`. The
2133HASH-METHOD may be a string or symbol matching a key in that alist, or
2134a plist with the `:mac-algorithm-id' numeric property, or the number
2135itself. */)
2136 (Lisp_Object hash_method, Lisp_Object key, Lisp_Object input)
2137{
2138 if (BUFFERP (input) || STRINGP (input))
2139 input = list1 (input);
2140
2141 CHECK_CONS (input);
2142
2143 if (BUFFERP (key) || STRINGP (key))
2144 key = list1 (key);
2145
2146 CHECK_CONS (key);
2147
2148 int ret = GNUTLS_E_SUCCESS;
2149
2150 gnutls_mac_algorithm_t gma = GNUTLS_MAC_UNKNOWN;
2151
2152 Lisp_Object info = Qnil;
2153 if (STRINGP (hash_method))
2154 hash_method = intern (SSDATA (hash_method));
2155
2156 if (SYMBOLP (hash_method))
2157 info = XCDR (Fassq (hash_method, Fgnutls_macs ()));
2158 else if (INTEGERP (hash_method))
2159 gma = XINT (hash_method);
2160 else
2161 info = hash_method;
2162
2163 if (!NILP (info) && CONSP (info))
2164 {
2165 Lisp_Object v = Fplist_get (info, QCmac_algorithm_id);
2166 if (INTEGERP (v))
2167 gma = XINT (v);
2168 }
2169
2170 if (gma == GNUTLS_MAC_UNKNOWN)
2171 error ("GnuTLS MAC-method was invalid or not found");
2172
2173 ptrdiff_t kstart_byte, kend_byte;
2174 const char* kdata = extract_data_from_object (key, &kstart_byte, &kend_byte);
2175 gnutls_hmac_hd_t hmac;
2176 ret = gnutls_hmac_init (&hmac, gma,
2177 kdata + kstart_byte, kend_byte - kstart_byte);
2178
2179 if (kdata == NULL)
2180 error ("GnuTLS MAC key extraction failed");
2181
2182 if (ret < GNUTLS_E_SUCCESS)
2183 {
2184 const char* str = gnutls_strerror (ret);
2185 if (!str)
2186 str = "unknown";
2187 error ("GnuTLS MAC %s initialization failed: %s",
2188 gnutls_mac_get_name (gma), str);
2189 }
2190
2191 ptrdiff_t istart_byte, iend_byte;
2192 const char* idata = extract_data_from_object (input, &istart_byte, &iend_byte);
2193 if (idata == NULL)
2194 error ("GnuTLS MAC input extraction failed");
2195
2196 size_t digest_length = gnutls_hmac_get_len (gma);
2197 Lisp_Object digest = make_uninit_string (digest_length);
2198
2199 ret = gnutls_hmac (hmac, idata + istart_byte, iend_byte - istart_byte);
2200
2201 if (STRINGP (XCAR (key)))
2202 Fclear_string (XCAR (key));
2203
2204 if (ret < GNUTLS_E_SUCCESS)
2205 {
2206 gnutls_hmac_deinit (hmac, NULL);
2207
2208 const char* str = gnutls_strerror (ret);
2209 if (!str)
2210 str = "unknown";
2211 error ("GnuTLS MAC %s application failed: %s",
2212 gnutls_mac_get_name (gma), str);
2213 }
2214
2215 gnutls_hmac_output (hmac, SSDATA (digest));
2216 gnutls_hmac_deinit (hmac, NULL);
2217
2218 return digest;
2219}
2220
2221DEFUN ("gnutls-hash-digest", Fgnutls_hash_digest, Sgnutls_hash_digest, 2, 2, 0,
2222 doc: /* Digest INPUT with DIGEST-METHOD into a unibyte string.
2223
2224Returns nil on error.
2225
2226The INPUT can be specified as a buffer or string or in other
2227ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs').
2228
2229The alist of digest algorithms can be obtained with `gnutls-digests`.
2230The DIGEST-METHOD may be a string or symbol matching a key in that
2231alist, or a plist with the `:digest-algorithm-id' numeric property, or
2232the number itself. */)
2233 (Lisp_Object digest_method, Lisp_Object input)
2234{
2235 if (BUFFERP (input) || STRINGP (input))
2236 input = list1 (input);
2237
2238 CHECK_CONS (input);
2239
2240 int ret = GNUTLS_E_SUCCESS;
2241
2242 gnutls_digest_algorithm_t gda = GNUTLS_DIG_UNKNOWN;
2243
2244 Lisp_Object info = Qnil;
2245 if (STRINGP (digest_method))
2246 digest_method = intern (SSDATA (digest_method));
2247
2248 if (SYMBOLP (digest_method))
2249 info = XCDR (Fassq (digest_method, Fgnutls_digests ()));
2250 else if (INTEGERP (digest_method))
2251 gda = XINT (digest_method);
2252 else
2253 info = digest_method;
2254
2255 if (!NILP (info) && CONSP (info))
2256 {
2257 Lisp_Object v = Fplist_get (info, QCdigest_algorithm_id);
2258 if (INTEGERP (v))
2259 gda = XINT (v);
2260 }
2261
2262 if (gda == GNUTLS_DIG_UNKNOWN)
2263 error ("GnuTLS digest-method was invalid or not found");
2264
2265 gnutls_hash_hd_t hash;
2266 ret = gnutls_hash_init (&hash, gda);
2267
2268 if (ret < GNUTLS_E_SUCCESS)
2269 {
2270 const char* str = gnutls_strerror (ret);
2271 if (!str)
2272 str = "unknown";
2273 error ("GnuTLS digest initialization failed: %s", str);
2274 }
2275
2276 size_t digest_length = gnutls_hash_get_len (gda);
2277 Lisp_Object digest = make_uninit_string (digest_length);
2278
2279 ptrdiff_t istart_byte, iend_byte;
2280 const char* idata = extract_data_from_object (input, &istart_byte, &iend_byte);
2281 if (idata == NULL)
2282 error ("GnuTLS digest input extraction failed");
2283
2284 ret = gnutls_hash (hash, idata + istart_byte, iend_byte - istart_byte);
2285
2286 if (ret < GNUTLS_E_SUCCESS)
2287 {
2288 gnutls_hash_deinit (hash, NULL);
2289
2290 const char* str = gnutls_strerror (ret);
2291 if (!str)
2292 str = "unknown";
2293 error ("GnuTLS digest application failed: %s", str);
2294 }
2295
2296 gnutls_hash_output (hash, SSDATA (digest));
2297 gnutls_hash_deinit (hash, NULL);
2298
2299 return digest;
2300}
2301
2302#endif
2303
1700DEFUN ("gnutls-available-p", Fgnutls_available_p, Sgnutls_available_p, 0, 0, 0, 2304DEFUN ("gnutls-available-p", Fgnutls_available_p, Sgnutls_available_p, 0, 0, 0,
1701 doc: /* Return t if GnuTLS is available in this instance of Emacs. */) 2305 doc: /* Return list of capabilities if GnuTLS is available in this instance of Emacs.
2306
2307...if supported : then...
2308GnuTLS 3 or higher : the list will contain 'gnutls3.
2309GnuTLS MACs : the list will contain 'macs.
2310GnuTLS digests : the list will contain 'digests.
2311GnuTLS symmetric ciphers: the list will contain 'ciphers.
2312GnuTLS AEAD ciphers : the list will contain 'AEAD-ciphers. */)
1702 (void) 2313 (void)
1703{ 2314{
1704#ifdef HAVE_GNUTLS 2315#ifdef HAVE_GNUTLS
2316 Lisp_Object capabilities = Qnil;
2317
2318#ifdef HAVE_GNUTLS3
2319
2320 capabilities = Fcons (intern("gnutls3"), capabilities);
2321
2322#ifdef HAVE_GNUTLS3_DIGEST
2323 capabilities = Fcons (intern("digests"), capabilities);
2324#endif
2325
2326#ifdef HAVE_GNUTLS3_CIPHER
2327 capabilities = Fcons (intern("ciphers"), capabilities);
2328
2329#ifdef HAVE_GNUTLS3_AEAD
2330 capabilities = Fcons (intern("AEAD-ciphers"), capabilities);
2331#endif
2332
2333#ifdef HAVE_GNUTLS3_HMAC
2334 capabilities = Fcons (intern("macs"), capabilities);
2335#endif
2336
2337#endif
2338
2339#endif
2340
1705# ifdef WINDOWSNT 2341# ifdef WINDOWSNT
1706 Lisp_Object found = Fassq (Qgnutls, Vlibrary_cache); 2342 Lisp_Object found = Fassq (Qgnutls, Vlibrary_cache);
1707 if (CONSP (found)) 2343 if (CONSP (found))
1708 return XCDR (found); 2344 return XCDR (found); // TODO: use capabilities.
1709 else 2345 else
1710 { 2346 {
1711 Lisp_Object status; 2347 Lisp_Object status;
1712 status = init_gnutls_functions () ? Qt : Qnil; 2348 // TODO: should the capabilities be dynamic here?
2349 status = init_gnutls_functions () ? capabilities : Qnil;
1713 Vlibrary_cache = Fcons (Fcons (Qgnutls, status), Vlibrary_cache); 2350 Vlibrary_cache = Fcons (Fcons (Qgnutls, status), Vlibrary_cache);
1714 return status; 2351 return status;
1715 } 2352 }
1716# else /* !WINDOWSNT */ 2353# else /* !WINDOWSNT */
1717 return Qt; 2354 return capabilities;
1718# endif /* !WINDOWSNT */ 2355# endif /* !WINDOWSNT */
1719#else /* !HAVE_GNUTLS */ 2356#else /* !HAVE_GNUTLS */
1720 return Qnil; 2357 return Qnil;
@@ -1753,6 +2390,27 @@ syms_of_gnutls (void)
1753 DEFSYM (QCverify_flags, ":verify-flags"); 2390 DEFSYM (QCverify_flags, ":verify-flags");
1754 DEFSYM (QCverify_error, ":verify-error"); 2391 DEFSYM (QCverify_error, ":verify-error");
1755 2392
2393 DEFSYM (QCcipher_id, ":cipher-id");
2394 DEFSYM (QCcipher_aead_capable, ":cipher-aead-capable");
2395 DEFSYM (QCcipher_blocksize, ":cipher-blocksize");
2396 DEFSYM (QCcipher_keysize, ":cipher-keysize");
2397 DEFSYM (QCcipher_tagsize, ":cipher-tagsize");
2398 DEFSYM (QCcipher_keysize, ":cipher-keysize");
2399 DEFSYM (QCcipher_ivsize, ":cipher-ivsize");
2400
2401 DEFSYM (QCmac_algorithm_id, ":mac-algorithm-id");
2402 DEFSYM (QCmac_algorithm_noncesize, ":mac-algorithm-noncesize");
2403 DEFSYM (QCmac_algorithm_keysize, ":mac-algorithm-keysize");
2404 DEFSYM (QCmac_algorithm_length, ":mac-algorithm-length");
2405
2406 DEFSYM (QCdigest_algorithm_id, ":digest-algorithm-id");
2407 DEFSYM (QCdigest_algorithm_length, ":digest-algorithm-length");
2408
2409 DEFSYM (QCtype, ":type");
2410 DEFSYM (Qgnutls_type_cipher, "gnutls-symmetric-cipher");
2411 DEFSYM (Qgnutls_type_mac_algorithm, "gnutls-mac-algorithm");
2412 DEFSYM (Qgnutls_type_digest_algorithm, "gnutls-digest-algorithm");
2413
1756 DEFSYM (Qgnutls_e_interrupted, "gnutls-e-interrupted"); 2414 DEFSYM (Qgnutls_e_interrupted, "gnutls-e-interrupted");
1757 Fput (Qgnutls_e_interrupted, Qgnutls_code, 2415 Fput (Qgnutls_e_interrupted, Qgnutls_code,
1758 make_number (GNUTLS_E_INTERRUPTED)); 2416 make_number (GNUTLS_E_INTERRUPTED));
@@ -1780,6 +2438,14 @@ syms_of_gnutls (void)
1780 defsubr (&Sgnutls_peer_status); 2438 defsubr (&Sgnutls_peer_status);
1781 defsubr (&Sgnutls_peer_status_warning_describe); 2439 defsubr (&Sgnutls_peer_status_warning_describe);
1782 2440
2441 defsubr (&Sgnutls_ciphers);
2442 defsubr (&Sgnutls_macs);
2443 defsubr (&Sgnutls_digests);
2444 defsubr (&Sgnutls_hash_mac);
2445 defsubr (&Sgnutls_hash_digest);
2446 defsubr (&Sgnutls_symmetric_encrypt);
2447 defsubr (&Sgnutls_symmetric_decrypt);
2448
1783 DEFVAR_INT ("gnutls-log-level", global_gnutls_log_level, 2449 DEFVAR_INT ("gnutls-log-level", global_gnutls_log_level,
1784 doc: /* Logging level used by the GnuTLS functions. 2450 doc: /* Logging level used by the GnuTLS functions.
1785Set this larger than 0 to get debug output in the *Messages* buffer. 2451Set this larger than 0 to get debug output in the *Messages* buffer.