domain ;buffer field
))))
+;; Poor man's bignums: natural numbers represented as lists of bytes
+;; in little-endian order.
+;; When this code no longer needs to run on Emacs 26 or older, all this
+;; silliness should be simplified to use ordinary Lisp integers.
+
+(eval-and-compile ; for compile-time simplification
+ (defun ntlm--bignat-of-int (x)
+ "Convert the natural number X into a bignat."
+ (declare (pure t))
+ (and (not (zerop x))
+ (cons (logand x #xff) (ntlm--bignat-of-int (ash x -8)))))
+
+ (defun ntlm--bignat-add (a b &optional carry)
+ "Add the bignats A and B and the natural number CARRY."
+ (declare (pure t))
+ (and (or a b (and carry (not (zerop carry))))
+ (let ((s (+ (if a (car a) 0)
+ (if b (car b) 0)
+ (or carry 0))))
+ (cons (logand s #xff)
+ (ntlm--bignat-add (cdr a) (cdr b) (ash s -8))))))
+
+ (defun ntlm--bignat-shift-left (x n)
+ "Multiply the bignat X by 2^{8N}."
+ (declare (pure t))
+ (if (zerop n) x (ntlm--bignat-shift-left (cons 0 x) (1- n))))
+
+ (defun ntlm--bignat-mul-byte (a b)
+ "Multiply the bignat A with the byte B."
+ (declare (pure t))
+ (let ((p (mapcar (lambda (x) (* x b)) a)))
+ (ntlm--bignat-add
+ (mapcar (lambda (x) (logand x #xff)) p)
+ (cons 0 (mapcar (lambda (x) (ash x -8)) p)))))
+
+ (defun ntlm--bignat-mul (a b)
+ "Multiply the bignats A and B."
+ (declare (pure t))
+ (and a b (ntlm--bignat-add (ntlm--bignat-mul-byte a (car b))
+ (cons 0 (ntlm--bignat-mul a (cdr b))))))
+
+ (defun ntlm--bignat-of-string (s)
+ "Convert the string S (in decimal) to a bignat."
+ (declare (pure t))
+ (ntlm--bignat-of-digits (reverse (string-to-list s))))
+
+ (defun ntlm--bignat-of-digits (digits)
+ "Convert the little-endian list DIGITS of decimal digits to a bignat."
+ (declare (pure t))
+ (and digits
+ (ntlm--bignat-add
+ nil
+ (ntlm--bignat-mul-byte (ntlm--bignat-of-digits (cdr digits)) 10)
+ (- (car digits) ?0))))
+
+ (defun ntlm--bignat-to-int64 (x)
+ "Convert the bignat X to a 64-bit little-endian number as a string."
+ (declare (pure t))
+ (apply #'unibyte-string (mapcar (lambda (n) (or (nth n x) 0))
+ (number-sequence 0 7))))
+ )
+
(defun ntlm--time-to-timestamp (time)
"Convert TIME to an NTLMv2 timestamp.
Return a unibyte string representing the number of tenths of a
microsecond since January 1, 1601 as a 64-bit little-endian
signed integer. TIME must be on the form (HIGH LOW USEC PSEC)."
- (let* ((s (+ (ash (nth 0 time) 16) (nth 1 time)))
- (us (nth 2 time))
- (ps (nth 3 time))
+ (let* ((s-hi (ntlm--bignat-of-int (nth 0 time)))
+ (s-lo (ntlm--bignat-of-int (nth 1 time)))
+ (s (ntlm--bignat-add (ntlm--bignat-shift-left s-hi 2) s-lo))
+ (us*10 (ntlm--bignat-of-int (* (nth 2 time) 10)))
+ (ps/1e5 (ntlm--bignat-of-int (/ (nth 3 time) 100000)))
+ ;; tenths of microseconds between 1601-01-01 and 1970-01-01
+ (to-unix-epoch (ntlm--bignat-of-string "116444736000000000"))
(tenths-of-us-since-jan-1-1601
- (+ (* s 10000000) (* us 10) (/ ps 100000)
- ;; tenths of microseconds between 1601-01-01 and 1970-01-01
- 116444736000000000)))
- (apply #'unibyte-string
- (mapcar (lambda (i)
- (logand (ash tenths-of-us-since-jan-1-1601 (* i -8))
- #xff))
- (number-sequence 0 7)))))
+ (ntlm--bignat-add
+ (ntlm--bignat-add
+ (ntlm--bignat-add
+ (ntlm--bignat-mul s (ntlm--bignat-of-int 10000000))
+ us*10)
+ ps/1e5)
+ to-unix-epoch)))
+ (ntlm--bignat-to-int64 tenths-of-us-since-jan-1-1601)))
(defun ntlm-compute-timestamp ()
"Current time as an NTLMv2 timestamp, as a unibyte string."
--- /dev/null
+;;; ntlm-tests.el --- tests for ntlm.el -*- lexical-binding: t -*-
+
+;; Copyright (C) 2020 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+(require 'ert)
+(require 'ntlm)
+
+;; This is the Lisp bignum implementation of `ntlm--time-to-timestamp',
+;; for reference.
+(defun ntlm-tests--time-to-timestamp (time)
+ "Convert TIME to an NTLMv2 timestamp.
+Return a unibyte string representing the number of tenths of a
+microsecond since January 1, 1601 as a 64-bit little-endian
+signed integer. TIME must be on the form (HIGH LOW USEC PSEC)."
+ (let* ((s (+ (ash (nth 0 time) 16) (nth 1 time)))
+ (us (nth 2 time))
+ (ps (nth 3 time))
+ (tenths-of-us-since-jan-1-1601
+ (+ (* s 10000000) (* us 10) (/ ps 100000)
+ ;; tenths of microseconds between 1601-01-01 and 1970-01-01
+ 116444736000000000)))
+ (apply #'unibyte-string
+ (mapcar (lambda (i)
+ (logand (ash tenths-of-us-since-jan-1-1601 (* i -8))
+ #xff))
+ (number-sequence 0 7)))))
+
+(ert-deftest ntlm-time-to-timestamp ()
+ ;; Verify poor man's bignums in implementation that can run on Emacs < 27.1.
+ (let ((time '(24471 63910 412962 0)))
+ (should (equal (ntlm--time-to-timestamp time)
+ (ntlm-tests--time-to-timestamp time))))
+ (let ((time '(397431 65535 999999 999999)))
+ (should (equal (ntlm--time-to-timestamp time)
+ (ntlm-tests--time-to-timestamp time)))))
+
+(provide 'ntlm-tests)