CyberAlberto

Mi rincón personal en internet

Compresión y encriptación asimétrica mediante GPG: sfxcompress

En la entrada que hablamos sobre el compresor que genera ficheros autoextraibles, explicamos la manera de generar dichos ficheros mediante un script de shell.

A este tipo de ficheros podemos añadir una nueva característica: encriptación asimétrica. La criptografía asimétrica es el método criptográfico que usa un par de claves para el almacenamiento o envío de información: una denominada pública y otra privada. La clave pública se puede entregar a cualquier persona y la privada debe mantenerse en secreto, de modo que con este método se encripta con la clave pública y se desencripta con la privada. Uno de los programas que se encarga de esta tarea es el denominado GNU Privacy Guard (GPG), pero existen otros, por ejemplo: DSA, PGP, SSH, SSL y TLS.

El objetivo del script de shell es simplificar las opciones a utilizar y generar ficheros comprimidos, encriptados y auto-extraibles, de modo que se facilite su uso. Para ello partimos del código explicado en la entrada mencionada.

Primero, dado que el número de opciones ha aumentado, es necesario tratar los parámetros de entrada de manera más cuidadosa. Se va a definir las siguientes opciones:

Opción Parámetro Descripción
-f filename Indica el nombre base del fichero de salida y su path.
-g Indica que se utilice compresión gzip.
-b Indica que se utilice compresión bzip2 (es la opción por defecto).
-k Indica que debe encriptarse el fichero de salida.
-h Muestra las opciones
-v Muestra la versión del programa y la licencia

Como se observa, se ha definido los parámetros como los estándares de UNIX: opciones de una sola letra con un guión delante.

Para hacer el análisis de los parámetros de entrada, se utiliza el siguiente bucle:

# Parse the parameters
while getopts  ":bf:ghkv" flag
do
        chooseOptions $flag "$OPTARG"
done

Donde la función chooseOptions recibe cada parámetro y asigna las variables necesarias antes de la compresión:

# function name: chooseOptions, the function shows by screen the version of the program
#    parameters: - the valid option (b, f, g, h, k, or v)
#                - the parameter needed, for instance "f" needs the filename
function chooseOptions {

        if [ "$1" = "g" ]; then
                compresor="z"
                extension="tgz"
        else
        if [ "$1" = "b" ]; then
                compressorByDefault
        else
        if [ "$1" = "k" ]; then
                encriptar="si"
        else
        if [ "$1" = "f" ]; then
                filename="$2"
        else
        if [ "$1" = "h" ]; then
                syntax
        else
        if [ "$1" = "v" ]; then
                version
        else
                echo "ERROR: \"$2\" is an invalid option."
                echo
                exit -1
        fi
        fi
        fi
        fi
        fi
        fi

} # chooseOptions

Si todo es correcto, el programa llama a la función compress que se encarga de comprimir (y encriptar) el fichero de salida. Es importante señalar que la compresión se hace en la memoria de tal forma que no se generan ficheros intermedios que puedan comprometer información sensible. Primero comprueba si existe el fichero “.exclude” que indica los ficheros que deben ignorarse, por ejemplo, en ciertas circunstancias puede ser interesante ignorar los ficheros que terminen en ‘~’: “*~”.

