* smtp.el (smtp-authenticate-type): Rename from `smtp-authenticate-type'.
[elisp/flim.git] / digest-md5.el
index 31f2c6f..cb4d697 100644 (file)
@@ -3,6 +3,7 @@
 ;; Copyright (C) 1999 Kenichi OKADA
 
 ;; Author: Kenichi OKADA <okada@opaopa.org>
+;;     Daiki Ueno <ueno@ueda.info.waseda.ac.jp>
 ;; Keywords: DIGEST-MD5, HMAC-MD5, SASL, IMAP, POP, ACAP
 
 ;; This file is part of FLIM (Faithful Library about Internet Message).
 
 ;; Examples.
 ;;
-;; (digest-md5-make-response "chris" "elwood.innosoft.com"
-;;                       "OA6MG9tEQGm2hh" "OA6MHXh6VqTrRk" 1
-;;                       "auth" "imap/elwood.innosoft.com"
-;;                       "d388dad90d4bbd760a152321f2143af7" nil "utf-8")
-;; => "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth"
-;;
+;; (digest-md5-parse-digest-challenge 
+;;   "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8")
+;; => (realm "elwood.innosoft.com" nonce "OA6MG9tEQGm2hh" qop "auth" algorithm md5-sess charset utf-8)
+
+;; (digest-md5-build-response-value
+;;   "chris" "secret" "OA6MHXh6VqTrRk" "imap/elwood.innosoft.com")
+;; => "d388dad90d4bbd760a152321f2143af7"
 
 ;;; Code:
 
 (require 'hmac-md5)
+(require 'unique-id)
+
+(defvar digest-md5-challenge nil)
+(defvar digest-md5-nonce-count 1)
+
+(defvar digest-md5-parse-digest-challenge-syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?= "." table)
+    (modify-syntax-entry ?, "." table)
+    table)
+  "A syntax table for parsing digest-challenge attributes.")
+
+;;;###autoload
+(defun digest-md5-parse-digest-challenge (digest-challenge)
+  ;; return a property list of 
+  ;; (realm nonce qop-options stale maxbuf charset 
+  ;; algorithm cipher-opts auth-param).
+  (with-temp-buffer
+    (set-syntax-table digest-md5-parse-digest-challenge-syntax-table)
+    (insert digest-challenge)
+    (goto-char (point-min))
+    (insert "(")
+    (while (progn (forward-sexp) (not (eobp)))
+      (delete-char 1)
+      (insert " "))
+    (insert ")")
+    (condition-case nil
+       (setplist 'digest-md5-challenge (read (point-min-marker)))
+      (end-of-file
+       (error "Parse error in digest-challenge.")))))
 
 (defun digest-md5-digest-uri (serv-type host &optional serv-name)
   (concat serv-type "/" host
                   (null (string= host serv-name)))
              (concat "/" serv-name))))
 
-(defun digest-md5-digest-response (username 
-                                  realm nonce cnonce
-                                  nonce-count qop digest-uri response 
-                                  &optional maxbuf charset cipher authzid)
-  (concat
-   (if charset
-       (concat "charset=" charset ","))
-   "username=\"" username "\""
-   ",realm=\"" realm "\""
-   ",nonce=\"" nonce "\""
-   (format ",nc=%08x" nonce-count)
-   ",cnonce=\"" cnonce "\""
-   ",digest-uri=\"" digest-uri "\""
-   ",response=" response
-   ",qop=" qop
-   (if maxbuf
-       (concat ",maxbuf=" maxbuf))
-   (if cipher
-       (concat ",cipher=" cipher))
-   (if authzid
-       (concat ",authzid=\"" authzid "\""))))
+(defmacro digest-md5-cnonce ()
+  ;; It is RECOMMENDED that it 
+  ;; contain at least 64 bits of entropy.
+  '(concat (unique-id-m "") (unique-id-m "")))
+
+(defmacro digest-md5-challenge (prop)
+  (list 'get ''digest-md5-challenge prop))
+
+(defmacro digest-md5-build-response-value 
+  (username passwd cnonce digest-uri qop)
+  `(encode-hex-string
+    (md5-binary
+     (concat
+      (encode-hex-string
+       (md5-binary (concat (md5-binary 
+                           (concat ,username 
+                                   ":" (digest-md5-challenge 'realm)
+                                   ":" ,passwd))
+                          ":" (digest-md5-challenge 'nonce)
+                          ":" ,cnonce
+                          (let ((authzid (digest-md5-challenge 'authzid)))
+                            (if authzid (concat ":" authzid) nil)))))
+      ":" (digest-md5-challenge 'nonce)
+      ":" (format "%08x" digest-md5-nonce-count) ":" ,cnonce ":" ,qop ":"
+      (encode-hex-string
+       (md5-binary
+       (concat "AUTHENTICATE:" ,digest-uri
+               (if (string-equal "auth-int" ,qop)
+                   ":00000000000000000000000000000000"
+                 nil))))))))
 
+;;;###autoload
+(defun digest-md5-digest-response (username passwd digest-uri &optional qop)
+  (let ((cnonce (digest-md5-cnonce)))
+    (concat
+     "username=\"" username "\","
+     "realm=\"" (digest-md5-challenge 'realm) "\","
+     "nonce=\"" (digest-md5-challenge 'nonce) "\","
+     (format "nc=%08x," digest-md5-nonce-count)
+     "cnonce=\"" cnonce "\","
+     "digest-uri=\"" digest-uri "\","
+     "response=" 
+     (digest-md5-build-response-value username passwd cnonce digest-uri 
+                                     (or qop "auth"))
+     ","
+     (mapconcat 
+      #'identity
+      (delq nil 
+           (mapcar (lambda (prop)
+                     (if (digest-md5-challenge prop)
+                         (format "%s=%s"
+                                 prop (digest-md5-challenge prop))))
+                   '(charset qop maxbuf cipher authzid)))
+      ","))))
   
 (provide 'digest-md5)