Il blog di Gianni Giaccaglini

Blog su VBA e VSTO
Gianni Giaccaglini

My Links

News

NB - V. anche gli ARTICOLI (in fondo a questa barra)
Solo quesiti validi a: giannigiac@tin.it
Il mio Best seller su VBA
(v. www.hoepli.it)


Il mio ultimo libro su Open XML
(v. www.FAG.it):



La mia nipotina ELISA

Foto con dedica a ME di
Bill Gates giovanissimo
nei mitici anni 80!

Categorie Post

Categorie Articoli

Archivio

Immagini

Blog Stats

Scombussolare un documento, rendendolo illeggibile

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.

?>

?>

?>

posted on mercoledì 30 novembre 2011 19.15