# function name: compress, the main function of the program
#    parameters: - the base name
function compress {

        compressed=$(mktemp -t XXXXXXXXXXXXXXXX)
        encrypted=$(mktemp -t XXXXXXXXXXXXXXXX)
        # If .exclude exists, then the program must be work with it
        if [ -f ".exclude" ] ; then
                excluded=$(mktemp -t XXXXXXXXXXXXXXXX)
                echo >$excluded
                for i in $( cat .exclude ); do
                        find . -name "$i" >>$excluded
                done
                if [ "$encriptar" = si ]; then
                        rm -f $encrypted
                        tar cvX$compresor $excluded . | gpg -o $encrypted --encrypt
                else
                        tar cvXf$compresor $excluded $encrypted .
                fi
        else
                if [ "$encriptar" = si ]; then
                        rm -f $encrypted
                        tar cv$compresor . | gpg -o $encrypted --encrypt
                else
                        tar cvf$compresor $encrypted .
                fi
        fi
        # The compressed (and encrypted) file has been created

        # The last step is to encapsulate all in a self-extracting style bash-script
        comp=$(mktemp -t XXXXXXXXXXXXXXXX)
        SIZE=$(cat $encrypted | wc -c)
        SALIDA="$1.$extension.sfx"
        echo "#!/bin/bash" >$comp
        echo "">>$comp
        echo "SIZE=\"$SIZE\"">>$comp
        echo "compresor=\"$compresor\"">>$comp
        echo "filename=\"$(basename "$1")\"">>$comp
        echo "extension=\"$extension\"">>$comp
        echo "fecha=\"$(date)\"">>$comp
        echo "mdf=\"$(cat $encrypted | md5)\"">>$comp
        if [ "$encriptar" = si ]; then
                echo "encriptado=\"$encriptar\"">>$comp
                echo "adjetive=\" encrypted\"">>$comp
        fi
        # Take care!, the parameter in tail indicates the start of
        # the second part of the script, do not modify unless you
        # know what you do.
        cat "$0" | tail -39 >>$comp
        cat $encrypted >>$comp
        chmod 755 $comp
        # Delete temporary files
        rm -f $compressed
        rm -f $excluded
        rm -f $encrypted
        mv $comp "$SALIDA"
        echo
        echo The file $SALIDA has been created.
        echo

} # compress

Para ver el código completo y descargarlo, puede dirigirse a la página oficial, alojada en SourceForge.

Anuncios

Viernes, 12 noviembre 2010 Posted by | Informática e Internet, Investigación, programación, Script bash | Comentarios desactivados en Compresión y encriptación asimétrica mediante GPG: sfxcompress

Compresor que genera ficheros autoextraíbles

Es una idea que siempre me ha rondado por la cabeza, hasta que he decidido desarrollarla. Quería tener un fichero comprimido con las instrucciones en su interior para descomprimirse a sí mismo.

Los ficheros generados por esta utilidad contienen un shell script en su cabecera que contiene las instrucciones para descomprimirse. La sobrecarga que produce es de poco más de medio Kbyte, por lo que no supone ningún inconveniente. Se ha probado en Mac OS X, Linux y CygWin, por lo que seguramente funcione en el resto de sabores UNIX (o lo que es lo mismo: en cualquier sistema operativo distinto de Windows). Para que funcione en Windows es necesario instalar CygWin.

Se necesitará tener instalados los siguientes ejecutables en el sistema: bash, tar, gzip, bzip2, cat, wc, tail, chmod, rm, mv. md5 y mkdir. Esto normalmente no supone ningun problema, debido a que son comandos estándar en cualquier distribución.

La sintáxis es la siguiente:

comprime nombre_de_archivo [formato_de_compresión]
  • nombre_de_archivo: es el nombre de archivo a generar junto con su ruta
  • formato_de_compresión: puede ser b (bzip2) o g (gzip)

Por ejemplo:

cd /home/user/documents
comprime ~/fichero g

Crea el archivo ejecutable ~/fichero.tgz.sfx que contiene el directorio /home/user/documents comprimido mediante gzip

A la hora de descomprimir el fichero generado, se ha incluido código hash md5, para saber si ha sido alterado el fichero comprimido.

El código es el siguiente:

#!/bin/bash

if [ "$2" = "g" ]; then
	compresor="z"
	extension="tgz"
else
	if [ "$2" = "b" ]; then
		compresor="j"
		extension="tbz"
	else
		if [ "$2" = "" ]; then
			compresor="j"
			extension="tbz"
		else
			echo "ERROR: \"$2\" is an invalid format, choose g (gzip) or b (bzip2)."
			exit -1
		fi
	fi
fi

if [ $1 ] ; then
	tar cvf$compresor /tmp/compressed .
	SIZE=$(cat /tmp/compressed | wc -c)
	SALIDA="$1.$extension.sfx"
	echo "#!/bin/bash" >/tmp/comp.sfx
	echo "">>/tmp/comp.sfx
	echo "SIZE=\"$SIZE\"">>/tmp/comp.sfx
	echo "compresor=\"$compresor\"">>/tmp/comp.sfx
	echo "extension=\"$extension\"">>/tmp/comp.sfx
	echo "fecha=\"$(date)\"">>/tmp/comp.sfx
	echo "mdf=\"$(cat /tmp/compressed | md5)\"">>/tmp/comp.sfx
	cat "$0" | tail -35 >>/tmp/comp.sfx
	cat /tmp/compressed >>/tmp/comp.sfx
	chmod 755 /tmp/comp.sfx
	rm /tmp/compressed
	mv /tmp/comp.sfx "$SALIDA"
	echo
	echo The file $SALIDA has been created.
	exit 0
