;
; cryptdev.a   --   created from fdev.device -- "filesystem in a file"
;
; 1992-10-14
;
; uses IDEA encryption algorithm
;
;
; created  1989-08-13  TR
;
; modifications:
;  1989-08-15  TR  -  added separate dos calling process
;  1989-08-16  TR  -  removed unit processes (all commands are IMMEDIATE...)
;  1989-08-19  TR  -  changed DosCaller parameter passing
;		      also no longer need to RemTask a process
;		      dos caller process messages now contain IORequest
;		      pointer in LN_NAME field. zero LN_NAME field
;		      means that the message is a request to quit
;		      the DosCaller process.
;  1989-08-20  TR  -  now closes the file when requested to turn motor off
;  1989-08-31  TR  -  some small bugs fixed. AbortIO now returns an error.
;
;  1991-06-10  TR  -  Now properly clears IO_ERROR when there is no error.
;		      Can now be assembled with standard include files.
;		      Created Makefile (for PD Make & A68k)
;
	incdir	coding:os2.0/include2.0/
	include	'exec/exec_lib.i'
	include	'exec/types.i'
	include	'exec/initializers.i'
	include	'exec/memory.i'
	include	'exec/io.i'
	include	'exec/devices.i'
	include	'exec/resident.i'
	include	'exec/errors.i'
	include	'devices/trackdisk.i'
	include	'libraries/dos.i'
	include	'libraries/dos_lib.i'
	include	'libraries/dosextens.i'
	include	'intuition/intuition.i'
	include	'intuition/intuition_lib.i'
;
; new library call macros
;
lib		macro
		jsr	_LVO\1(a6)
		endm

rw		macro
		dc.w	\1-*
		endm

BLOCKSIZE	equ	512
KS_SIZE		equ	216

;
; the data structure of the device
;

	STRUCTURE CryptDevBase,LIB_SIZE
	 APTR	crd_SegList
	 APTR	crd_DosLib
	 APTR	crd_IntuitionBase
	 APTR	crd_DosCallerProc
	 APTR	crd_DosCallerPort
	 STRUCT	crd_DosCallerInitMsg,MN_SIZE
	 STRUCT	crd_UnitList,MLH_SIZE
	LABEL crd_Sizeof

;
; unit data structure. note that units are linked to the device
; structure using the crdu_Link field.
;
	STRUCTURE CryptDevUnit,UNIT_SIZE
	 STRUCT	crdu_Link,MLN_SIZE	to link to units to a list
	 UWORD	crdu_UnitNum
	 LONG	crdu_FileHandle		zero if file not open
	 STRUCT	crdu_FileName,32
	 STRUCT crdu_KeySched,KS_SIZE
	 STRUCT	crdu_IVec,8
	 STRUCT	crdu_Buffer,BLOCKSIZE
	 UBYTE	crdu_KeyValid
	LABEL crdu_Sizeof

DOSCALLERSTACK	equ	2000
DOSCALLERPRI	equ	0

;
; try to remember to increment the revision number...
;
VERSION		equ	1
REVISION	equ	8


	section	text,CODE

;
; start of code -- just return zero if someone executes this directly
; the Null-standard library routine points also here
;
Null	moveq	#0,d0
	rts

RomTag	dc.w	RTC_MATCHWORD
	dc.l	RomTag
	dc.l	EndSkip
	dc.b	RTF_AUTOINIT
	dc.b	VERSION
	dc.b	NT_DEVICE
	dc.b	0
	dc.l	dev_name
	dc.l	dev_idstring
	dc.l	dev_init

dev_name	dc.b	'crypt.device',0
dev_idstring	dc.b	'crypt 1.8 (TR 16-Oct-1992)',13,10,0

dosname		dc.b	'dos.library',0
intuition_name	dc.b	'intuition.library',0

doscallername	dc.b	'CryptDev_DosCaller',0

	even
;
; because the RTF_AUTOINIT flag in the RomTag structure was set
; RT_INIT points to this table of parameters for MakeLibrary()
;
dev_init
	dc.l	crd_Sizeof
	dc.l	dev_FuncInit
	dc.l	dev_StructInit
	dc.l	dev_InitRoutine

dev_StructInit
	INITBYTE LN_TYPE,NT_DEVICE
	INITLONG LN_NAME,dev_name
	INITLONG LIB_IDSTRING,dev_idstring
	INITBYTE LIB_FLAGS,LIBF_CHANGED!LIBF_SUMUSED
	INITLONG LIB_VERSION,(VERSION<<16)+REVISION
	dc.l	0

;
; a simple macro to save typing in library routine definitions below
;
fw	macro
	dc.w	\1-dev_FuncInit
	endm

dev_FuncInit
	dc.w	-1
; standard system library routines
	fw	Open
	fw	Close
	fw	Expunge
	fw	Null
; standard device routines

	fw	BeginIO
	fw	AbortIO

; end of table marker
	dc.w	-1

;
; device command table
;
DevCmdTable
	rw	Invalid			CMD_INVALID
	rw	ResetPassword		CMD_RESET
	rw	DoRead			CMD_READ
	rw	DoWrite			CMD_WRITE
	rw	DoNothing		CMD_UPDATE
	rw	DoNothing		CMD_CLEAR
	rw	Invalid			CMD_STOP
	rw	Invalid			CMD_START
	rw	DoNothing		CMD_FLUSH
	rw	DoMotor			TD_MOTOR
	rw	DoNothing		TD_SEEK
	rw	DoWrite			TD_FORMAT
	rw	DoNothing		TD_REMOVE
	rw	DoNothing		TD_CHANGENUM
	rw	DoNothing		TD_CHANGESTATE
	rw	DoNothing		TD_PROTSTATUS
	rw	Invalid			TD_RAWREAD
	rw	Invalid			TD_RAWWRITE
	rw	DoGetDriveType		TD_GETDRIVETYPE
	rw	DoGetNumTracks		TD_GETNUMTRACKS
	rw	DoNothing		TD_ADDCHANGEINT
	rw	DoNothing		TD_REMCHANGEINT

