Scombussolare un documento, rendendolo illeggibile a estranei
Mi occupo di crittografia, più che altro per diletto ma nella convinzione che adottando criteri personali ci si possa mettere al riparo rispetto a (più o meno mitici) procedimenti di decifratura. Da ultimo, in una notte insonne, mi è venuta in mente un’ideuzza: perché, anziché cifrare singoli caratteri non procedere a un rimescolamento random di piccoli pezzi? In tal modo la decriptazione classica fallirebbe.
Mi spiego con un esempio, la stringa “Domani è un altro giorno”, si compone dei seguenti brani di tre caratteri:
“Dom”, “ani”, “ è “, “un “, “alt”, “ro “, “gio”, “rno”
Essa diventerebbe qualcosa come:
“gio è altrnoaniro Domun"
Un pasticciaccio del tutto incomprensibile, a meno di conoscere la posizione originaria dei pezzettini. Volendo si potrebbe complicare la faccenda aggiungendo, a valle del rimescolamento, una cifratura più o meno classica.
Nota La lunghezza 3 non è obbligatoria, potrebbe andar bene anche 4 o 5 ma ritengo che un valore maggiore potrebbe rendere il procedimento che sto per illustrare più veloce, ma con un minor grado di disordine.
Ma ecco del codice VBA per Microsoft Word atto a compiere questa particolare azione criptante (che in realtà non può essere definita cifratura). La parte alta, a livello Dichiarazioni, reca le variabili strOrig, strRandom, VettSpost() e swCript, ovvero nell’ordine la stringa originaria, quella randomizzata, un vettore destinato a ospitare spostamenti rimescolati e un booleano per alternare la crittazione e la decrittazione.
L’array VettSpost ha formato Long per consentire il trattamento di testi abbastanza lunghi. Inizialmente conterrà i numeri da 1 al numero di terzine che compongono strOrig, a seguito della cura risulterà disordinato in modo casuale.
Vediamo la prima routine.
Dim strOrig As String, strRandom As String
Dim VettSpost() As Long
Dim swCript As Boolean
Private Sub CreaVettSpost()
Dim NumCar As Long, R3 As Integer, k As Integer
NumCar = Len(strOrig)
R3 = NumCar Mod 3
If R3 > 0 Then
' Rendi lunghezza di strOrig multipla di 3
For k = 1 To 3 - R3
strOrig = strOrig & "#" ' Anziché "#" (utile per debug) è preferibile " "
Next
End If
Dim Num3 As Long
Num3 = Len(strOrig) \ 3 ' Numero terzine
Dim VettCas() As Long ' Vettore di casuali
ReDim VettCas(Num3)
ReDim VettSpost(Num3)
Dim i As Long
' Genera il vattore dei casuali e il VettSpost da 1 a N3
For i = 1 To UBound(VettCas)
VettCas(i) = Rnd: VettSpost(i) = i
Next
Dim j As Long, dep As Long
' Disordina VettSpost con bubble sort
For i = 1 To Num3 - 1
For j = i + 1 To Num3
If VettCas(j) < VettCas(i) Then
dep = VettCas(j)
VettCas(j) = VettCas(i): VettCas(i) = dep
dep = VettSpost(j)
VettSpost(j) = VettSpost(i): VettSpost(i) = dep
End If
Next
Next
End Sub
L’algoritmo disordinante è espletato dal ciclo For i = 1 To Num3 evidenziato in grassetto, che riordinando VettCas produce disordine in VettSpost. Per brevità mi astengo da ulteriori commenti dettagliati, dico solo che il ricorso a vettori è stata un’alternativa alla (più sofferta e... meno chiara) scelta di cavarsela con funzioni di stringa...
Ed ecco una possibile Sub che sfrutta la precedente per disporre caoticamente una stringa.
Sub Cripta()
Dim questoDoc As String
questoDoc = ThisDocument.Content
' MsgBox Len(questoDoc) ' Usato per debug
If Not swCript Then
strOrig = questoDoc
strRandom = ""
CreaVettSpost strOrig
Dim i As Long, j As Long, N As Long
N = UBound(VettSpost)
Dim VettstrOrig() As String, VettTerzine() As String
ReDim VettstrOrig(N)
j = 0
For i = 1 To Len(strOrig) - 1 Step 3
j = j + 1
VettstrOrig(j) = Mid(strOrig, i, 3)
Next
ReDim VettTerzine(N)
For i = 1 To N
VettTerzine(i) = VettstrOrig(VettSpost(i))
Next
For i = 1 To N
strRandom = strRandom & VettTerzine(i)
Next
swCript = True
End If
Me.Content = ""
Me.Content = strRandom
End Sub
Anche qui commenti tacitiani, per pigrizia e... un pizzico di sadismo. Tengo però a segnalare una proprietà poco nota dell’ oggetto Document, ovvero la Content che ne fornisce per l’appunto l’intero contenuto (a proposito: al posto di ThisDocument andava altrettanto bene Me o magari ActiveDocument). In due parole, dopo l’invocazione di CreaVettSpost viene impostato un VettTerzine di dimensioni uguali a quelle di VettSpost seguito dal ciclo For i = 1 To Len(strOrig) – 1 Step 3... Next che spazzola le terzine e le sistema nelle posizioni pseudo casuali di VettSpost(j). Il successivo loop provvede palesemente a concatenare i vari pezzetti tri-letterali di VettTerzine nella strRandom. Il lavoro si conclude assegnando quest’ultima al Content del documento.
Recupero del testo, storia di un’illusione
Successivamente ho sviluppato e testato una routine Decripta di cui qui riporto poche righe (affido il resto agl'interessati & volonterosi).
Private Sub Decripta()
If swCript Then
Dim VettTerzine() As String
ReDim VettTerzine(UBound(VettSpost))
' o m i s s i s
Me.Content = strOrig
ThisDocument.Undo 2
End Sub
Il lettore comunque si chiederà che ci sta a fare l’ultima istruzione grassettata. Premesso che Undo n è un’istruzione che ho scoperto solo adesso, il motivo è presto detto: in sua assenza si constata che la penultima istruzione restituisce un documento interamente formattato come il prima paragrafo. Magari esiste un’alternativa ma Undo 2 annullando le due ultime operazioni rimette le cose a posto.
A questo punto, altalenando i richiami di Cripta e Decripta – grazie allo switch “toggle” swCript si assiste allo scombussolamento e al recupero del contenuto del nostro documento.
Tutto bene? Neanche per sogno! Perché il giochetto funziona soltanto nella sessione corrente. Infatti, esaminando i passaggi da vicino, magari con una stringa di debug non troppo lunga (v. commento specifico) si hanno due sorprese:
· ad ogni occorrenza di Cripta il risultato cambia;
· A ben riflettere il merito di Decripta risiede unicamente nell’Undo!
Di conseguenza Decripta si può ridurre, semplicemente, così:
Sub Decripta()
If swCript Then
ThisDocument.Undo 2
End If
End Sub
Riflettendo mi sono reso conto la delusione dipende dal fatto che i numeri generati dalla funzione Rnd sono di fatto “abbastanza” casuali anche senza ricorrere a Randomize. Il che è un merito in tutti i casi di simulazione statistica e simili ma non va bene nel nostro caso. Si noti che anche l’Undo è lungi dal costituire una panacea, perché tranne i più sprovveduti tutti possono presto scoprire che basta dare un paio di comandi manuali Annulla per scoprire l’originale.
Dunque dobbiamo rinunciare al nostro sogno? In fondo al post fornisco due possibili rimedi.
Un piccolo ripiego, ingegnoso ma insufficiente
Un’ideuzza graziosa consiste nell’utilizzare la routine d’evento Open:
Private Sub Document_Open()
Application.EnableCancelKey = wdCancelDisabled
Me.Range.Font.Color = wdColorWhite ' Documento tutto bianco per occultarlo
Cripta
Dim Pswrd As String
Pswrd = "127axtm9v" ' Un valore più o meno privato
If InputBox("Password", "") <> Pswrd Then Me.UndoClear
End Sub
Il procedimento consiste nel richiamare Cripta all’apertura del documento, scombiccherandolo in modo diverso ad ogni sua nuova apertura e chiedendo subito una password all’utente, che solo se la conosce può vedere l’originale. Altrimenti non potrà usufruire nemmeno del comando Annulla digitazione, grazie al metodo UndoClear che lo inibisce radicalmente, restando con un palmo di naso, dinanzi a un documento tutto bianco. Potrà schiarirlo a mano ma visualizzando il caos in parola.
Nota. In precedenza sono ammattito con Undo = False che invece viene rifiutato
Il recupero dell’originale avverrà con:
Sub Decripta()
If swCript Then
ThisDocument.Undo 3
End If
End Sub
ove Undo 3 tiene conto anche dell’ulteriore operazione di bianchettatura.
Nota. UndoClear non solo disattiva la casellina nonché l’equivalente combinazione Ctrl+Backspace ma rende inefficace istruzione VBA Undo, ergo anche l’azione di Decripta.
Due possibili rimedi
Ovviamente il precedente trucchetto inganna solo i più disattenti, perché basta tacitare le macro o, più semplicemente, ricorrere a Office.org per vedere da vicino l’originale che sul disco non è trasformato. La cosa potrebbe servire per qualche scherzo ad amici, colleghi e parenti.
Nota. Ma mi raccomando! Non ponete tale codice nel modulo ThisDocument posto sotto Normal, perché tutti i documenti verrebbero sconvolti all’apertura.
L’ideale resta quello di crittare e salvare e, in seguito, decrittare. Comunque i due possibili rimedi anticipati poc’anzi esistono:
1) Trasmettere in qualche modo, assieme al file randomizzato, l’elenco degli spostamenti utilizzati (i valori del VettSpost.
Questa possibilità, sulla carta utile nel caso di due corrispondenti, appare però molto complessa da gestire e, comunque, NON ho avuto tempo né voglia di usarla. Meglio ancora si potrebbe:
2) Generare numeri pseudo casuali, che chiamerei “regolari”, che sulla base di opportuni parametri, generano numeri caotici secondo una serie più o meno lunga ma sempre identica dopo che se ne fissa un valore iniziale.
Ci stavo lavorando MA AL MOMENTO DEBBO ONESTAMENTE PRECISARE CHE INCONTRO DIFFICOLTA' di vario genere... Appena posso dirò il risultato.
?>
?>
?>