fi

echo
echo SYNTAX: $(basename $0) filename [format]
echo 
echo WHERE:
echo "        filename --> The desired name and path"
echo "        format   --> g (gzip) or b (bzip2)"
echo
echo $(basename $0) compresses the current directory as a self-extracting file.
echo 
echo
exit 1

# --------------------------------------------------------

if [ -f "$0" ] ; then
	DIRECTORIO=$(basename $(basename "$0" .sfx) .$extension)
	
	if [ -d "$DIRECTORIO" ] ; then
		echo
		echo "ERROR: The directory named \"./$DIRECTORIO\" exists"
		exit -1
	else
		echo "This file was packed on $fecha."
		echo
		while true
			do
			echo -n "Do you want to unpack the file named \"$0\" in the folder \"$DIRECTORIO\"? "
			read CONFIRM
			case $CONFIRM in
				y|Y|yes|yeS|yEs|yES|Yes|YeS|YEs|YES) break ;;
				n|N|no|NO|No|nO) exit  ;;
			*) echo -n "(yes or no) "
			esac
		done
		mkdir "$DIRECTORIO"
		mdfc=\"$(tail -c $SIZE "$0" | md5)\"
		echo
		echo "MD5 Stored:     \"$mdf\""
		echo "MD5 Calculated: $mdfc"
		tail -c $SIZE "$0" | tar -x$compresor -C
		echo
		echo "File \"$0\" unpacked."
		exit 0
	fi
fi

echo ERROR!
exit -1

Puede ser descargado desde Sourceforge.

Viernes, 7 mayo 2010 Posted by | Personal, Script bash | Comentarios desactivados en Compresor que genera ficheros autoextraíbles

Emulando el comando deltree de DOS

Aunque emular el comportamiento se puede hacer en un simple alias, dado el peligro potencial que puede suponer su uso, se ha escrito un pequeño script llamado deltree:

#!/bin/bash

remdir()
{
	find "$1" -delete
}

if [ -d "$1" ] ; then
	while true
		do
		echo -n "Delete the whole tree named \"$1\" and its contents? "
		read CONFIRM
		case $CONFIRM in
			y|Y|yes|yeS|yEs|yES|Yes|YeS|YEs|YES|s|S|SI|si|Si|sI) break ;;
			n|N|no|NO|No|nO) exit  ;;
		*) echo -n "(yes or no) "
		esac
	done
	remdir "$1"
else
	echo $0: $1: No such directory
fi

Espero que os sea de utilidad.

Martes, 16 marzo 2010 Posted by | Script bash | Comentarios desactivados en Emulando el comando deltree de DOS

Montaje de unidades de red SMB en Mac OS

SMB es el protocolo de Microsoft DOS y Windows para el montaje y compartición de unidades de red.

Normalmente en Mac OS el montaje de estas unidades es muy sencillo, ya que en la parte izquierda de cualquier ventana de Finder aparece un apartado llamado “COMPARTIDO” y haciendo click sobre un PC, aparece la lista de unidades compartidas. El montaje es “casi siempre” automático.

Pero puede ocurrir que esto no funcione, ya que a veces no aparece en esa lista ningún ordenador, o no aparecen todos.

Otras veces no te deja conectarte aunque apareza, incluso aunque pongas las credenciales correctas… no es un sistema infalible.

Admitámoslo: Mac OS dista mucho de ser perfecto y a veces la extrema sencillez de su interfaz desespera.

Cuando no puedes conectarte a la máquina deseada y sabes (1) que dicha máquina existe en la red, (2) que tienes conectividad con ella -que puedes hacer ping y te responde-, entonces es el momento de abrir el terminal y hacer las cosas como es debido: sabiendo lo que de verdad se hace.