;
; init routine, entry: d0=LibBase, a0=SegList, a6=ExecBase
; return: d0=LibBase if initialization is successfull, zero if failed
;
dev_InitRoutine
	move.l	a4,-(sp)
	move.l	d0,a4			get library base in a4
	move.l	a0,crd_SegList(a4)	save our seglist for expunge...

;
; allocate and partially initialize the dos caller process message port
; the dos caller process initializes MP_SIGBIT and MP_SIGTASK fields
; and sets MP_FLAGS to PA_SIGNAL...
;
	moveq	#MP_SIZE,d0
	move.l	#MEMF_CLEAR!MEMF_PUBLIC,d1
	lib	AllocMem
	move.l	d0,crd_DosCallerPort(a4)
	beq.w	initfail3
	move.l	d0,a0
	move.b	#PA_IGNORE,MP_FLAGS(a0)
	lea	MP_MSGLIST(a0),a0
	NEWLIST	a0

	lea	intuition_name(pc),a1
	moveq	#33,d0
	lib	OpenLibrary
	move.l	d0,crd_IntuitionBase(a4)
	beq.b	initfail2a

	lea	dosname(pc),a1
	moveq	#33,d0
	lib	OldOpenLibrary
	move.l	d0,crd_DosLib(a4)
	beq.s	initfail2
	move.l	a6,-(sp)
	move.l	d0,a6

	move.l	#DOSCALLERSTACK,d4
	lea	doscallerseglist(pc),a0
	move.l	a0,d3
	lsr.l	#2,d3			convert to BPTR
	moveq	#DOSCALLERPRI,d2
	lea	doscallername(pc),a0
	move.l	a0,d1
	lib	CreateProc
	move.l	(sp)+,a6
	move.l	d0,crd_DosCallerProc(a4)
	tst.l	d0
	beq.s	initfail1

;
; send startup message to the dos caller process
;
	move.l	d0,a0
	lea	crd_DosCallerInitMsg(a4),a1
	move.l	a4,LN_NAME(a1)
	lib	PutMsg

	lea	crd_UnitList(a4),a0
	NEWLIST	a0

	move.l	a4,d0
	bra.s	init9

initfail1
	move.l	crd_DosLib(a4),a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)
initfail2
	move.l	crd_IntuitionBase(a4),a1
	move.l	4.w,a6
	jsr	_LVOCloseLibrary(a6)
initfail2a
	move.l	crd_DosCallerPort(a4),a1
	moveq	#MP_SIZE,d0
	lib	FreeMem
initfail3
	moveq	#0,d0

init9	move.l	(sp)+,a4
	rts

;
; open routine, entry: a6=DevBase, a1=IORequest, d0=UnitNum, d1=Flags
; return: d0=zero or error code
;
Open	movem.l	d2/a2-a4,-(sp)
	move.l	a1,a2

	move.l	d0,d2		save unit number
	bsr.w	FindUnit
	tst.l	d0
	bne.s	unit_ready

	move.l	d2,d0
	bsr.w	InitUnit
	tst.l	d0
	beq.s	open_err

unit_ready
	move.l	d0,a3
	move.l	d0,IO_UNIT(a2)

	addq.w	#1,UNIT_OPENCNT(a3)
	addq.w	#1,LIB_OPENCNT(a6)
	and.b	#~LIBF_DELEXP,LIB_FLAGS(a6)
	moveq	#0,d0

open_exit
	movem.l	(sp)+,d2/a2-a4
	rts

open_err
	moveq	#IOERR_OPENFAIL,d0
	move.b	d0,IO_ERROR(a2)
	bra.s	open_exit

;
; close routine, entry: a6=devBase, a1=IORequest
; return: d0=SegList if device no longer in use, else zero
;
Close	movem.l	a2-a3,-(sp)
	move.l	a1,a2

	move.l	IO_UNIT(a2),a3

	moveq	#-1,d0
	move.l	d0,IO_DEVICE(a2)
	move.l	d0,IO_UNIT(a2)

	subq.w	#1,UNIT_OPENCNT(a3)
	bne.s	do_close
	bsr.w	ExpungeUnit

do_close
	moveq	#0,d0
	subq.w	#1,LIB_OPENCNT(a6)
	bne.s	.100
	btst	#LIBB_DELEXP,LIB_FLAGS(a6)
	beq.s	.100
	bsr.s	Expunge

.100:	movem.l	(sp)+,a2-a3
	rts

;
; expunge routine, entry: a6=devBase
; return: d0=SegList if device no longer in use, else zero
;
Expunge	tst.w	LIB_OPENCNT(a6)
	beq.s	.200
	or.b	#LIBF_DELEXP,LIB_FLAGS(a6)
	moveq	#0,d0
	rts
.200:	movem.l	a4-a6,-(sp)
	move.l	a6,a4
	move.l	4.w,a6
	move.l	crd_SegList(a4),a5
	move.l	a4,a1
	lib	Remove

