;; 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)