Supongamos que nos queremos conectar a la máquina NEO, como es lógico, a veces tampoco funciona la resolución de nombres, por lo cual echamos mano de su dirección IP, por ejemplo 192.168.1.5. El recurso compartido de red es por ejemplo shared. En la sintáxis de Windows esto sería \\NEO\shared. En NEO tenemos el usuario foo cuya clave es 131313.El siguiente script monta la unidad compartida en nuestro Mac en el directorio /Volumes (que es el estándar, en linux se suele usar el directorio /media):

#!/bin/bash

mkdir /Volumes/shared

mount_smbfs //foo:131313@NEO:139/shared /Volumes/shared

rmdir /Volumes/shared

Es importante crear y borrar el directorio, ya que de esta manera, cuando desmontemos el volumen, automáticamente borrará el directorio donde se ha montado.

Como vemos, la sintáxis de mount_smbfs es:

mount_smbfs //usuario:contraseña@máquina:139/recurso punto_de_montaje

Si con el nombre de NEO no es capaz de localizarlo, entonces echamos mano de su dirección IP:

mount_smbfs //foo:131313@192.168.1.5:139/shared /Volumes/shared

El valor 139 es el número de puerto asignado en Windows para transferencias NETBIOS.

Para desmontar el volumen, podemos usar el imperfecto Finder o abrir el terminal y escribir:

umount /Volumes/shared

Las pruebas han sido realizadas con Mac OS 10.5.7 con kernel 9.7.0 y con Windows Server 2003 R2 SP2.

Jueves, 4 junio 2009 Posted by | Curiosidades, Informática e Internet, Investigación, programación, Script bash | Comentarios desactivados en Montaje de unidades de red SMB en Mac OS

Script para visualizar el árbol de directorios

En los tiempos del MS-DOS, existía un comando llamado TREE que mostraba de manera textual la estructura de directorios. Ésta utilidad la he hechado mucho de menos en otros sistemas como UNIX.

Podemos solucionarlo con un simple bash script que se llama a sí mismo, es un código muy sencillo y no contempla todos los casos posibles… pero es un primer acercamiento. La utilidad la he llamado dirtree.

#!/bin/bash

if [ "$1" ]; then
	path=$1
else
	path="."
fi

tab="|       "

echo "$2$(basename "$path")"

cd "$path"

find . -name "??*" -type d -maxdepth 1 -exec dirtree {} "$tab$2" \;

if [ "$(find . -name "??*" -type d -maxdepth 1)" ]; then
	echo "$tab$2"
fi

Sábado, 7 marzo 2009 Posted by | programación, Script bash | Comentarios desactivados en Script para visualizar el árbol de directorios

Ampliación de los scripts de compresión

Hace unos días mostré el código para comprimir en formato .tar.gz y .tar.bz2.

Ahora lo he ampliado para que comprima en formato .tar.7z. El formato 7zip es un revolucionario sistema de compresión con altas tasas de compresión y es además multithreading.

El código del script ahora requiere de 4 ejecutables en el path: tar, gzip, bzip2 y 7za.

Script comprime:

#!/bin/bash

if [ "$2" = "gzip" ]; then
	echo "Se ha elegido gzip como compresor."
	compresor="z"
	extension="gz"
else
	if [ "$2" = "bzip" ]; then
		echo "Se ha elegido bzip2 como compresor."
		compresor="j"
		extension="bz2"
	else
		if [ "$2" = "" ]; then
			compresor="z"
			extension="gz"
		else
			if [ "$2" = "7zip" ]; then
				compresor="7"
				extension="7z"
			else
				echo "Error: El parámetro debe ser gzip, bzip o 7zip"
				exit 1
			fi
		fi
	fi
fi

if [ $1 ] ; then
	FICHERO=$(basename $1)
	if [ "$compresor" = "7" ]; then
		tar -cvf - . | 7za a -si ~/$FICHERO.tar.$extension
	else
		tar cvf$compresor ~/$FICHERO.tar.$extension .
	fi
	echo
	echo Fichero ~/$FICHERO.tar.$extension creado.
	exit 0
fi

echo
echo Script para comprimir ficheros y directorios, uso:
echo
echo $(basename $0) NOMBRE \[FORMATO\]
echo
echo \(Donde NOMBRE indica el nombre del fichero que se va a generar tras comprimir
echo el directorio actual y FORMATO el compresor a usar\)
echo
echo Nota: Si no se indica FORMATO, se utilizará gzip.
echo Formatos aceptados: gzip, bzip y 7zip
echo
exit 1