;
; send termination message to dos caller process
;
	suba.l	a1,a1
	move.l	a4,a6
	bsr.w	SendDosCallerRequest

	move.l	crd_DosCallerPort(a4),a1
	moveq	#MP_SIZE,d0
	move.l	4.w,a6
	jsr	_LVOFreeMem(a6)

	move.l	crd_DosLib(a4),a1
	lib	CloseLibrary

	move.l	crd_IntuitionBase(a4),a1
	lib	CloseLibrary

	move.l	a4,a1
	moveq	#0,d0
	moveq	#0,d1
	move.w	LIB_NEGSIZE(a4),d0
	move.w	LIB_POSSIZE(a4),d1
	sub.l	d0,a1
	add.l	d1,d0
	lib	FreeMem
	move.l	a5,d0
	movem.l	(sp)+,a4-a6
	rts

;
; find unit with a given unit number
; inputs: a6=DevBase, d0=UnitNum
; returns: d0=pointer to unit or zero if not found
FindUnit
	move.l	crd_UnitList(a6),a0
.00	move.l	(a0),d1
	beq.s	.01
	cmp.w	crdu_UnitNum-crdu_Link(a0),d0
	beq.s	.02
	move.l	d1,a0
	bra.s	.00
.01	moveq	#0,d0
	rts
.02	lea	-crdu_Link(a0),a0
	move.l	a0,d0
	rts

;
; initialize an unit. a6=DevBase, d2=unitnum
;
InitUnit
	movem.l	d2-d4/a3,-(sp)

	move.l	#crdu_Sizeof,d0
	move.l	#MEMF_PUBLIC!MEMF_CLEAR,d1
	move.l	a6,-(sp)
	move.l	4.w,a6
	lib	AllocMem
	move.l	(sp)+,a6
	tst.l	d0
	beq.s	init_u9
	move.l	d0,a3
	move.w	d2,crdu_UnitNum(a3)

; create filename for FDEV-file
	movem.l	a2-a3/a6,-(sp)
	lea	formatstr(pc),a0
	lea	crdu_UnitNum(a3),a1
	lea	putch(pc),a2
	lea	crdu_FileName(a3),a3
	move.l	4.w,a6
	lib	RawDoFmt
	movem.l	(sp)+,a2-a3/a6

;
; (encryption key is asked from the user and initialized
; in DosCallerProc if crdu_KeyValid is false...)
;

	lea	crd_UnitList(a6),a0
	lea	crdu_Link(a3),a1

	move.l	a6,-(sp)
	move.l	4.w,a6
	lib	AddHead
	move.l	(sp)+,a6

	move.l	a3,d0

init_u9	movem.l	(sp)+,d2-d4/a3
	rts

putch	move.b	d0,(a3)+
	rts

formatstr	dc.b	'CRDEV%d:',0

	even
;
; expunge an unit. a3=unit, a6=device
;
ExpungeUnit
;
; request dos caller process to close the file. we give it a fake
; iorequest with io_Command set to CMD_RESET. we use the crdu_FileName
; field of the unit structure to store that iorequest (it is not used
; anymore anyway...)
;
	lea	crdu_FileName(a3),a1
	move.l	a3,IO_UNIT(a1)			this is important
	move.w	#CMD_RESET,IO_COMMAND(a1)
	bsr.w	SendDosCallerRequest

	move.l	a6,-(sp)
	move.l	4.w,a6

	lea	crdu_Link(a3),a1
	lib	Remove

	move.l	a3,a1
	move.l	#crdu_Sizeof,d0
	lib	FreeMem

	move.l	(sp)+,a6
	rts

;
; a1=IORequest, a6=Device
;
BeginIO	movem.l	d2/a2-a3,-(sp)	
	move.l	a1,a2
	clr.b	IO_ERROR(a2)
	move.l	IO_UNIT(a2),a3
	move.w	IO_COMMAND(a2),d2
	and.w	#$7fff,d2
	cmp.w	#TD_LASTCOMM,d2
	bcc.s	invalid_cmd

;beginio_immediate
;PerformIO	; a2=IORequest, a3=Unit, a6=Device
	add.w	d2,d2
	lea	DevCmdTable(pc),a0
	add.w	d2,a0
	add.w	(a0),a0
	jsr	(a0)

beginio_exit
	movem.l	(sp)+,d2/a2-a3
	rts

invalid_cmd
	move.b	#IOERR_NOCMD,IO_ERROR(a1)
	bra.s	beginio_exit

; you can't abort this
AbortIO	moveq	#IOERR_NOCMD,d0
	rts

InvalidParams
	move.b	#IOERR_BADLENGTH,IO_ERROR(a1)
	bra.b	TermIO

	; a1=IORequest, a6=Device
Invalid	move.b	#IOERR_NOCMD,IO_ERROR(a1)
	; fall to TermIO
DoNothing
	clr.l	IO_ACTUAL(a1)
TermIO		; a1=IORequest, a3=Unit, a6=Device
;termio_immediate
	btst	#IOB_QUICK,IO_FLAGS(a1)
	bne.s	termio_exit

	move.l	a6,-(sp)
	move.l	4.w,a6
	lib	ReplyMsg
	move.l	(sp)+,a6

termio_exit
	rts

;
; here are the device command routines
;  a1=a2=IORequest
;  a3=Unit
;  a6=Device
;

DoGetDriveType
	moveq	#1,d0			make it look like 3.5"
	move.l	d0,IO_ACTUAL(a1)
	bra.b	TermIO

;
; actually any number will do,
; this device really doesn't even know it's size...
;
DoGetNumTracks
	move.l	#80,IO_ACTUAL(a1)
	bra.b	TermIO

;
; CMD_RESET, clears password
;
ResetPassword
	lea	crdu_KeySched(a3),a0
	moveq	#(KS_SIZE/4)-1,d1
	moveq	#0,d0
