;*********************************************************************************
; Auteur : Pascal VERON                                                          *
;*********************************************************************************
;* RIJNDAEL: chiffrement uniquement pour un message de 128 bits et une clé de    *
;* 128 bits. La table S est stockée en EEPROM. Le message est reçu via le port   *
;* série. Le cryptogramme est renvoyé sur le port série                          *
;* La clé est stockée dans la mémoire flash à partir de l'adresse 1FFF-longueur  *
;* de la clé+1 									 *
;*********************************************************************************

	LIST      p=16F876            ; Définition de processeur
	#include <p16F876.inc>        ; Définitions des constantes
        __CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC & _WRT_ENABLE_ON

;******** DEFINITION DES CONSTANTES ***********************************************

mess_length EQU 16	; 128 bits = 16 octets ;
key_length EQU H'10'	; 128 bits = 16 octets ;

;**********************************************************************************

				; définitions des variables en ram 
	CBLOCK 0x20		

	Serial_buffer	: 1		; buffer d'envoi
	Serial_Count	: 1		; compteur pour l'envoi
	cpt	    	: 1		; compteur
	adr_elem_cour 	: 1		; pour shiftrow (contient l'adresse du dernier élément de la ligne devant être shiftée)
	cpt_cour      	: 1		; pour shiftrow (compteur permettant d'effectuer le nombre de shifts nécessaires pour une ligne)
        perm_cpt      	: 1		; pour shiftrow (compteur utilisé pour shifter une fois les élém. d'une ligne)
        tempo, indf_keep: 1		; variables temporaires de sauvegarde
	cpt_col		: 1		; pour mixcolumn compteur pour parcourir les élém d'une colonne
	tm		: 1		; variable temporaire pour mixcolumn
        rcon		: 1		; constante pour le key scheduling
	NR		: 1		; compteur pour le nombre de tours
	endc

	CBLOCK 0x40
	message		: mess_length   ; message occupera les adresses de 40 à au plus 5F
	endc

	CBLOCK 0x60
	key		: key_length    ; key occupera les adresses de 60 à au plus 7F
	endc
	
	; les adresses de message seront donc de la forme 010xxxxx et celles de key de la forme 
	; 011xxxxx, elles ne diffèrent donc que par le bit 5, pratique pour la gestion de l'adressage
	; indirect car il n'y a qu'un seul registre FSR

OPTIONVAL 	equ 0x08
        include "../myroutines/mydefs.asm"

	ORG 0000			; indique que l'instruction qui suit sera stockée à l'adresse 0000H 
        goto Start

Envoi					; routine d'envoi d'une suite d'octets, avant l'appel de la routine le  nombre d'octets 
        COPY INDF,Serial_buffer		; à envoyer doit être placé dans la variable cpt et l'adresse du premier octet dans FSR
	call Send
	incf FSR,F
	decfsz cpt,F
   	goto Envoi
	return

Init_Key				; routine qui récupère la clé dans la mém flash et la place en Ram
        MOVEVAL key, FSR
	BANK2	
	MOVEVAL H'1F', EEADRH		; ici on ne parcourera qu'au plus 32 adresses  donc la partie
					; haute de l'adresse ne changera jamais et vaudra 1F
	MOVEVAL H'FF'-key_length+1,EEADR; initialisation de la partie basse de l'adresse
Init_Key_loop
        bsf STATUS,RP0	       		; bank3 pour accéder à EECON1, registre de conf des lectures/écritures en EEPROM et FLASH
	bsf EECON1,EEPGD       		; on indique que l'accès se fera en mém flash
	bsf EECON1,RD	       		; on lance la phase de lecture (le résultat sera dispo 2 instructions après celle-ci)
	bcf STATUS,RP0	       		; bank2 pour récupérer le résultat dans EEDATH:EEDATA (ici seul EEDATA nous intéresse car sur les 14 bits, on ne veut que les 8 premiers)
	nop		       		; une instruction vide
        nop			
	movf EEDATA,W
	movwf INDF	       		; on place la valeur lue dans key[i]
        incf FSR,F	       		; on se positionne sur key[i+1] (inutile de changer de banque, FSR et INDF existent dans les 4)	
	incfsz EEADR,F	       		; on avance dans la mém flash, qd EEADR=FF alors EEADR+1=0 et c'est terminé	
        goto Init_Key_loop
	bcf STATUS,RP1	       		; on repasse en BANK0
	MOVEVAL 1, rcon	       		; initialisation de la constante pour le key scheduling
	return

Add_Round_Key			     	; routine d'ajout et de mise à jour de la clé
	MOVEVAL key,FSR		     	; on se positionne sur le premier élém de la clé	
	MOVEVAL mess_length, cpt     	; compteur pour parcourir les éléments 	
Add_Round_Key_loop
	movf INDF,W			; on récupère key[i] dans W
	bcf FSR,5			; l'adresse de key[i] et de mess[i] ne diffèrent que par le bit 5, on se place sur mess[i]
	xorwf INDF,F			; mess[i] <- mess[i] ^ key[i]
	bsf FSR,5			; on se replace sur key[i]
	incf FSR,F			; on passe à l'élém suivant
	decfsz cpt,F			; on décrémente le compteur
	goto Add_Round_Key_loop
	movf NR,F			; si NR = 0 il est inutile de faire une maj de
	btfsc STATUS,Z			; la clé on a terminé le key scheduling
	return
        MOVEVAL 3, cpt                  ; mise à jour des 4 premiers octets de la clé (W[j], j multiple de Nk), on commence d'abord avec 
	decf FSR,F			; les 3 premiers octets qui doivent être combinés avec les 3 derniers octets de la clé, FSR pointe sur le dernier octet de la clé
Update_first				
	EEREADR INDF			; W <- ByteSub(key[i+13]) (où key[i] est l'un des 3 premiers octets de la clé)
	movwf tempo			; tempo <- W
	movlw -13
	addwf FSR,F			; on se place sur key[i] (un des 3 premiers octets de la clé)
	movf tempo,W			; W <- tempo = ByteSub(key[i+13])
	xorwf INDF,F			; key[i] <- key[i]^ByteSub(key[i+13])
	movlw 12
	addwf FSR,F			; on se place sur key[i+12]
	decfsz cpt,F
	goto Update_first
					; traitement particulier du 4ème octet de la clé. 
        EEREADR INDF			; On est sur le premier octet des 4 derniers octets de la clé, W <- ByteSub(key[12])
	movwf tempo			; tempo <- W
	movlw -9
	addwf FSR,F			; on se place sur key[3] 
	movf tempo,W			; W <- tempo = ByteSub(key[12])
	xorwf INDF,F			; key[3] <- key[3]^ByteSub(key[12])	

	MOVEVAL key, FSR                ; on se replace sur key[1]
	bcf STATUS,C
	rlf rcon, W			; mise à jour de la constante rcon
	btfsc STATUS, C
	xorlw H'1B'
	movwf rcon			; rcon <- xtime(rcon)
	xorwf INDF,F			; key[1] <- key[1] ^ rcon = key[1] ^ ByteSub(key[14]) ^ rcon

	MOVEVAL 12,cpt                  ; mise à jour des 12 octets suivants de la clé (3 élém suivants si on raisonne avec des word)
Update_other
	COPY INDF, indf_keep            ; on récupère key[i] (au début on est sur key[1] puisque FSR n'a pas été modifié)
	movlw 4					
	addwf FSR,F			; on se place sur key[i+4] (élément dans le mot suivant)
	movf indf_keep,W		; W <- key[i]
	xorwf INDF,F			; key[i+4] <- key[i+4] ^ key[i]
	movlw -3				
	addwf FSR,F			; on se place sur key[i+1] (prochain élément)
	decfsz cpt, F
	goto Update_other
	return
		

ByteSub
	MOVEVAL mess_length, cpt 	; cpt = longueur du message (en octets)
	MOVEVAL message, FSR            ; on se positionne sur le premier octet
ByteSub_loop
	EEREADR INDF			; on lit en eeprom la case d'adresse j si message[i] = j, i.e on lit S[j];
	movwf INDF			; message[i] est remplacé par S[j], i.e par S[message[i]]
	incf FSR,F			; on passe au caractère suivant dans le message
	decfsz cpt,F			; on décrémente le compteur
   	goto ByteSub_loop
	return

ShiftRow
	MOVEVAL 3, cpt				; compteur pour passer sur les 3 lignes  à shifter, on commencera par la dernière ligne
	MOVEVAL message+15, adr_elem_cour	; adresse du dernier élément de la 3ème ligne
Traiter_ligne					; partie qui effectue le nombre de rotations nécessaires sur la ligne courante (la ligne i doit être shiftée i fois)
	COPY cpt, cpt_cour			; ce nombre vaut cpt, on initialise donc cpt_cour (le compteur de rotations) à cpt
Rotation					; partie qui effectue une rotation circulaire de la ligne courante
	COPY adr_elem_cour, FSR			; on se positionne sur le dernier élément de la ligne
	COPY INDF, tempo			; on le sauvegarde dans tempo
	MOVEVAL 3, perm_cpt			; on initialise un compteur pour parcourir les 3 éléments de la ligne
Un_Shift					; partie qui remplace l'élém courant par son prédécesseur dans la ligne courante	
	movlw -4				; attention le message étant stocké sous forme de colonnes, le prédécesseur
	addwf FSR,F				; dans la ligne se trouve en fait à 4 adresses avant.
	COPY INDF, indf_keep			; on sauvegarde le prédécesseur
	movlw 4					;
	addwf FSR,F				; on se replace sur l'élément courant
	COPY indf_keep, INDF			; on le remplace par son prédécesseur
	movlw -4				; on se positionne sur l'élément précédent
	addwf FSR,F				;
	decfsz perm_cpt,F			; on passe à l'élément suivant
	goto Un_Shift
	COPY tempo, INDF			; on remplace le 1er élément de la ligne par le dernier
	decfsz cpt_cour, F			; une rotation a été réalisé, on décrémente le compteur de rotation	
	goto Rotation				;
        decf adr_elem_cour, F			; la ligne a été traitée, on se place sur le dernier élément de la ligne
	decfsz cpt,F				; précédente (les élém étant stockés en colonnes) il se trouve juste avant
	goto Traiter_ligne			; le dernier élément de la ligne courante
	return

MixColumn
	MOVEVAL 4, cpt				; compteur pour parcourir les 4 colonnes
	MOVEVAL message, FSR			; on se positionne sur le 1er élém, 1ère col
Traiter_col					; phase de traitement d'une colonne A
	COPY INDF, indf_keep			; on sauvegarde A[0] nécessaire pour la mise à jour de A[3]
	movlw 3
	addwf FSR,F				; on se positionne sur le dernier élém de la colonne courante: A[3]
	movf INDF, W         			; W <- A[3]
	decf FSR,F
	xorwf INDF,W				; W <- W^A[2]
	decf FSR,F
	xorwf INDF,W				; W <- W^A[1]
	decf FSR,F
	xorwf INDF,W				; W  <- W^A[0]
	movwf tempo				; tempo <- A[3]^A[2]^A[1]^A[0]	
	MOVEVAL 3, cpt_col			; compteur pour mettre à jour A[0], A[1] et A[2]. A[3] est traité à part
						; FSR contient ici l'adresse de A[0]
Maj_elem
	COPY INDF,tm                            ; tm reçoit A[i] (élém en cours de traitement)
	incf FSR,F				; on se place sur A[i+1]	
	movf INDF,W				; 
	xorwf tm,F				; tm <- A[i]^A[i+1]
	clrw					; W <- 0
	bcf STATUS,C
	rlf tm,F				; tm <- x*tm
	btfsc STATUS,C				; tm est-il un polynome de degré 8 ?
	movlw H'1B'				; si oui on doit le réduire modulo x^8+...+
	xorwf tm,W				; W <- xtime(tm)
	xorwf tempo,W				; W <- tempo^xtime(tm)	
	decf FSR,F				; on se place sur A[i]
	xorwf INDF,F				; A[i] <- A[i]^tempo^xtime(tm)
	incf FSR,F				; on se place sur A[i+1] qui devient l'élém. courant pour le prochain passage
	decfsz cpt_col,F			; on décrémente cpt_col
	goto Maj_elem				;
	COPY INDF, tm				; on est sur A[3], tm <- A[3]
        movf indf_keep, W			; W <- A[0]
	xorwf tm,F				; tm <- A[0]^A[3]
	clrw					; W <- 0	
	bcf STATUS,C				;
	rlf tm,F				;	
	btfsc STATUS,C				;	
	movlw H'1B'				;
	xorwf tm,W				; W <- xtime(tm)
	xorwf tempo,W				; W <- tempo^xtime(tm)	
	xorwf INDF,F				; A[3] <- A[3]^tempo^xtime(tm)
	incf FSR,F				; on se place sur le 1er élém de la colonne suivante
	decfsz cpt,F				; on décrémente le compteur de colonnes
	goto Traiter_col
	return	
       
        include "../myroutines/mycall.asm"
Start
        movlw OPTIONVAL
        BANK1
        movwf OPTION_REG		; initialisation du registre OPTION_REG, indispensable pour initialiser timer0
        BANK0
        bcf INTCON,GIE 			; reset de tous les bits du registre d'interruption 
	call Init_Key			; transfert de la clé de la mém flash vers la Ram 	
	MOVEVAL mess_length, cpt        ; cpt = longueur du message (en octets)
        MOVEVAL message, FSR            ; on place l'adresse de départ de stockage du message dans FSR
Lecture					; phase de réception du message
        call Receive
	COPY Serial_buffer, INDF        ; on copie la valeur reçue dans la case mémoire courante
        incf FSR,F                      ; on se place sur la case suivante
        decfsz cpt,F                    ; on décrémente cpt
        goto Lecture

	MOVEVAL 9, NR
	call Add_Round_Key
Round
	call ByteSub
	call ShiftRow
	call MixColumn
	call Add_Round_Key
	decfsz NR,F
	goto Round

	call ByteSub
	call ShiftRow
	call Add_Round_Key
        MOVEVAL mess_length, cpt
	MOVEVAL message, FSR
	call Envoi
	BANK2				; phase de réception d'une nouvelle clef
	MOVEVAL H'1F',EEADRH		; on place une fois pour toutes 1F dans le registre haut de l'adresse de la mém flash puisqu'on ne parcourera que des adresses de la forme '1Fxx'	
	MOVEVAL H'FF'-key_length+1,EEADR; on initialise le registre bas de l'adresse
	MOVEVAL H'3F',EEDATH
Update_key
	call Receive			; on reçoit un octet de la clé, en sortant on est en BANK0
	movf Serial_buffer,W		; on le place dans W
	bcf PIR2,EEIF
	BANK2
	movwf EEDATA			; on le déplace dans le registre d'envoi (on utilise que le registre bas puisqu'on ne gère que 8 bits sur les 14)
	bsf STATUS,RP0			; bank3
	bsf EECON1,EEPGD		; on indique que l'on va travailler en mém flash
	bsf EECON1,WREN			; on autorise les écritures en mém flash
	MOVEVAL 0x55,EECON2		; 
	MOVEVAL 0xAA,EECON2		; constantes à envoyer avant une écriture (spécifications Microchip)
	bsf EECON1,WR			; on lance la phase d'écriture (nécessite deux instructions)
	nop
	nop
	bcf EECON1,WREN			; on inhibe les écritures
	bcf STATUS,RP0			; on repasse en bank2
	incfsz EEADR,F			; on passe à l'adresse suivante, quand EEADR vaudra FF+1 (=0), on aura reçu toute la clé
	goto Update_key
fin
	goto fin 	
	end