Script descomprime:

#!/bin/bash

if [ "$1" != "" ]; then
	if [ -f "$1" ] ; then
		DIRECTORIO=$(basename $(basename "$1" .tgz) .tar.gz)
		DIRECTORIO=$(basename $(basename "$DIRECTORIO" .tbz2) .tar.bz2)
		DIRECTORIO=$(basename $(basename "$DIRECTORIO" .t7z) .tar.7z)
		EXTCHK=$(basename $(basename "$1" .t7z) .tar.7z)
		if [ "$EXTCHK" = "$DIRECTORIO" ]; then
			EXTENSION="7z"
		else
			EXTENSION="(none)"
		fi
		if [ -d "$DIRECTORIO" ] ; then
			echo
			echo Error: No se puede descomprimir, existe el directorio ./$DIRECTORIO
			exit 1
		else
			mkdir "$DIRECTORIO"
			if [ "$EXTENSION" = "7z" ]; then
				7za e -so "$1" | tar -xvC "$DIRECTORIO"
			else
				tar -xvC "$DIRECTORIO" -f "$1"
			fi
			echo
			echo Fichero $1 descomprimido.
			exit 0
		fi
	fi
fi

echo
echo Script para descomprimir ficheros y directorios en formato .tgz \(.tar.gz\),
echo .tbz2 \(.tar.bz2\) o .t7z \(.tar.7z\), uso:
echo
echo $(basename $0) FICHERO
echo
echo \(Donde FICHERO indica el nombre del fichero comprimido\)
echo
echo Nota: se descomprime a un directorio el cual no debe existir.
echo
exit 1

Jueves, 19 febrero 2009 Posted by | programación, Script bash | Comentarios desactivados en Ampliación de los scripts de compresión

Scripts para compresión y descompresión

Aunque esto se puede ya hacer de una manera muy fácil gracias a las interfaces gráficas y programas de compresión en modo gráfico, hay momentos en los que necesitamos de un simple script que comprima la carpeta actual o descomprima un fichero en formato .tar.gz, .tbz, .tar.bz o .tbz. Esas veces que estás en modo texto programando y quieres empaquetar todo el código fuente en un simple fichero.

Estos scripts tienen ya muchos años de antigüedad, pero al recordar que aún no lo tenía instalado en mi portátil, he decidido mostrarlo por si a alguien le sirve.

El compresor:

El compresor dado un nombre de fichero comprime el directorio actual y lo almacena en el directorio home del usuario. Si además le indicamos gz o bzip lo comprime en el formato indicado.

Por ejemplo: $ comprime fichero bzip que crea el fichero ~/fichero.tar.bz el cual contiene todos los ficheros del directorio en el cual se lanzó el compresor. El código es:

#!/bin/bash

if [ "$2" = "gzip" ]; then
	echo "Se ha elegido gzip como compresor."
	compresor="z"
	extension="gz"
else
	if [ "$2" = "bzip" ]; then
		echo "Se ha elegido bzip2 como compresor."
		compresor="j"
		extension="bz2"
	else
		if [ "$2" = "" ]; then
			compresor="z"
			extension="gz"
		else
			echo "Error: El parámetro debe ser gzip o bzip"
			exit 1
		fi
	fi
fi

if [ $1 ] ; then
	FICHERO=$(basename $1)
	tar cvf$compresor ~/$FICHERO.tar.$extension .
	echo
	echo Fichero ~/$FICHERO.tar.$extension creado.
	exit 0
fi

echo
echo Script para comprimir ficheros y directorios, uso:
echo
echo $(basename $0) NOMBRE \[FORMATO\]
echo
echo \(Donde NOMBRE indica el nombre del fichero que se va a generar tras comprimir el directorio actual y FORMATO el compresor a usar\)
echo
echo Nota: Si no se indica FORMATO, se utilizará gzip
echo
exit 1

El descompresor:

Descomprime el fichero dentro de un directorio que no debe existir previamente, para evitar sobreescribir su contenido por accidente. Por ejemplo: $ descomprime paquete.tar.gz que genera el directorio paquete que cuelga del directorio actual y descomprime en él el contenido del fichero paquete.tar.gz.