.1	move.l	d0,(a0)+
	dbf	d1,.1
	clr.b	crdu_KeyValid(a3)
	bra.b	TermIO

;
; this does not work exactly like trackdisk TD_MOTOR but hopefully
; the file system doesn't mind...
;
DoMotor	moveq	#0,d0
	tst.l	crdu_FileHandle(a3)
	sne	d0
	move.l	d0,IO_ACTUAL(a1)
	tst.l	IO_LENGTH(a1)
	bne.s	.01
	move.b	#CMD_RESET,IO_COMMAND+1(a1)
	bsr.s	SendDosCallerRequest	request to close the file
	move.b	#TD_MOTOR,IO_COMMAND+1(a2)
.01	move.l	a2,a1
	bra.b	TermIO

DoRead
DoWrite	;check that IO_OFFSET and IO_LENGTH are divisible by BLOCKSIZE (512)
	move.l	IO_OFFSET(a1),d0
	and.w	#BLOCKSIZE-1,d0
	bne.b	InvalidParams
	move.l	IO_LENGTH(a1),d0
	and.w	#BLOCKSIZE-1,d0
	bne.w	InvalidParams

	bsr.s	SendDosCallerRequest
	move.l	a2,a1
	bra.b	TermIO

;
; send requests to DosCallerProc
;
SendDosCallerRequest
	movem.l	d5/a2/a5-a6,-(sp)
	move.l	a1,d5

	move.l	a6,a5

	moveq	#MN_SIZE+MP_SIZE,d0
	move.l	#MEMF_CLEAR!MEMF_PUBLIC,d1
	move.l	4.w,a6
	lib	AllocMem
	tst.l	d0
	beq.s	sendreq_end
	move.l	d0,a2

	move.l	d5,LN_NAME(a2)

	moveq	#-1,d0
	lib	AllocSignal
	move.b	d0,MN_SIZE+MP_SIGBIT(a2)
	bmi.s	sendreq_freemsg

	suba.l	a1,a1
	lib	FindTask
	move.l	d0,MN_SIZE+MP_SIGTASK(a2)

	lea	MN_SIZE(a2),a0
	move.l	a0,MN_REPLYPORT(a2)

	lea	MN_SIZE+MP_MSGLIST(a2),a0
	NEWLIST	a0

	move.l	crd_DosCallerPort(a5),a0
	move.l	a2,a1
	lib	PutMsg
	lea	MN_SIZE(a2),a0
	lib	WaitPort	

	moveq	#0,d0
	move.b	MN_SIZE+MP_SIGBIT(a2),d0
	lib	FreeSignal

sendreq_freemsg
	move.l	a2,a1
	moveq	#MN_SIZE+MP_SIZE,d0
	lib	FreeMem
sendreq_end
	movem.l	(sp)+,d5/a2/a5-a6
	rts

;
; the dos caller process keeps DosBase in d7
; this macro is used to call DOS
;
calldos	macro
	exg	d7,a6
	lib	\1
	exg	d7,a6
	endm

;
; the dos caller process. only this process uses unit crdu_FileHandle fields
;

; a fake seglist for CreateProc()
	cnop	0,4
	dc.l	20
doscallerseglist
	dc.l	0

doscaller_start
	suba.l	a1,a1
	move.l	4.w,a6
	lib	FindTask
	move.l	d0,a3
	lea	pr_MsgPort(a3),a0
	lib	WaitPort		wait for startup message
	move.l	d0,a1
	move.l	d0,a2
	lib	Remove			remove the message from port
	move.l	LN_NAME(a2),a5		get device base address
	move.l	crd_DosLib(a5),d7	get DosBase
	move.l	crd_IntuitionBase(a5),d6
	move.l	crd_DosCallerPort(a5),a5	get message port address

	move.l	a3,MP_SIGTASK(a5)
	moveq	#-1,d0
	lib	AllocSignal
	move.b	d0,MP_SIGBIT(a5)
	clr.b	MP_FLAGS(a5)		set flags to PA_SIGNAL

	bra.s	proc_msgloop

proc_wait
	moveq	#0,d0
	move.b	MP_SIGBIT(a5),d1
	bset	d1,d0
	lib	Wait

proc_msgloop
	move.l	a5,a0
	lib	GetMsg
	tst.l	d0
	beq.s	proc_wait
	move.l	d0,a4
	move.l	LN_NAME(a4),d0
	beq.w	quit_proc
	move.l	d0,a3			get IORequest pointer to a3
	move.l	IO_UNIT(a3),a2		get unit pointer to a2
	cmp.w	#CMD_RESET,IO_COMMAND(a3)
	beq.w	proc_reset		this doesn't need a valid key

	tst.b	crdu_KeyValid(a2)
	bne.b	key_valid
;
	bsr.w	AskPassword
;

key_valid
	move.w	IO_COMMAND(a3),d0
	and.w	#$7fff,d0		mask out EXTCOM flag bit
	cmp.w	#CMD_RESET,d0		request to close file ?
	beq.w	proc_reset
	cmp.w	#CMD_READ,d0		if not read then write
	bne.b	do_write		write may be CMD_WRITE or TD_FORMAT

	bsr.w	openfile
	tst.l	d0
	beq.w	proc_error

	bsr.w	seekfile
	tst.l	d0
	bmi.w	proc_error
;
; read
;
	move.l	crdu_FileHandle(a2),d1
	move.l	IO_DATA(a3),d2
	move.l	IO_LENGTH(a3),d3
	calldos	Read
	tst.l	d0
	bmi.w	proc_error
	move.l	d0,IO_ACTUAL(a3)
	move.l	d0,d3
