update.
[elisp/flim.git] / digest-md5.el
1 ;;; digest-md5.el --- Compute DIGEST-MD5.
2
3 ;; Copyright (C) 1999 Kenichi OKADA
4
5 ;; Author: Kenichi OKADA <okada@opaopa.org>
6 ;;      Daiki Ueno <ueno@ueda.info.waseda.ac.jp>
7 ;; Keywords: DIGEST-MD5, HMAC-MD5, SASL, IMAP, POP, ACAP
8
9 ;; This file is part of FLIM (Faithful Library about Internet Message).
10
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or
14 ;; (at your option) any later version.
15
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with this program; see the file COPYING.  If not, write to
23 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Commentary:
27
28 ;; This program is implemented from draft-leach-digest-sasl-05.txt.
29 ;;
30 ;; It is caller's responsibility to base64-decode challenges and
31 ;; base64-encode responses in IMAP4 AUTHENTICATE command.
32 ;;
33 ;; Passphrase should be longer than 16 bytes. (See RFC 2195)
34
35 ;; Examples.
36 ;;
37 ;; (digest-md5-parse-digest-challenge 
38 ;;   "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8")
39 ;; => (realm "elwood.innosoft.com" nonce "OA6MG9tEQGm2hh" qop "auth" algorithm md5-sess charset utf-8)
40
41 ;; (digest-md5-build-response-value
42 ;;   "chris" "elwood.innosoft.com" "secret" "OA6MG9tEQGm2hh"
43 ;;   "OA6MHXh6VqTrRk" 1 "imap/elwood.innosoft.com" "auth")
44 ;; => "d388dad90d4bbd760a152321f2143af7"
45
46 ;;; Code:
47
48 (require 'hmac-md5)
49 (require 'unique-id)
50
51 (defvar digest-md5-challenge nil)
52
53 (defvar digest-md5-parse-digest-challenge-syntax-table
54   (let ((table (make-syntax-table)))
55     (modify-syntax-entry ?= "." table)
56     (modify-syntax-entry ?, "." table)
57     table)
58   "A syntax table for parsing digest-challenge attributes.")
59
60 ;;;###autoload
61 (defun digest-md5-parse-digest-challenge (digest-challenge)
62   ;; return a property list of 
63   ;; (realm nonce qop-options stale maxbuf charset 
64   ;; algorithm cipher-opts auth-param).
65   (with-temp-buffer
66     (set-syntax-table digest-md5-parse-digest-challenge-syntax-table)
67     (insert digest-challenge)
68     (goto-char (point-min))
69     (insert "(")
70     (while (progn (forward-sexp) (not (eobp)))
71       (delete-char 1)
72       (insert " "))
73     (insert ")")
74     (condition-case nil
75         (setplist 'digest-md5-challenge (read (point-min-marker)))
76       (end-of-file
77        (error "Parse error in digest-challenge.")))))
78
79 (defun digest-md5-digest-uri (serv-type host &optional serv-name)
80   (concat serv-type "/" host
81           (if (and serv-name
82                    (null (string= host serv-name)))
83               (concat "/" serv-name))))
84
85 (defmacro digest-md5-cnonce ()
86   ;; It is RECOMMENDED that it 
87   ;; contain at least 64 bits of entropy.
88   '(concat (unique-id-m "") (unique-id-m "")))
89
90 (defmacro digest-md5-challenge (prop)
91   (list 'get ''digest-md5-challenge prop))
92
93 (defmacro digest-md5-build-response-value
94   (username realm passwd nonce cnonce nonce-count digest-uri qop)
95   `(encode-hex-string
96     (md5-binary
97      (concat
98       (encode-hex-string
99        (md5-binary (concat (md5-binary 
100                             (concat ,username 
101                                     ":" ,realm
102                                     ":" ,passwd))
103                            ":" ,nonce
104                            ":" ,cnonce
105                            (let ((authzid (digest-md5-challenge 'authzid)))
106                              (if authzid (concat ":" authzid) nil)))))
107       ":" ,nonce
108       ":" (format "%08x" ,nonce-count) ":" ,cnonce ":" ,qop ":"
109       (encode-hex-string
110        (md5-binary
111         (concat "AUTHENTICATE:" ,digest-uri
112                 (if (string-equal "auth-int" ,qop)
113                     ":00000000000000000000000000000000"
114                   nil))))))))
115
116 ;;;###autoload
117 (defun digest-md5-digest-response
118   (username realm passwd nonce cnonce nonce-count digest-uri
119             &optional charset qop maxbuf cipher authzid)
120   (concat
121    "username=\"" username "\","
122    "realm=\"" realm "\","
123    "nonce=\"" nonce "\","
124    (format "nc=%08x," nonce-count)
125    "cnonce=\"" cnonce "\","
126    "digest-uri=\"" digest-uri "\","
127    "response=" 
128    (digest-md5-build-response-value
129     username realm passwd nonce cnonce nonce-count digest-uri
130     (or qop "auth"))
131    ","
132    (mapconcat 
133     #'identity
134     (delq nil 
135           (mapcar (lambda (prop)
136                     (if (digest-md5-challenge prop)
137                         (format "%s=%s"
138                                 prop (digest-md5-challenge prop))))
139                   '(charset qop maxbuf cipher authzid)))
140     ",")))
141   
142 (provide 'digest-md5)
143
144 ;;; digest-md5.el ends here