#!/bin/bash

if [ "$1" != "" ]; then
	if [ -f "$1" ] ; then
		DIRECTORIO=$(basename $(basename "$1" .tgz) .tar.gz)
		DIRECTORIO=$(basename $(basename "$DIRECTORIO" .tbz2) .tar.bz2)
		if [ -d "$DIRECTORIO" ] ; then
			echo
			echo Error: No se puede descomprimir, existe el directorio ./$DIRECTORIO
			exit 1
		else
			mkdir "$DIRECTORIO"
			tar -xvC "$DIRECTORIO" -f "$1"
			echo
			echo Fichero $1 descomprimido.
			exit 0
		fi
	fi
fi

echo
echo Script para descomprimir ficheros y directorios en formato .tgz (.tar.gz) o .tbz2 (.tar.bz2), uso:
echo
echo $(basename $0) FICHERO
echo
echo \(Donde FICHERO indica el nombre del fichero comprimido\)
echo
echo Nota: se descomprime a un directorio.
echo
exit 1

Los scripts estan probados y funcionan en Linux, Solaris y MacOS, pero requieren que esté en el PATH tanto la utilidad tar como gzip y bzip2. Estos scripts son de uso libre, además las críticas positivas o posibles mejoras siempre son bien recibidas.

Y basándose en el compresor, puede que nos venga bien este script que comprime el directorio actual sin tener que especificar nada y añade la fecha al nombre del fichero, la utilidad se llama backup. Muy útil para guardar un historial del contenido de un proyecto.

#!/bin/bash

nombre=$(date +%Y-%m-%d.%H-%M).$(basename $(pwd))

comprime \"$nombre\" bzip

Jueves, 5 febrero 2009 Posted by | Personal, programación, Script bash | 1 comentario

Script para conversión de ficheros WAV a MP3

Hay infinidad de programas que te convierten de WAV a MP3, pero en el mundo UNIX no he encontrado uno que me deje configurar los parámetros de calidad como a mí me gustan, ni que aproveche los ‘cores’ del procesador. En Windows solía usar Razorlame, un ‘front-end’ muy básico pero a la vez muy potente. En Mac OS aún no lo he encontrado, así que he tenido que programarme un pequeño script que cualquiera puede usar. Las críticas contructivas son bien recibidas 😉

Este script funciona en cualquier sistema UNIX que tenga BASH y el binario lame. Lame es el software que se encarga de codificar un fichero de audio WAV a MP3. El script que presentamos a continuación tiene 2 formas de invocarse en el terminal:

  • Sin parámetros: wav2mp3, que codifica todos los ficheros WAV del directorio actual a MP3. Lanza un proceso independiente por cada fichero, por tanto pone el procesador a máximo rendimiento.
  • Con 1 parámetro: wav2mp3 fichero_wav, que codifica dicho fichero WAV.

La calidad de MP3 que a mí me gusta es la siguiente: Variable bit rate, mínima 192Kbps, media 264Kbps y máxima 320Kbps. El código fuente del script es:

#!/bin/bash

##
## wav2mp3, convert a wav file in a mp3.
##

if [ "$1" == "" ]; then
	echo Convert all in this directory
	find . -maxdepth 1 -name "*.wav" -exec wav2mp3 {} \;
	exit
fi

# Define useful variables
dirname=$(dirname "$1")
inputfile=$(basename "$1")

filename=$(basename "$1" .wav)
filename=$(basename "$filename" .WAV)

# If file is located in root dir, then supress "/"
if [ "$dirname" == "/" ]; then
	dirname=""
fi

# Convert the file
if [ "$dirname" == "." ]; then
	echo "Converting file \"$filename\" from WAVE to MPEG layer 3..."
	echo "--------------------"
	lame  -S -b 192 -m s -h --abr 264 -B 320 "$inputfile" "$filename.mp3" &
else
	echo "Converting file \"$dirname/$filename\" from WAVE to MPEG layer 3..."
	echo "--------------------"
	lame  -S -b 192 -m s -h --abr 264 -B 320 "$dirname/$inputfile" "$dirname/$filename.mp3" &
fi

echo
echo Task launched!

Martes, 27 enero 2009 Posted by | Personal, programación, Script bash | Comentarios desactivados en Script para conversión de ficheros WAV a MP3