;
; decrypt
;
	move.l	d6,-(sp)
	move.l	#BLOCKSIZE,d5
	move.l	IO_OFFSET(a3),d6

decrypt_loop
	move.l	d6,crdu_IVec(a2)
	move.l	d6,crdu_IVec+4(a2)

	add.l	d5,d6

	clr.l	-(sp)		;decrypt
	pea	crdu_IVec(a2)
	pea	crdu_KeySched(a2)
	move.l	d5,-(sp)
	move.l	d2,-(sp)
	move.l	d2,-(sp)
	bsr.w	_idea_cbc_encrypt
	lea	6*4(sp),sp
	add.l	d5,d2
	sub.l	d5,d3
	bgt.b	decrypt_loop
	move.l	(sp)+,d6
	bra.b	proc_replymsg

do_write
	bsr.w	openfile
	tst.l	d0
	beq.w	proc_error

	bsr.w	seekfile
	tst.l	d0
	bmi.b	proc_error

	move.l	d6,-(sp)
	move.l	#BLOCKSIZE,d5
	move.l	IO_DATA(a3),d2
	move.l	IO_LENGTH(a3),d3
	clr.l	IO_ACTUAL(a3)
	move.l	IO_OFFSET(a3),d6

encrypt_loop
	move.l	d6,crdu_IVec(a2)
	move.l	d6,crdu_IVec+4(a2)

	add.l	d5,d6

	pea	1		;encrypt
	pea	crdu_IVec(a2)
	pea	crdu_KeySched(a2)
	move.l	d5,-(sp)
	pea	crdu_Buffer(a2)
	move.l	d2,-(sp)
	bsr.w	_idea_cbc_encrypt
	lea	6*4(sp),sp

	move.l	crdu_FileHandle(a2),d1
	lea	crdu_Buffer(a2),a0
	movem.l	d2/d3,-(sp)
	move.l	a0,d2
	move.l	d5,d3
	calldos	Write
	movem.l	(sp)+,d2/d3
	tst.l	d0
	bmi.b	proc_error1

	add.l	d5,d2
	add.l	d5,IO_ACTUAL(a3)
	sub.l	d5,d3
	bgt.b	encrypt_loop

	move.l	(sp)+,d6

proc_replymsg
	move.l	a4,a1
	lib	ReplyMsg
	bra.w	proc_msgloop

proc_error1
	move.l	(sp)+,d6

proc_error
	calldos	IoErr
	tst.b	d0
	beq.s	.01
	moveq	#-10,d0
.01	move.b	d0,IO_ERROR(a3)
	clr.l	IO_ACTUAL(a3)
	bra.s	proc_replymsg

proc_reset
	bsr.s	closefile
	bra.s	proc_replymsg

openfile
	move.l	crdu_FileHandle(a2),d0
	bne.s	opf9
	lea	crdu_FileName(a2),a0
	move.l	a0,d1
	move.l	#MODE_OLDFILE,d2
	calldos	Open
	move.l	d0,crdu_FileHandle(a2)
opf9	rts

seekfile
	move.l	crdu_FileHandle(a2),d1
	move.l	IO_OFFSET(a3),d2
	moveq	#OFFSET_BEGINNING,d3
	calldos	Seek
	rts

closefile
	move.l	crdu_FileHandle(a2),d1
	beq.s	clf9
	calldos	Close
	clr.l	crdu_FileHandle(a2)
clf9	rts

;
; quit the DosCaller process (just return from it...)
;
quit_proc
	move.l	a4,a1
	jmp	_LVOReplyMsg(a6)

;
; ask password from the user
;
AskPassword
	movem.l	d2/d3/a4/a3/a6,-(sp)

	moveq	#0,d2

	lea	crdu_Buffer(a2),a0
	suba.l	a1,a1
	move.l	#sc_BarHeight+1,d0
	moveq	#WBENCHSCREEN,d1
	move.l	d6,a6			;intuitionbase
	lib	GetScreenData
	tst.l	d0
	beq.b	.0

	move.b	crdu_Buffer+sc_BarHeight(a2),d2

.0	lea	NewWin(pc),a0
	lea	crdu_Buffer(a2),a1
	moveq	#nw_SIZEOF,d0
	move.l	4.w,a6
	lib	CopyMem

	add.w	d2,crdu_Buffer+nw_Height(a2)

	move.l	a2,-(sp)
	lea	passwd_format(pc),a0
	lea	crdu_UnitNum(a2),a1
	lea	crdu_Buffer+400(a2),a3
	lea	putch(pc),a2
	lib	RawDoFmt
	move.l	(sp)+,a2

	move.l	d6,a6
	lea	crdu_Buffer(a2),a0
	lib	OpenWindow
	tst.l	d0
	beq.w	askpw_end
	move.l	d0,a4

	move.l	a2,-(sp)
	move.l	a4,a0
	lea	crdu_Buffer+400(a2),a1
	lea	-1,a2
	lib	SetWindowTitles
	move.l	(sp)+,a2

enter_pass
	lea	enterpass_txt(pc),a0
	moveq	#10,d0
	moveq	#8,d1
	bsr.w	showtext

	bsr.w	get_input_line

	lea	crdu_Buffer(a2),a0
	move.l	a0,d0
	lea	crdu_Buffer+128(a2),a1
