* sasl.el (sasl-number-base36): New function.
[elisp/flim.git] / scram-md5.el
1 ;;; scram-md5.el --- Compute SCRAM-MD5.
2
3 ;; Copyright (C) 1999 Shuhei KOBAYASHI
4
5 ;; Author: Shuhei KOBAYASHI <shuhei@aqua.ocn.ne.jp>
6 ;; Keywords: SCRAM-MD5, HMAC-MD5, SASL, IMAP, POP, ACAP
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
13 ;; (at your option) any later version.
14
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU 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
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;; This program is implemented from draft-newman-auth-scram-03.txt.
28 ;;
29 ;; It is caller's responsibility to base64-decode challenges and
30 ;; base64-encode responses in IMAP4 AUTHENTICATE command.
31 ;;
32 ;; Passphrase should be longer than 16 bytes. (See RFC 2195)
33 ;;
34 ;; TODO: Provide higher-level (SASL) APIs.
35
36 ;;; Code:
37
38 (require 'hmac-md5)                     ; (hmac-md5 TEXT KEY)
39
40 (defmacro scram-security-info-no-security-layer (security-info)
41   `(eq (logand (aref ,security-info 0) 1) 1))
42 (defmacro scram-security-info-integrity-protection-layer (security-info)
43   `(eq (logand (aref ,security-info 0) 2) 2))
44 (defmacro scram-security-info-buffer-size (security-info)
45   `(let ((ssecinfo ,security-info))
46      (+ (lsh (aref ssecinfo 1) 16)
47         (lsh (aref ssecinfo 2) 8)
48         (aref ssecinfo 3))))
49
50 (defun scram-make-security-info (integrity-protection-layer
51                                  no-security-layer buffer-size)
52   (let ((csecinfo (make-string 4 0)))
53     (when integrity-protection-layer
54       (aset csecinfo 0 2))
55     (if no-security-layer
56         (aset csecinfo 0 (logior (aref csecinfo 0) 1))
57       (aset csecinfo 1
58             (lsh (logand buffer-size (lsh 255 16)) -16))
59       (aset csecinfo 2
60             (lsh (logand buffer-size (lsh 255 8)) -8))
61       (aset csecinfo 3 (logand buffer-size 255)))
62     csecinfo))
63
64 (defun scram-make-unique-nonce ()       ; 8*OCTET, globally unique.
65   ;; For example, concatenated string of process-identifier, system-clock,
66   ;; sequence-number, random-number, and domain-name.
67   (concat "<" (sasl-unique-id) "@" (system-name) ">"))
68   
69 (defun scram-xor-string (str1 str2)
70   ;; (length str1) == (length str2) == (length dst) == 16 (in SCRAM-MD5)
71   (let* ((len (length str1))
72          (dst (make-string len 0))
73          (pos 0))
74     (while (< pos len)
75       (aset dst pos (logxor (aref str1 pos) (aref str2 pos)))
76       (setq pos (1+ pos)))
77     dst))
78
79 (defun scram-md5-make-client-msg-1 (authenticate-id &optional authorize-id)
80   "Make an initial client message from AUTHENTICATE-ID and AUTHORIZE-ID.
81 If AUTHORIZE-ID is the same as AUTHENTICATE-ID, it may be omitted."
82   (concat authorize-id "\0" authenticate-id "\0" (scram-make-unique-nonce)))
83
84 (defun scram-md5-parse-server-msg-1 (server-msg-1)
85   "Parse SERVER-MSG-1 and return a list of (SALT SECURITY-INFO SERVICE-ID)."
86   (when (and (> (length server-msg-1) 16)
87              (eq (string-match "[^@]+@[^\0]+\0" server-msg-1 12) 12))
88     (list (substring server-msg-1 0 8)  ; salt
89           (substring server-msg-1 8 12) ; server-security-info
90           (substring server-msg-1       ; service-id
91                      12 (1- (match-end 0))))))
92
93 (defun scram-md5-make-salted-pass (passphrase salt)
94   (unwind-protect
95       (hmac-md5 salt passphrase)
96     ;; immediately erase plaintext passphrase from memory.
97     (fillarray passphrase 0)))
98
99 (defun scram-md5-make-client-key (salted-pass)
100   (md5-binary salted-pass))
101
102 (defun scram-md5-make-client-verifier (client-key)
103   (md5-binary client-key))
104
105 (defun scram-md5-make-shared-key (server-msg-1
106                                   client-msg-1
107                                   client-security-info
108                                   client-verifier)
109   (hmac-md5 (concat server-msg-1 client-msg-1 client-security-info)
110             client-verifier))
111
112 (defun scram-md5-make-client-proof (client-key shared-key)
113   (scram-xor-string client-key shared-key))
114
115 (defun scram-md5-make-client-msg-2 (client-security-info client-proof)
116   (concat client-security-info client-proof))
117
118 (defun scram-md5-authenticate-server (server-msg-1
119                                       server-msg-2
120                                       client-msg-1
121                                       client-security-info
122                                       salt salted-pass)
123   (string= (hmac-md5 (concat client-msg-1 server-msg-1 client-security-info)
124                      (hmac-md5 salt salted-pass))
125            server-msg-2))
126
127 (provide 'scram-md5)
128
129 ;;; scram-md5.el ends here