* sasl.el (sasl-scram-md5-client-msg-1): New function.
[elisp/flim.git] / sasl.el
1 ;;; sasl.el --- basic functions for SASL
2
3 ;; Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc.
4
5 ;; Author: Kenichi OKADA <okada@opaopa.org>
6 ;; Keywords: SMTP, SASL, RFC2222
7
8 ;; This file is part of FLIM (Faithful Library about Internet Message).
9
10 ;; This program is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
14
15 ;; This program is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Code:
26
27 (require 'hmac-md5)
28
29 ;;; CRAM-MD5
30 (defun sasl-cram-md5 (username passphrase challenge)
31   (let ((secure-word (copy-sequence passphrase)))
32     (setq secure-word (unwind-protect
33                           (hmac-md5 challenge secure-word)
34                         (fillarray secure-word 0))
35           secure-word (unwind-protect
36                           (encode-hex-string secure-word)
37                         (fillarray secure-word 0))
38           secure-word (unwind-protect
39                           (concat username " " secure-word)
40                         (fillarray secure-word 0)))))
41
42 ;;; PLAIN
43 (defun sasl-plain (authorid authenid passphrase)
44   (concat authorid "\0" authenid "\0" passphrase))
45
46 ;;; SCRAM-MD5
47 (defvar sasl-scram-md5-client-security-info
48   (scram-make-security-info nil t 0))
49
50 (defun sasl-scram-md5-client-msg-1 (authenticate-id &optional authorize-id)
51   (scram-md5-make-client-msg-1 authenticate-id authorize-id))
52
53 (defun sasl-scram-md5-client-msg-2 (server-msg-1 client-msg-1 passphrase)
54   (let (client-key)
55     (scram-md5-make-client-msg-2
56      sasl-scram-md5-client-security-info
57      (scram-md5-make-client-proof
58       (setq client-key
59             (scram-md5-make-client-key
60              (scram-md5-make-salted-pass
61               passphrase
62               (car ; salt
63                (scram-md5-parse-server-msg-1 server-msg-1)))))
64       (scram-md5-make-shared-key
65        server-msg-1
66        client-msg-1
67        sasl-scram-md5-client-security-info
68        (scram-md5-make-client-verifier client-key))))))
69
70 (defun sasl-scram-md5-authenticate-server (server-msg-1
71                                            server-msg-2
72                                            client-msg-1
73                                            passphrase)
74   (scram-md5-authenticate-server
75    server-msg-1
76    server-msg-2
77    client-msg-1
78    sasl-scram-md5-client-security-info
79    (car ; salt
80     (scram-md5-parse-server-msg-1 server-msg-1))
81    (scram-md5-make-salted-pass
82     passphrase
83     (car ; salt
84      (scram-md5-parse-server-msg-1 server-msg-1)))))
85
86 ;;; unique-ID
87 (defun sasl-number-base36 (num len)
88   (if (if (< len 0)
89           (<= num 0)
90         (= len 0))
91       ""
92     (concat (sasl-number-base36 (/ num 36) (1- len))
93             (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
94                                   (% num 36))))))
95
96 (defvar sasl-unique-id-char nil)
97
98 (defun sasl-unique-id ()
99   ;; Don't use microseconds from (current-time), they may be unsupported.
100   ;; Instead we use this randomly inited counter.
101   (setq sasl-unique-id-char
102         (% (1+ (or sasl-unique-id-char (logand (random t) (1- (lsh 1 20)))))
103            ;; (current-time) returns 16-bit ints,
104            ;; and 2^16*25 just fits into 4 digits i base 36.
105            (* 25 25)))
106   (let ((tm (static-if (fboundp 'current-time)
107                 (current-time)
108               (let* ((cts (split-string (current-time-string) "[ :]"))
109                      (m (cdr (assoc (nth 1 cts)
110                                     '(("Jan" . "01") ("Feb" . "02")
111                                       ("Mar" . "03") ("Apr" . "04")
112                                       ("May" . "05") ("Jun" . "06")
113                                       ("Jul" . "07") ("Aug" . "08")
114                                       ("Sep" . "09") ("Oct" . "10")
115                                       ("Nov" . "11") ("Dec" . "12"))))))
116                 (list (string-to-int (concat (nth 6 cts) m
117                                              (substring (nth 2 cts) 0 1)))
118                       (string-to-int (concat (substring (nth 2 cts) 1)
119                                              (nth 4 cts) (nth 5 cts)
120                                              (nth 6 cts))))))))
121     (concat
122      (if (memq system-type '(ms-dos emx vax-vms))
123          (let ((user (downcase (user-login-name))))
124            (while (string-match "[^a-z0-9_]" user)
125              (aset user (match-beginning 0) ?_))
126            user)
127        (sasl-number-base36 (user-uid) -1))
128      (sasl-number-base36 (+ (car   tm)
129                           (lsh (% sasl-unique-id-char 25) 16)) 4)
130      (sasl-number-base36 (+ (nth 1 tm)
131                           (lsh (/ sasl-unique-id-char 25) 16)) 4)
132      ;; Append the name of the message interface, because while the
133      ;; generated ID is unique to this newsreader, other newsreaders
134      ;; might otherwise generate the same ID via another algorithm.
135      ".sasl")))
136
137 (provide 'sasl)
138
139 ;;; sasl.el ends here