.1	move.b	(a0)+,(a1)+		;make a copy of the passphrase
	bne.b	.1

	suba.l	d0,a0
	cmpa.w	#11,a0			;minimum passphrase length 10 chars
	bcs.b	too_short

	lea	verifypass_txt(pc),a0
	moveq	#10,d0
	moveq	#8,d1
	bsr.w	showtext

	bsr.w	get_input_line

	lea	crdu_Buffer(a2),a0
	lea	crdu_Buffer+128(a2),a1
.2	cmpm.b	(a0)+,(a1)+
	bne.b	verify_fail
	tst.b	-1(a0)
	bne.b	.2
;
; verify ok
;
	movea.l	a4,a0
	movea.l	d6,a6
	lib	CloseWindow

	lea	crdu_Buffer+256(a2),a0
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

	lea	crdu_Buffer(a2),a0
	lea	crdu_Buffer+256+16(a2),a3
passwd_l1
	lea	crdu_Buffer+256(a2),a1
passwd_loop
	move.b	(a0)+,d0
	beq.b	do_key_sched
	add.b	d0,(a1)+
	cmpa.l	a3,a1
	bcs.b	passwd_loop
	bra.b	passwd_l1

do_key_sched
	pea	crdu_KeySched(a2)
	pea	crdu_Buffer+256(a2)
	bsr.w	_idea_key_schedule
	addq.l	#8,sp

;
; remove password from memory
;
	lea	crdu_Buffer(a2),a0
	moveq	#0,d0
	moveq	#(512/4)-1,d1
clr2_loop
	move.l	d0,(a0)+
	dbf	d1,clr2_loop

	st	crdu_KeyValid(a2)

askpw_end
	movem.l	(sp)+,d2/d3/a3/a4/a6
	rts

too_short
	lea	tooshort_txt(pc),a0
	bra.b	fail_comm

verify_fail
	lea	verifyfail_txt(pc),a0

fail_comm
	moveq	#10,d0
	moveq	#8,d1
	bsr.w	showtext

	move.l	wd_WScreen(a4),a0
	move.l	d6,a6
	lib	DisplayBeep

	move.l	d7,a6
	move.l	#2*50,d1	;2 seconds
	lib	Delay
	bra.w	enter_pass

get_input_line
	lea	crdu_Buffer(a2),a3

msgloop	move.l	wd_UserPort(a4),a0
	move.l	4.w,a6
	lib	WaitPort
	move.l	wd_UserPort(a4),a0
	lib	GetMsg
	tst.l	d0
	beq.b	msgloop
	move.l	d0,a1
	move.l	im_Class(a1),d2
	move.w	im_Code(a1),d3
	lib	ReplyMsg
	cmp.l	#VANILLAKEY,d2
	bne.s	msgloop

	cmp.b	#8,d3		;backspace?
	beq.b	pw_del
	cmp.b	#127,d3		;del?
	bne.b	no_del

pw_del	lea	crdu_Buffer(a2),a0
	cmp.l	a0,a3
	beq.b	do_beep		;can't delete if no chars in buffer

	subq.l	#1,a3
	lea	space_txt(pc),a0
	bsr.b	point
	bra.b	msgloop

no_del	cmp.b	#13,d3		;CR?
	beq.b	getline_done

	lea	crdu_Buffer+125(a2),a0
	cmp.l	a0,a3
	bcs.b	add_char

do_beep	move.l	wd_WScreen(a4),a0
	move.l	d6,a6
	lib	DisplayBeep
	bra.b	msgloop

add_char
	lea	point_txt(pc),a0
	bsr.b	point

	move.b	d3,(a3)+
	bra.b	msgloop

getline_done
	clr.b	(a3)
	moveq	#10,d0
	moveq	#20,d1
	lea	spaces_txt(pc),a0
	bsr.b	showtext
	moveq	#10,d0
	moveq	#28,d1
	lea	spaces_txt(pc),a0
	bsr.b	showtext
	moveq	#10,d0
	moveq	#36,d1
	lea	spaces_txt(pc),a0
	bsr.b	showtext
	moveq	#10,d0
	moveq	#44,d1
	lea	spaces_txt(pc),a0
	bra.b	showtext

;
;
;
point	lea	crdu_Buffer(a2),a1
	suba.l	a3,a1
	move.l	a1,d0
	neg.l	d0
	move.l	d0,d1
	and.w	#%11111,d0
	lsr.w	#5,d1
	lsl.l	#3,d0
	add.w	#10,d0
	lsl.l	#3,d1
	add.w	#20,d1
;	bra	showtext
; drop to showtext

;
; text in a0..., x/y coords in d0/d1 (upper word must be zero)
;
showtext
	swap	d0
	or.l	d0,d1

	move.l	a0,d0
;
; use the end of crdu_Buffer for an IntuiText structure
;
	lea	crdu_Buffer+BLOCKSIZE-it_SIZEOF(a2),a0
	move.l	#(1<<24)!(0<<16)!(RP_JAM2<<8)!0,(a0)+	;pens, drawmode
	move.l	d1,(a0)+			;left, top
	lea	Topaz80(pc),a1
	move.l	a1,(a0)+		;font
	move.l	d0,(a0)+		;text
	clr.l	(a0)			;next
	move.l	d6,a6			;intutionbase
	move.l	wd_RPort(a4),a0
	lea	crdu_Buffer+BLOCKSIZE-it_SIZEOF(a2),a1
	moveq	#0,d0
	moveq	#0,d1
	move.b	wd_BorderLeft(a4),d0
	move.b	wd_BorderTop(a4),d1
	lib	PrintIText
	rts

