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

	nolist
	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/dosextens.i'
	include	'intuition/intuition.i'
	list
;
; new library call macros
;
getbase		macro
		ifc	'\1','Exec'
		  ifeq	NARG-2
		    move.l _ExecBase,\2
		  endc
		  ifeq	NARG-1
		    move.l _ExecBase,a6
		  endc
		endc
		ifnc	'\1','Exec'
		  ifeq	NARG-2
		    move.l _\1Base(a4),\2
		  endc
		  ifeq	NARG-1
		    move.l _\1Base(a4),a6
		  endc
		endc
		endm

lib		macro
		ifeq	NARG-2
		getbase	\1
		lib	\2
		endc
		ifeq	NARG-1
		xref	_LVO\1
		jsr	_LVO\1(a6)
		endc
		endm

jlib		macro
		ifeq	NARG-2
		getbase	\1
		jlib	\2
		endc
		ifeq	NARG-1
		jmp	_LVO\1(a6)
		endc
		endm

slib		macro
		move.l	a6,-(sp)
		lib	\1,\2
		move.l	(sp)+,a6
		endm

xlib		macro
		xref	_LVO\1
		endm

rw		macro
		dc.w	\1-*
		endm

_ExecBase	equ	4
;
;


;
; external references for encryption functions (in idea.c)
;
		xref	_idea_ecb_encrypt
		xref	_idea_cbc_encrypt
		xref	_idea_key_schedule

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	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	OpenLibrary
	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
	lib	CloseLibrary
initfail2
	move.l	crd_IntuitionBase(a4),a1
	lib	CloseLibrary
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	FindUnit
	tst.l	d0
	bne.s	unit_ready

	move.l	d2,d0
	bsr	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	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	_ExecBase,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	SendDosCallerRequest

	move.l	crd_DosCallerPort(a4),a1
	moveq	#MP_SIZE,d0
	lib	Exec,FreeMem

	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
	slib	Exec,AllocMem
	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
	lib	Exec,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
	slib	Exec,AddHead
	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	SendDosCallerRequest

	move.l	a6,-(sp)
	move.l	_ExecBase,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

	slib	Exec,ReplyMsg

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	TermIO

;
; actually any number will do,
; this device really doesn't even know it's size...
;
DoGetNumTracks
	move.l	#80,IO_ACTUAL(a1)
	bra	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	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	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	InvalidParams

	bsr.s	SendDosCallerRequest
	move.l	a2,a1
	bra	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
	lib	Exec,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
	lib	Exec,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	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	proc_reset		this doesn't need a valid key

	tst.b	crdu_KeyValid(a2)
	bne.b	key_valid
;
	bsr	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	proc_reset
	cmp.w	#CMD_READ,d0		if not read then write
	bne	do_write		write may be CMD_WRITE or TD_FORMAT

	bsr	openfile
	tst.l	d0
	beq	proc_error

	bsr	seekfile
	tst.l	d0
	bmi	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	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	_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	proc_replymsg

do_write
	bsr	openfile
	tst.l	d0
	beq	proc_error

	bsr	seekfile
	tst.l	d0
	bmi	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	_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	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
	jlib	ReplyMsg

;
; 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
	lib	Exec,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	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	showtext

	bsr	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	showtext

	bsr	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	_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	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	enter_pass

get_input_line
	lea	crdu_Buffer(a2),a3

msgloop	move.l	wd_UserPort(a4),a0
	lib	Exec,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	msgloop

getline_done
	clr.b	(a3)
	moveq	#10,d0
	moveq	#20,d1
	lea	spaces_txt(pc),a0
	bsr	showtext
	moveq	#10,d0
	moveq	#28,d1
	lea	spaces_txt(pc),a0
	bsr	showtext
	moveq	#10,d0
	moveq	#36,d1
	lea	spaces_txt(pc),a0
	bsr	showtext
	moveq	#10,d0
	moveq	#44,d1
	lea	spaces_txt(pc),a0
	bra	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

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

	end