NewWin	dc.w	100,100,300,70
	dc.b	-1,-1
	dc.l	VANILLAKEY
	dc.l	WINDOWDEPTH!WINDOWDRAG!ACTIVATE!SMART_REFRESH!NOCAREREFRESH
	dc.l	0
	dc.l	0
	dc.l	dev_name	;title
	dc.l	0,0	;no screen, no bitmap
	dc.w	0,0,0,0
	dc.w	WBENCHSCREEN

Topaz80	dc.l	topaz_name
	dc.w	8
	dc.b	FS_NORMAL
	dc.b	FPF_ROMFONT

topaz_name	dc.b	'topaz.font',0

enterpass_txt	dc.b	'Enter pass phrase    ',0
verifypass_txt	dc.b	'Retype pass phrase   ',0
verifyfail_txt	dc.b	'Pass phrases differ  ',0
tooshort_txt	dc.b	'Pass phrase too short',0
passwd_format	dc.b	'cryptdev unit #%d',0
point_txt	dc.b	'.',0
space_txt	dc.b	' ',0
spaces_txt	dc.b	'                                 ',0
		even

ZSIZE		equ	108

;-------------------------------------------------------------------------
; Internal functions
;-------------------------------------------------------------------------
; macro: imul dea,dn , d7=scratch
; Arg 1 accessed only once (can be (an)+) Handles zero value arguments.
;

imul		macro

		move.w	\2,d7
		beq.b	.imul0
		mulu.w	\1,d7
		beq.b	.imul1
		move.w	d7,\2
		swap	d7
		sub.w	d7,\2
		moveq	#0,d7
		addx.w	d7,\2
		bra.b	.imul3

.imul0		move.w	\1,\2
.imul1		neg.w	\2
		addq.w	#1,\2
.imul3
		endm

;-------------------------------------------------------------------------
; word16 _mul_idea(word16 a,word16 b)
;
; Return a * b mod 65537

__mul_idea	move.l	d7,-(sp)
		move.w	10(sp),d0

		imul	14(sp),d0

		move.l	(sp)+,d7
		rts

;-------------------------------------------------------------------------
; word16 _inv_idea(word16 a)
; d0 = inv(d0)
;
; Return multiplicative inverse of a mod 65537
;

__inv_idea	move.w	6(sp),d0

inv		cmp.w	#2,d0		; inv(0)=0,inv(1)=1
		bcs.b	.1

		cmp.w	#3,d0
		bcc.b	.2

		move.w	#32769,d0	; inv(2)
.1		rts

.2		movem.l	d2-d5,-(sp)
		move.l	#$10001,d1	; d1 = n1
		moveq	#1,d2		; d2 = b2
		moveq	#0,d3		; d3 = b1		

inv_loop	divu.w	d0,d1
		move.l	d1,d4
		swap	d4		; r = d4
		tst.w	d4
		beq.b	inv_done

		move.w	d2,d5
		muls.w	d1,d5
		exg	d3,d2
		sub.l	d5,d2
		moveq	#0,d1
		move.w 	d0,d1
		move.w	d4,d0
		bra.b	inv_loop

inv_done	tst.l	d2
		bpl.b	.1

		move.l	#$10001,d0
		add.l	d2,d0
		bra.b	.2

.1		move.l	d2,d0

.2		movem.l	(sp)+,d2-d5
		rts

;-------------------------------------------------------------------------
; _en_key_idea(a0=userkey,a1=subkey)
;
; Create 54-word encryption subkey from 8-word user key
;

__en_key_idea	movem.l	4(sp),a0-a1

en_key_idea	movem.l	d2-d4,-(sp)
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)+
		move.l	(a0)+,(a1)

		suba.w	#12,a1

		moveq	#47,d0
		moveq	#0,d1
		moveq	#14,d4

.1		move.w	d1,d2
		addq.w	#2,d2
		and.w	d4,d2
		move.w	0(a1,d2.w),d3
		swap	d3
		addq.w	#2,d2
		and.w	d4,d2
		move.w	0(a1,d2.w),d3
		lsr.l	#7,d3
		move.w	d3,16(a1,d1.w)
		addq.w	#2,d1
		move.w	d1,d2
		and.w	#16,d2
		adda.w	d2,a1
		and.w	d4,d1
		dbra	d0,.1

		movem.l	(sp)+,d2-d4
		rts

;-------------------------------------------------------------------------
; _de_key_idea(a0=subkey,a1=inverted_subkey)
;
; Invert 54-word subkey for idea decrypting
;

__de_key_idea	movem.l	4(sp),a0-a1

de_key_idea	movem.l	d2-d3,-(sp)

		moveq	#96,d2
		moveq	#0,d3
		bra.b	e2

e1		move.l	8(a0,d2.w),-4(a1,d3.w)

e2		move.w	0(a0,d2.w),d0
		bsr.w	inv
		move.w	d0,0(a1,d3.w)
		move.l	2(a0,d2.w),d0
		neg.w	d0
		swap	d0
		neg.w	d0
		move.l	d0,2(a1,d3.w)
		move.w	6(a0,d2.w),d0
		bsr.w	inv
		move.w	d0,6(a1,d3.w)
		add.w	#12,d3
		sub.w	#12,d2
		bcc.b	e1

		move.l	2(a1),d0
		swap	d0
		move.l	d0,2(a1)
		move.l	98(a1),d0
		swap	d0
		move.l	d0,98(a1)

		movem.l	(sp)+,d2-d3
		rts

;-------------------------------------------------------------------------
; _cipher_idea(a0=src,a1=dest,a2=subkey)
;
; En/Decipher 4-word block from src to dest using 54-word subkey as key
;

round		macro
		imul	(a0)+,d0
		add.w	(a0)+,d1		
		add.w	(a0)+,d2

		move.w	d3,d7
		beq.b	imul0
		mulu.w	(a0)+,d7
		beq.b	imul1
		move.w	d7,d3
		swap	d7
		sub.w	d7,d3
		moveq	#0,d7
		addx.w	d7,d3
		bra.b	imul3
imul0		move.w	(a0)+,d3
imul1		neg.w	d3
		addq.w	#1,d3
imul3

		move.w	d0,d4
		eor.w	d2,d4


		move.w	d4,d7
		beq.b	imul00
		mulu.w	(a0)+,d7
		beq.b	imul01
		move.w	d7,d4
		swap	d7
		sub.w	d7,d4
		moveq	#0,d7
		addx.w	d7,d4
		bra.b	imul03
imul00		move.w	(a0)+,d4
imul01		neg.w	d4
		addq.w	#1,d4
imul03


		move.w	d1,d5
		eor.w	d3,d5
		add.w	d4,d5
		imul	(a0)+,d5
		move.w	d5,d6
		add.w	d4,d6

		eor.w	d5,d0
		move.w	d1,d4
		move.w	d2,d1
		eor.w	d5,d1
		move.w	d4,d2
		eor.w	d6,d2
		eor.w	d6,d3
		endm

__cipher_idea	move.l	a2,-(sp)
		movem.l	8(sp),a0-a2
		bsr.b	cipher_idea
		move.l	(sp)+,a2
		rts

cipher_idea	movem.l	d2-d7,-(sp)
		movem.w	(a0),d0-d3
		movea.l	a2,a0

		ifnd	UNROLL
		moveq	#7,d6
cipher_loop	swap	d6
		round
		swap	d6
		dbra	d6,cipher_loop
		endc

		ifd	UNROLL
		round
		round
		round
		round
		round
		round
		round
		round
		endc

		move.w	d0,d7
		beq.b	imul000
		mulu.w	(a0)+,d7
		beq.b	imul001
		move.w	d7,d0
		swap	d7
		sub.w	d7,d0
		moveq	#0,d7
		addx.w	d7,d0
		bra.b	imul003

imul000		move.w	(a0)+,d0
imul001		neg.w	d0
		addq.w	#1,d0
imul003

		exg	d1,d2
		add.w	(a0)+,d1
		add.w	(a0)+,d2
		imul	(a0)+,d3

		movem.w	d0-d3,(a1)
		movem.l	(sp)+,d2-d7
		rts

;-------------------------------------------------------------------------
; User callable routines 
;-------------------------------------------------------------------------
; idea_key_schedule(word16 *uk, word16 *ks)
;
; Create en/decryption keys from 8-word user key uk to 108-word key 
; schedule ks.
;

_idea_key_schedule
		movem.l	4(sp),a0-a1
		bsr.w	en_key_idea
		movea.l	8(sp),a0
		movea.l	a0,a1
		adda.w	#ZSIZE,a1
		bra.w	de_key_idea

;-------------------------------------------------------------------------
; idea_ebc_encrypt(word16 *src,word16 *dst,word16 *ks,int mode)
;
; En/decrypt 4 word (8-byte) block from src to dst using electronic code
; book mode.
;

_idea_ebc_encrypt
		move.l	a2,-(sp)
		movem.l	8(sp),a0-a2/d0
		tst.l	d0
		bne.w	e1
		adda.w	#ZSIZE,a2
		bsr.w	cipher_idea
		movea.l	(sp)+,a2
		rts

;-------------------------------------------------------------------------
; idea_cbc_encrypt(word16 *src, word16 *dst, int len, word16 *ks,
;   word16 *ivec,int mode)
;
; Encrypt (mode != 0) or decrypt (mode == 0) src to dst in cipher block 
; chaining mode. Len must be multiple of 8 bytes. Ivec contains final
; vector at return.
;

_idea_cbc_encrypt
		movem.l	a2-a5/d2-d4,-(sp)

		lea	32(sp),a0
		move.l	(a0)+,a3	; src
		move.l	(a0)+,a4	; dst
		move.l	(a0)+,d2	; len
		move.l	(a0)+,a2	; ks
		move.l	(a0)+,a5	; ivec

		tst.l	(a0)		; mode?
		beq.b	cbc_decrypt

cbc_en_loop	move.l	(a3)+,d3	; get data to encrypt
		move.l	(a3)+,d4
		eor.l	d3,(a5)		; eor with ivec		
		eor.l	d4,4(a5)

		movea.l	a5,a0		; cipher ivec to dst
		movea.l	a4,a1
		bsr.w	cipher_idea
		move.l	(a4)+,(a5)	; copy cipher to ivec
		move.l	(a4)+,4(a5)

		subq	#8,d2
		bhi.b	cbc_en_loop
		
cbc_exit	movem.l	(sp)+,a2-a5/d2-d4
		rts

cbc_decrypt	adda.w	#ZSIZE,a2	; decrypt key

cbc_de_loop	move.l	(a3)+,d3	; store cipher data (next ivec)
		move.l	(a3)+,d4

		lea	-8(a3),a0	; decrypt to src
		movea.l	a4,a1
		bsr.w	cipher_idea

		move.l	(a5)+,d0	; swap next/curr ivec
		move.l	(a5),d1
		move.l	d4,(a5)
		move.l	d3,-(a5)

		eor.l	d0,(a4)+	; eor deciphered with ivec
		eor.l	d1,(a4)+

		subq	#8,d2
		bhi.b	cbc_de_loop

		bra.b	cbc_exit



;
; label for RT_ENDSKIP -- note that this must be in the same hunk as RomTag
;
EndSkip

	end
